A Very Basic Introduction to Binary Trees in C

A binary tree is a finite set of elements called nodes, where each node is either empty or else is partitioned into three disjoint subsets, which themselves may also be likewise partitioned, or else empty.

A node in a binary tree may have at most two child nodes, a left node and a right node. A node that has no child nodes is called a leaf. The greatest ancestor of all nodes in the binary tree is the root node. Trees are graphically represented as growing downwards, rather than upwards.

The levels of a tree are defined starting at 0 for the root node, and increasing by one for each node descended from the parent node. The depth of the tree is determined by the maximum level of the leaf with the longest path from the root.

If every nonleaf node in a binary tree has nonempty left and right child nodes, then tree is a strictly binary tree. A complete binary tree of a certain depth of x is the strictly binary tree all of whose leaves reach level x.

In  a complete binary tree, the number of nodes at a certain level is twice the number of nodes at the level before it. Likewise, in a complete binary tree, the total number of nodes is always odd; in fact, the formula for finding the total number of nodes is the number 2 raised to a certain power, and then having one subtracted from that number, or 2^x – 1, where x is equal to the number of levels.

The official formula for finding the maximum number of nodes in a binary tree, which is the number of nodes a complete binary tree will have, is actually 2^(k+1) – 1, where k represents the height of the tree. Why k+1? Because height is measured by counting the number of lines, called edges, between the root node and its furthest descendant. As the root node by definition has no ancestors, and thus no lines connecting to it from above, its height is zero, and the height of the level beneath that is 1, and so on.

#include <math.h>

int GetMaxNodes(int height) {
 return pow(2, height+1) - 1;
}


int main()
{

 for (int i = 0; i < 5; i++) {
 printf("A binary tree graph of height %d will have a maximum of %d nodes\n",
 i, GetMaxNodes(i));
 }

 return 0;
}

A binary tree can be implemented as either an array of nodes or a linked list. The data stored in a node can be either simple or itself a structure.

To implement a binary tree as an array, we will set each node to be a struct with elements consisting of the left child, the right child, and the node data. In this scheme, the zero-indexed node structure is the root node, and a special named constant value LEAF is used to indicate that there is no child.

If we were to create a tree graph were each node only had a left child, we would in fact end up with a linear list. We will do exactly this.

#include <stdlib.h>
#include <math.h>
#include <time.h>

#define ROOT 0
#define LEAF -1

struct structNode {
	double dData;
	int leftChildIndex;
	int rightChildIndex;
};


void InitializeNode(structNode *node, double dValue);
void PrintNode(const structNode *node);

int main()
{
 const int Num_Nodes = 7;
 int iCtr;
 srand(time(NULL));

 struct structNode TreeGraph[Num_Nodes];

 for (iCtr = 0; iCtr < Num_Nodes - 1; iCtr++) {
 InitializeNode(&TreeGraph[iCtr], (rand() / (double)rand()));
 TreeGraph[iCtr].leftChildIndex = iCtr + 1;
 }
 
 InitializeNode(&TreeGraph[Num_Nodes-1], (rand() / (double)rand()));

 for (iCtr = 0; iCtr < Num_Nodes; iCtr++) {
 PrintNode(&TreeGraph[iCtr]);
 }

 return 0;
}

void InitializeNode(structNode *node, double dValue) {
	node->dData = dValue;
	node->leftChildIndex = LEAF;
	node->rightChildIndex = LEAF;
}

void PrintNode(const structNode *node) {
	printf("Node Value: %f", node->dData);
	if (node->leftChildIndex != LEAF) {
		printf("\t Left Child = %d", node->leftChildIndex);
	}
	else {
		printf("\t Left Child = LEAF");
	}

	if (node->rightChildIndex != LEAF) {
		printf("\t Rigth Child = %d", node->rightChildIndex);
	}
	else {
		printf("\t Right Child = LEAF");
	}

	printf("\n");
}

Function Pointers in C

A function is not really a separate, isolated block of code so much as it is the address of the entry point to a series of instructions that are organized logically as a function. At the level of assembly language, a function is simply a sequence of instructions with a known starting point, which is the memory address of the first instruction.

#include <stdio.h>
#include <stdlib.h>

int addThree(int a, int b, int c);
int subThree(int a, int b, int c);

int main()
{
 printf("Location of addThree() function %p\n", addThree);
 printf("Location of subThree() function %p\n", subThree);

 return 0;
}


int addThree(int a, int b, int c) {
 return a + b + c;
}

int subThree(int a, int b, int c) {
 return a - b - c;
}

A function pointer is therefore nothing more than a pointer that stores the entry point address of a function.

When declaring a function pointer, we should take care to separate the pointer declaration from the type of the return value by enclosing the pointer declaration in a set of parentheses.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//same param list and return values

//returns long long
long long addNum(int x, int y) { return x + y; }
long long subtractNum(int x, int y) { return x - y; }

//returns double
double divideNum(int x, int y) { return (double)x / y; }
double multiplyNum(int x, int y) { return (double)x * y; }


int main()
{
 long long (*iAction)(int x, int y);
 double(*dAction)(int x, int y);
 
 iAction = addNum;
 dAction = divideNum;

 //dereference the function pointer to call the function
 printf("%lld\n", (*addNum)(1138, 1701));
 printf("%f\n", (*dAction)(47, 7));

 dAction = multiplyNum;

 printf("%lld\n", (*subtractNum)(1138, 1701));
 printf("%f\n", (*dAction)(100, 11));

 return 0;
}

Note that function names function in the same as array names, in that they both devolve to a memory address.

We can use function pointers to increase the flexibility of the flow of our program. For example, by using function pointers we can alter the order in which we call functions depending on either compile time or run time data.

#include <stdio.h>
#include <stdlib.h>

const int Num_Functions = 4;

void funcOne(void) {
 printf("Executing function one.\n");
}

void funcTwo(void) {
 printf("Executing function two.\n");
}

void funcThree(void) {
 printf("Executing function three.\n");
}

void funcFour(void) {
 printf("Executing function four.\n");
}

void(*funcArray[Num_Functions])(void) { funcOne, funcTwo, funcThree, funcFour };

int main()
{
 int i = 0;

 while (i < Num_Functions) {
 //using pointer arithmetic
 (*(funcArray + i++))();
 }

 printf("Now backwards!\n");

 //remember i is at 4
 //so we must decrement it before using it
 //as an index value
 while (i-- > 0) {
 //using indexers
 (*funcArray[i])();
 }
 return 0;
}

Another interesting thing we can do with function pointers is create a function dispatcher.

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
//note we are including time.h for the srand() function


const int Num_Functions = 4;

void functionW(const char *szArg) {
 printf("Function W acting on parameter '%s'\n\n", szArg);
}

void functionX(const char *szArg) {
 printf("Function X acting on parameter '%s'\n\n", szArg);
}

void functionY(const char *szArg) {
 printf("Function Y acting on parameter '%s'\n\n", szArg);
}

void functionZ(const char *szArg) {
 printf("Function Z acting on parameter '%s'\n\n", szArg);
}


void(*actions[Num_Functions])(const char *szArg) = { functionW, functionX, functionY, functionZ };

//this function calls the other functions we have defined. 
void dispatchFunction(int iType, char * szInstruction) {
 if ((iType > -1) && (iType < Num_Functions)) {
 (*actions[iType])(szInstruction);
 }
}

int main()
{
 const int num_arguments = 6;
 srand(time(NULL));
 

 char *szArgs[num_arguments] = { 
 "Nice boat!", 
 "That is mahogany!", 
 "Don't Panic",
 "Greetings, Professor Falken",
 "You are likely to be eaten by a grue.",
 "El Psy Congroo."
 };

 for (int i = 0; i < num_arguments; i++) {
 dispatchFunction(rand() % Num_Functions, szArgs[i]);
 }
 return 0;
}

Enumerating, Creating, and Deleting Registry Keys and the Windows API

The RegOpenKeyEx() function retrieves an HKEY open handle that is assigned to an HKEY variable via a pointer to that variable, which is passed in as the fifth and final parameter of the function. The return value for RegOpenKeyEx() is normally ERROR_SUCCESS.

The RegEnumKeyEx() function enumerates the subkey names of an open registry key. The RegEnumKeyEx() function takes 8 parameters. The first parameter of RegOpenKeyEx() is a handle to an open registry key that was opened with the KEY_ENUMERATE_SUB_KEYS access right. The KEY_READ access right encompasses the KEY_ENUMERATE_SUB_KEYS access right.

The second parameter for the RegEnumKeyEx() function is a DWORD value indicating the index of the subkey to retrieve. This DWORD value should be 0 on the first call and then should be incremented on each subsequent call.

The third parameter for the RegNumKeyEx() function is a pointer to the buffer that stores the name of the subkey, including a terminating NULL character. The size limit for a key name is 255 characters. The fourth parameter is a pointer to a DWORD value that indicates the size limit of the buffer we are sending into the function; this size is in characters and includes the terminating NULL character.

The fifth parameter is reserved and must be NULL; the sixth and seventh parameters can be set to NULL, so we will. To be fair, they are rarely used. The final parameter is a pointer to a FILETIME structure.

If the function succeeds, the value is ERROR_SUCCESS.

#include "stdafx.h"
#include <Windows.h>

int main()
{
 const int Max_Key_Length = 256;
 LONG lRValue;
 DWORD dwIndex;
 DWORD dwKeyNameLength = Max_Key_Length;
 HKEY hOpenKey; 

 //let's get an open key 
 //use the HKEY_CURRENT_USER predefined key
 //let's look for the Console subkey
 //third argument is reserved, so 0
 //we want to be able to read and enumerate the subkeys
 //final argument is a pointer to the 
 //HKEY variable
 lRValue = RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("SOFTWARE"), 0, KEY_READ, &hOpenKey);
 
 //check to see that we opened the key
 if (lRValue == ERROR_SUCCESS) {
 printf("Opened the subkey successfully.\n\n");
 }
 else {
 printf("Error encountered.\n");
 exit(EXIT_FAILURE);
 }

 //let's enumerate the subkeys
 
 //priming read 
 lRValue = RegEnumKeyEx(hOpenKey, dwIndex, keyName, &dwKeyNameLength, 0, NULL, NULL, &structFT);
 while (lRValue == ERROR_SUCCESS) {
 _tprintf(_T("%s\n"), keyName);
 dwKeyNameLength = Max_Key_Length;
 lRValue = RegEnumKeyEx(hOpenKey, ++dwIndex, keyName, &dwKeyNameLength, 0, NULL, NULL, &structFT);
 }

 if (lRValue == ERROR_NO_MORE_ITEMS) {
 printf("\nSubkeys all enumerated successfully.\n\n");
 }

 return 0;
}

We can create a new Registry key with the RegCreateKeyEx() function. If the subkey already exists, then the RegCreateKeyEx() function opens it. The RegCreateKeyEx() function takes 9 parameters.

The first parameter for RegCreateKeyEx() is an open handle to a registry key. We must have KEY_CREATE_SUB_KEY access to the specified key. The second parameter is the name of the subkey to be created or opened. This parameter cannot be NULL.

The third parameter for RegCreateKeyEx() is reserved and so must be set to 0. The fourth parameter can be set to NULL. The fifth parameter is usually set to 0; however, we will set the option to REG_OPTION_VOLATILE, which means that the key created by the function will not be saved. Nonvolatile registry information is stored in a file and saved for when Windows restarts. Volatile registry keys only exist in memory and do not persist between system restarts.

The sixth parameter specifies the security and access rights for the new key. We will use KEY_ALL_ACCESS for this parameter. The seventh parameter is a pointer to a SECURITY_ATTRIBUTES structure; if we specify NULL for this parameter, then the key receives the default security descriptor.

The eighth argument is a pointer to an HKEY handle that will receive the handle to the new key. The ninth and final parameter is a pointer to a DWORD variable the will store one of two values, REG_OPENED_EXISTING_KEY or REG_CREATED_NEW_KEY.

#include "stdafx.h"
#include <Windows.h>

int main()
{
 HKEY hKey, hSubkey;
 DWORD dwDisposition;
 TCHAR *tszSubkeyName = _T("Volatile Environment");
 TCHAR *tszNewkeyName = _T("testing");
 LONG lReturnValue = 0;

 lReturnValue = RegOpenKeyEx(HKEY_CURRENT_USER, tszSubkeyName, 0, KEY_CREATE_SUB_KEY, &hKey);

 if (lReturnValue == ERROR_SUCCESS) {
 _tprintf(_T("Accessed HKEY_CURRENT_USER\\%s\n"), tszSubkeyName);
 }
 else {
 exit(EXIT_FAILURE);
 }

 //create new key
 lReturnValue = RegCreateKeyEx(hKey, tszNewkeyName, 0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &hSubkey, &dwDisposition);

 if (lReturnValue == ERROR_SUCCESS) {
 printf("Created/opened new subkey.\n");
 if (dwDisposition == REG_OPENED_EXISTING_KEY) {
 _tprintf(_T("%s already exists, is now opened.\n"), tszNewkeyName);
 }
 else {
 _tprintf(_T("%s created, is now opened.\n"), tszNewkeyName);
 }
 }

 //clean up after ourselves
 RegCloseKey(hKey);

 return 0;
}

Note that we use the RegCloseKey() function to close an open handle to a Registry key.

We use the RegDeleteKey() function to delete a key. It is a simple function to use; it takes only two arguments. The first argument is an open key handle, the second is the name of the subkey to be deleted.

#include "stdafx.h"
#include <Windows.h>

int main()
{

 LONG lRValue = 0;

 lRValue = RegDeleteKey(HKEY_CURRENT_USER, _T("Volatile Environment\\testing"));
 
 if (lRValue == ERROR_SUCCESS) {
 printf("The testing subkey was deleted successfully.\n");
 }
 else {
 printf("The testing subkey was not deleted successfully.\n");
 printf("Error %d\n", lRValue);
 if (lRValue == ERROR_FILE_NOT_FOUND) {
 printf("The registry key was not found.\n");
 }
 }

 return 0;
}

Pointers and Dynamic Memory

The malloc() function allocates a certain number of bytes of heap storage for our use. We specify the size of the memory block to allocate in bytes using a value of type size_t, which is an alias of an unsigned integer type. The function returns a pointer to the allocated memory block.

#include "stdafx.h"
#include <stdlib.h>

int main()
{
 size_t NumBytes = 256;
 void *ptrMemBlock;

 ptrMemBlock = malloc(NumBytes);

 if (ptrMemBlock == NULL) {
 printf("Failed to allocated memory.\n");
 exit(EXIT_FAILURE);
 }
 else {
 printf("Allocated memory.\n");
 }

 free(ptrMemBlock);

 return 0;
}

Note that the free() function returns a previously allocated block of memory to the free list.

To reiterate, size_t is a typedef representing the number of bytes to allocate.

The malloc() function returns a null pointer if it fails.

When allocating a block of memory of a particular type, we can use the sizeof() operator to calculate the number of bytes per unit to allocate. The use of sizeof() is important for maintaining portability.

#include "stdafx.h"
#include <stdlib.h>

int main()
{
 size_t byteSize = 512;
 //wchar_t technically isn't standard (?)
 //but we don't follow *their* rules here
 wchar_t *wszStr;

 wszStr = (wchar_t*)malloc(sizeof(wchar_t) * byteSize);

 if (wszStr == NULL) {
 printf("Failed to allocate buffer. Nuts!\n");
 exit(EXIT_FAILURE);
 }
 else {
 //copy the unicode string 
 //wcspy_s takes three params
 //the destination, the max number of wide 
 //chars to copy, and the source
 wcscpy_s(wszStr, byteSize, L"It can be said that the history of science is a history of the expansion of the human body's functionality, in other words, the history of humanity's cyberization.");
 }

 printf("%ls.\n", wszStr);

 free(wszStr);

 return 0;
}

Technically, it’s a good idea to initialize pointers to NULL at declaration and after free. With small programs such as these examples, it may seem extraneous, but its better to get in the habit of doing it always, even when unnecessary, then to forget even once when it is necessary.

Setting unused pointers to NULL is basically all-around good defensive programming, as it protects us from dangling pointer bugs, which can be difficult to track down. Always remember that in C and C++ pointers are inherently unsafe.

 
#include "stdafx.h"
#include <stdio.h>

int main()
{
	int i;
	int *iPtrBlock = NULL;
	
	iPtrBlock = (int *)malloc(sizeof(int) * 4);

	if (iPtrBlock == NULL) {
		printf("Error allocating memory.\n");
		exit(EXIT_FAILURE);
	}

	*iPtrBlock = 42;
	*(iPtrBlock + 1) = 73;
	*(iPtrBlock + 2) = 1138;
	*(iPtrBlock + 3) = 404;

	for (i = 0; i < 4; i++) {
		printf("%d\n", *(iPtrBlock + i));
	}

	//verify we are not passing free()
	//a NULL pointer
	if (iPtrBlock != NULL) {
		free(iPtrBlock);
	}
	
	iPtrBlock = NULL;
	
    return 0;
}


The realloc() function will change the size of the space allocated on the heap. If realloc() returns a NULL, it was unable to allocate any more free space on the heap; however, this is an unlikely event given memory sizes and the amount of data we happen to be working with. The realloc() function returns a pointer, as it may have had to move the memory block in order to fulfill the request to increase the block’s size.

#include <stdlib.h>
#include <Windows.h>

int main()
{
 double dNum = 0;
 const size_t szBuffer_Size = 256;
 const size_t numDigits = 16;
 double *dStorage = (double*)malloc(sizeof(double));
 char szBuffer[szBuffer_Size];
 //get handle for stdin
 HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
 DWORD dwBytesRead;
 int iLocation = 0;
 

 while (true) {
 printf("Please enter a number: ");
 ReadFile(hStdin, szBuffer, szBuffer_Size, &dwBytesRead, NULL);
 if (dwBytesRead > numDigits) {
 szBuffer[numDigits] = '\0';
 }
 else {
 szBuffer[dwBytesRead] = '\0';
 }
 //convert string to double
 dNum = atof(szBuffer);

 printf("Entering %f into database.\n", dNum);
 *(dStorage + iLocation) = dNum;
 
 printf("Continue entering numbers? (y/n) ");
 ReadFile(hStdin, szBuffer, szBuffer_Size, &dwBytesRead, NULL);
 *(szBuffer + 1) = '\0';
 if (*szBuffer == 'n' || *szBuffer == 'N') {
 printf("Thank you.\n");
 break;
 }

 iLocation++;

 //allocate more memory
 dStorage = (double*)realloc(dStorage, sizeof(double) * (iLocation + 1));
 } //end while loop

 printf("Numbers entered: \n");
 while (iLocation >= 0) {
 printf("%f \t", *(dStorage + iLocation));
 iLocation--;
 }

 return 0;
}

Opening a Windows Registry Key with C

The registry is a database for system and application configuration information. We access registry values through registry keys, which in turn can contain other keys, or else key/value pairs.

Information stored in the registry includes the Windows version number, information about the computer’s hardware, user-specific information, security information, installed services, and more.

Several predefined keys function as entry points into the registry. The HKEY_LOCAL_MACHINE key stores physical information about the system, along with information about installed software. The HKEY_USERS key stores user configuration information. The HKEY_CURRENT_USER key stores user-specific information, including environment variables and application preferences. The HKEY_CURRENT_CONFIG key defines current settings, such as display resolution and fonts.

We can access and manage the registry through a number of registry API functions. Registry management functions empower us to query and modify the key/value pairs that make up the registry, as well as create new subkeys and key/value pairs.

The RegOpenKeyEx() function can be used to open a named subkey. The function takes five parameters. The first parameter can be an HKEY handle, or else one of eight predefined keys.

#include "stdafx.h"
#include <Windows.h>

int main()
{
 printf("HKEY_CLASSES_ROOT (%x) - used by shell and COM applications.\n", HKEY_CLASSES_ROOT);
 printf("HKEY_CURRENT_USER (%p) - defines the preferences of the current user.\n", HKEY_CURRENT_USER);
 printf("HKEY_LOCAL_MACHINE (%p) - defines the physical state of the computer, including plug and play informaton.\n", HKEY_LOCAL_MACHINE);
 printf("HKEY_USERS (%p) - defines the default configuration for new users and the configuration for the current user.\n", HKEY_USERS);
 printf("HKEY_CURRENT_CONFIG (%p) - contains information about the current hardware profile of the local system.\n", HKEY_CURRENT_CONFIG);

 return 0;
}

The second parameter is the name of the subkey we wish to open. The subkey can be a path, or we can specify NULL here; this causes a new, duplicate key to be opened. The third parameter is reserved; we must specify 0 here. The fourth parameter is an access mask that describes the security level for the new key.

#include "stdafx.h"
#include <Windows.h>

int main()
{
 printf("KEY_ALL_ACCESS (%p)\n", KEY_ALL_ACCESS);
 printf("KEY_WRITE (%p)\n", KEY_WRITE);
 printf("KEY_QUERY_VALUE (%p)\n", KEY_QUERY_VALUE);
 printf("KEY_ENUMERATE_SUB_KEYS (%p)\n", KEY_ENUMERATE_SUB_KEYS);

 return 0;
}

Note that the function fails if the specified key’s security descriptor does not permit the requested level of access. Upon success, the function returns ERROR_SUCCESS.

 #include "stdafx.h"
#include <Windows.h>

int main()
{
 HKEY hTheKey;
 long long int llRValue;
 
 llRValue = RegOpenKeyEx(
 HKEY_CURRENT_USER, //use predefined key
 NULL, //just open a copy of above
 0, //don't mess with this one
 KEY_QUERY_VALUE, //just asking questions
 &hTheKey //pointer to HKEY variable
 );

 //check the return value
 if (llRValue == ERROR_SUCCESS) {
 printf("Duplicate registry key created!\n");
 }
 else {
 printf("Duplicate registry key not created!\n");
 }

 return 0;
}

We should close an open key handle with the RegCloseKey() function.

#include "stdafx.h"
#include <Windows.h>

int main()
{
 HKEY hOurKey;
 long long llReturn;
 WCHAR wszRegistryKeyName[] = L"SOFTWARE\\Microsoft";


 llReturn = RegOpenKeyEx(
 HKEY_LOCAL_MACHINE,
 wszRegistryKeyName,
 0,
 KEY_READ,
 &hOurKey
 );

 if (llReturn == ERROR_SUCCESS) {
 printf("Key acquired!\n");
 }
 else {
 printf("Key not acquired!\n");
 exit(EXIT_FAILURE);
 }

 //clean up!!!
 RegCloseKey(hOurKey);

 return 0;
}

Note that registry keys are not case sensitive.

Even More About Threads with win32 API

A Windows process doesn’t technically run; threads run, the process is simply a manager. Threads are the actual entities scheduled to execute code on calls. A thread by default runs under its parent process’s security context.

In our first program, we will create a thread to execute the classic “Hello World!” program. We will use the CreateThread() function, which takes six parameters. The first two parameters we will pass 0 to, the third parameter is the memory location of the function we wish to run, which we deliver in the form of the function’s name. The fourth parameter is the argument we wish to send to the function. The fifth argument will we specify 0 for, as we do not wish to pass in any special flags. The final argument is a pointer to a DWORD value to store the thread ID of the new thread.

 #include "stdafx.h"
#include <Windows.h>

//entry function for child thread
DWORD WINAPI ChildThreadFunc(LPVOID lpThreadParam) {
 //parameter is an integer value indicating
 //the number of seconds the thread should run for
 DWORD dwNumSeconds = (DWORD)lpThreadParam;
 DWORD dwThreadID = GetCurrentThreadId();
 while (dwNumSeconds-- > 0) {
 printf("Hello from thread ID %d\n", dwThreadID);
 Sleep(1000);
 }
 //lets return 0 
 return 0;
}

int main()
{
 HANDLE hChildThread;
 DWORD dwChildID, dwParentID, dwExitCode;

 //determine primary thread's ID number
 dwParentID = GetCurrentThreadId();

 printf("Application started; primary ID is %d\n", dwParentID);
 
 //launch child thread
 hChildThread = CreateThread(
 0,
 0,
 ChildThreadFunc,
 (LPVOID)5,
 0,
 &dwChildID
 );

 //check if the thread was created
 if (hChildThread == INVALID_HANDLE_VALUE) {
 printf("Error creating thread.\n");
 exit(EXIT_FAILURE);
 }
 else {
 printf("Thread %d created.\n", dwChildID);
 }

 Sleep(5000);

 //get the exit code 
 GetExitCodeThread(hChildThread, &dwExitCode);

 printf("Child thread exited with value %d\n", dwExitCode);
 printf("Exiting main thread ID %d\n", dwParentID);

 //close the handle
 CloseHandle(hChildThread);

 return 0;
}

Note that is possible to make the current thread stop executing for a specified period of time by calling the Sleep() function. In our second program, we will code a simple thread that beeps in the background.

#include "stdafx.h"
#include <Windows.h>

int GetInput(TCHAR tszPtr[], int Buffer_Size);
int ConvertTCHARToInt(TCHAR *tszPtr);
DWORD WINAPI BeepThread(LPVOID ptrParam);

struct SBeep {
 int iIterations;
 int iFrequency;
 int iDuration;
};

int main()
{
 HANDLE hThread;
 DWORD dwThreadID;
 struct SBeep structBeep;
 const int Buffer_Size = 56;
 TCHAR tszBuffer[Buffer_Size];

 printf("Please enter the number of beeps you'd like us to produce!\n");
 GetInput(tszBuffer, Buffer_Size);
 structBeep.iIterations = ConvertTCHARToInt(tszBuffer);
 printf("Please enter the frequency you'd like us to produce!\n");
 GetInput(tszBuffer, Buffer_Size);
 structBeep.iFrequency = ConvertTCHARToInt(tszBuffer);
 printf("Please enter the duration of the beep!\n");
 GetInput(tszBuffer, Buffer_Size);
 structBeep.iDuration = ConvertTCHARToInt(tszBuffer);
 _tprintf(_T("We will beep %d times for you at %d frequency for %d duration.\n"), 
 structBeep.iIterations, structBeep.iFrequency, structBeep.iDuration);
 //create a thread to execute the BeepThread function
 hThread = CreateThread(0, 0, BeepThread, &structBeep, 0, &dwThreadID);

 //wait for the thread to finish
 while (WaitForSingleObject(hThread, 500) == WAIT_TIMEOUT) {
 printf("Waiting on thread %d.\n", dwThreadID);
 }

 printf("All done!\n");

 return 0;
}

//get the string input 
int GetInput(TCHAR tszPtr[], int Buffer_Size) {
 TCHAR tch;
 int iCount = 0;
 while ((iCount < Buffer_Size) && (tch = getchar())!='\n'){
 *(tszPtr + iCount++) = tch;
 }
 //null-terminate the string
 *(tszPtr + iCount) = '\0';
 return iCount;
}

int ConvertTCHARToInt(TCHAR *tszPtr){
 int iCount = 0;
 int iRVal = 0;
 int iPlace = 1;
 //get length of string
 while (*(tszPtr + iCount) != '\0') { iCount++; }
 //iterate backwards through string
 while (iCount-- > 0) {
 //to get numeric value of character, subtract character zero
 iRVal += (*(tszPtr + iCount) -'0') * iPlace;
 iPlace *= 10;
 }
 return iRVal;
}

//the function to run in the thread
DWORD WINAPI BeepThread(LPVOID ptrParam) {
 //cast LPVOID param to correct data type
 struct SBeep *structBeep = (SBeep*)ptrParam;
 while (structBeep->iIterations-- > 0) {
 Beep(structBeep->iFrequency, structBeep->iDuration);
 Sleep(500);
 }
 return 0;
}

This program makes use of the WaitForSingleObject() function. We pass in as timeout value 200 milliseconds. If the thread has not completed by the time at which the timeout value has been reached, it returns the value WAIT_TIMEOUT.

As we have seen, it is possible to pass in a parameter to the thread function in the form of a pointer to a structure.

#include "stdafx.h"
#include <Windows.h>

int GetInput(TCHAR tszPtr[], int Buffer_Size);
int ConvertTCHARToInt(TCHAR *tszPtr);
DWORD WINAPI BeepThread(LPVOID ptrParam);
VOID BeepForUs(int iIter, int iFreq, int iDur, int iPause);

struct SBeep {
 int iIterations;
 int iFrequency;
 int iDuration;
 //new field, pause
 int iPause;
};

int main()
{
 const int Buffer_Size = 56;
 const int Num_Threads = 5;

 struct SBeep structBeep;

 TCHAR tszBuffer[Buffer_Size];
 //we will have an array of thread handles
 HANDLE hThread[Num_Threads];

 DWORD dwThreadID;
 int iNumThreads = 0;

 do {
 printf("Please enter the number of beeps you'd like us to produce!\n");
 GetInput(tszBuffer, Buffer_Size);
 structBeep.iIterations = ConvertTCHARToInt(tszBuffer);
 printf("Please enter the frequency you'd like us to produce!\n");
 GetInput(tszBuffer, Buffer_Size);
 structBeep.iFrequency = ConvertTCHARToInt(tszBuffer);
 printf("Please enter the duration of the beep!\n");
 GetInput(tszBuffer, Buffer_Size);
 structBeep.iDuration = ConvertTCHARToInt(tszBuffer);
 printf("Please enter pause between beeps!\n");
 GetInput(tszBuffer, Buffer_Size);
 structBeep.iPause = ConvertTCHARToInt(tszBuffer);
 _tprintf(_T("We will beep %d times for you at %d frequency for %d duration and %d pause length.\n\n"),
 structBeep.iIterations, structBeep.iFrequency, structBeep.iDuration, structBeep.iPause);

 //create a thread and add it to the array
 hThread[iNumThreads] = CreateThread(0, 0, BeepThread, &structBeep, 0, &dwThreadID);
 } while (++iNumThreads < Num_Threads);

 //wait for the thread to finish
 while (WaitForSingleObject(hThread, 500) == WAIT_TIMEOUT) {
 printf("Waiting on thread %d.\n", dwThreadID);
 }

 //now we wait for all threads to finish executing
 WaitForMultipleObjects(Num_Threads, hThread, TRUE, INFINITE);

 printf("All done!\n");

 return 0;
}

//get the string input 
int GetInput(TCHAR tszPtr[], int Buffer_Size) {
 TCHAR tch;
 int iCount = 0;
 while ((iCount < Buffer_Size) && (tch = getchar())!='\n'){
 *(tszPtr + iCount++) = tch;
 }
 //null-terminate the string
 *(tszPtr + iCount) = '\0';
 return iCount;
}

int ConvertTCHARToInt(TCHAR *tszPtr){
 int iCount = 0;
 int iRVal = 0;
 int iPlace = 1;
 //get length of string
 while (*(tszPtr + iCount) != '\0') { iCount++; }
 //iterate backwards through string
 while (iCount-- > 0) {
 //to get numeric value of character, subtract character zero
 iRVal += (*(tszPtr + iCount) -'0') * iPlace;
 iPlace *= 10;
 }
 return iRVal;
}

//the function to run in the thread
DWORD WINAPI BeepThread(LPVOID ptrParam) {
 //make local copies of info
 struct SBeep * sBeep = (SBeep*)ptrParam;
 BeepForUs(sBeep->iIterations, sBeep->iFrequency, sBeep->iDuration, sBeep->iPause);
 return 0;
}

VOID BeepForUs(int iIter, int iFreq, int iDur, int iPause) {
 while (iIter-- > 0) {
 Beep(iFreq, iDur);
 Sleep(iPause);
 }
}

Thread prioritization is how important a thread is relative to others within the process. After we create a thread, we can change its priority from the default of normal to something more appropriate for the application. The SetThreadPriority() function takes two arguments; the first is the handle to the thread, the second is the priority value for the thread.

#include "stdafx.h"
#include <Windows.h>


int main()
{
 //thread priority named constants
 printf("THREAD_PRIORITY_TIME_CRITICAL = %d\n", THREAD_PRIORITY_TIME_CRITICAL);
 printf("THREAD_PRIORITY_HIGHEST = %d\n", THREAD_PRIORITY_HIGHEST);
 printf("THREAD_PRIORITY_ABOVE_NORMAL = %d\n", THREAD_PRIORITY_ABOVE_NORMAL);
 printf("THREAD_PRIORITY_NORMAL = %d\n", THREAD_PRIORITY_NORMAL);
 printf("THREAD_PRIORITY_BELOW_NORMAL = %d\n", THREAD_PRIORITY_BELOW_NORMAL);
 printf("THREAD_PRIORITY_LOWEST = %d\n", THREAD_PRIORITY_LOWEST);

 return 0;
}

In our next program, we will create five threads and assign them an arbitrary workload to accomplish. We will then randomly modify their priority and wait for all of them to complete. The threads should complete in order of priority, with the threads with a higher priority number finishing first.

#include "stdafx.h"
#include <Windows.h>
//for intializing srand()
#include <ctime>

DWORD WINAPI DoWork(LPVOID lpParam) {
 int i = INT_MAX;
 int j = 1;
 while (i-- > 0) { j = i - 1; }
 printf("Thread %d done.\n", GetCurrentThreadId());
 return 0;
}


int main()
{
 const int Num_Threads = 5;
 const int Num_Priorities = 5;
 HANDLE hThreads[Num_Threads];
 DWORD dwThreadIDs[Num_Threads];
 int iNamedCons[Num_Priorities] = { THREAD_PRIORITY_LOWEST, THREAD_PRIORITY_BELOW_NORMAL, THREAD_PRIORITY_NORMAL, THREAD_PRIORITY_ABOVE_NORMAL, THREAD_PRIORITY_HIGHEST };
 int iCounter;
 int iRandValue; 

 srand(time(NULL));

 for (iCounter = 0; iCounter < Num_Threads; iCounter++) {
 hThreads[iCounter] = CreateThread(0, 0, DoWork, 0, 0, &dwThreadIDs[iCounter]);
 }

 //alter thread priority
 for (iCounter = 0; iCounter < Num_Threads; iCounter++) {
 iRandValue = rand() % Num_Priorities;
 printf("Setting thread ID %d to priority %d\n", dwThreadIDs[iCounter], iNamedCons[iRandValue]);
 SetThreadPriority(hThreads[iCounter], iNamedCons[iRandValue]);
 }

 WaitForMultipleObjects(Num_Threads, hThreads, TRUE, INFINITE);

 return 0;
}

Well, that is enough for today. Take a look at my book, if you are interested: http://www.amazon.com/Big-Als-C-Standard-ebook/dp/B00A4JGE0M/