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/

Advertisements

Threads and Processes with win32

We can create a separate process from within a running application using the CreateProcess() function. In our program, we will retrieve the current process’s startup information using the GetStartupInfo() function.

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


int main()
{
 TCHAR szBuffer[MAX_PATH];
 STARTUPINFO structStartupInfo;
 PROCESS_INFORMATION structProcInfo;
 BOOL bSuccess;
 DWORD dwCharsRead;


 //get the name of the executable to run in the 
 //new process
 printf("Enter the command to execute, please: ");
 ReadConsole(GetStdHandle(STD_INPUT_HANDLE), szBuffer, _tcslen(szBuffer) - 2, &dwCharsRead, NULL);
 szBuffer[dwCharsRead - 2] = '';

 GetStartupInfo(&structStartupInfo);


 bSuccess = CreateProcess(0, szBuffer, 0, 0, FALSE, CREATE_NEW_CONSOLE, 0, 0, &structStartupInfo, &structProcInfo);

 if (bSuccess) {
 printf("New process created successfully.\n");
 //wait for child process to complete
 if ((WaitForSingleObject(structProcInfo.hProcess, INFINITE)) == WAIT_OBJECT_0) {
 printf("Process %d ended.\n", structProcInfo.dwProcessId);
 }
 CloseHandle(structProcInfo.hProcess);
 CloseHandle(structProcInfo.hThread);
 }

 printf("Finished!\n");

 return 0;
}

For the CreateProcess() function, we can indicate the executable to run in either the first or second parameter. By and large, most people use the second parameter to define the executable and any command line arguments.

When a process spawns a child process, the child process is completely independent of the parent. It is possible, however, for the child process to inherit handles from the parent if the parent marks any of its handles as inheritable.

The ParentProcess program

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

//parent process
HANDLE hFile;
const int Max_Buffer = 2056;

int main()
{
 STARTUPINFO structSUpInfo;
 PROCESS_INFORMATION structProcInfo;
 BOOL bSuccess;
 TCHAR tszFileName[MAX_PATH];
 TCHAR tszBuffer[Max_Buffer];
 SECURITY_ATTRIBUTES structSAttrib;
 //we'll use dwFileAttributes to check 
 //for the existence of the child executable
 DWORD dwCharsRead, dwFileAttributes;
 //store file handle number as text
 TCHAR tszFileHandleString[256];
 LARGE_INTEGER uLI;
 
 //set up security attributes
 //so as to allow child process to inherit 
 //file handle
 structSAttrib.nLength = sizeof(SECURITY_ATTRIBUTES);
 structSAttrib.lpSecurityDescriptor = 0;
 structSAttrib.bInheritHandle = TRUE;

 //get file name
 printf("Please enter the name of the file: ");
 bSuccess = ReadConsole(GetStdHandle(STD_INPUT_HANDLE), tszFileName, MAX_PATH, &dwCharsRead, NULL);
 if (bSuccess) {
 printf("File name acquired.\n");
 }
 else {
 exit(EXIT_FAILURE);
 }

 //remove windows newline chars
 tszFileName[dwCharsRead - 2] = '';

 printf("Creating file.\n");

 hFile = CreateFile(tszFileName, GENERIC_ALL, FILE_SHARE_READ | FILE_SHARE_WRITE, &structSAttrib, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
 
 if (hFile != INVALID_HANDLE_VALUE) {
 printf("File created.\n");
 }
 else {
 printf("Error creating file. Error %d. \n", GetLastError());
 exit(EXIT_FAILURE);
 }
 
 //check to make sure executable for child process 
 //is available
 dwFileAttributes = GetFileAttributes(_T("ChildProcess.exe"));
 if (dwFileAttributes == INVALID_FILE_ATTRIBUTES) {
 printf("Could not find the child process executable.\n");
 exit(EXIT_FAILURE);
 } 

 //get startup info
 GetStartupInfo(&structSUpInfo);

 //convert file handle to string for passing to child process
 printf("Handle value is %d\n", hFile);
 swprintf(tszFileHandleString, 16, L"%d", hFile);


 bSuccess = CreateProcess(_T("ChildProcess.exe"), tszFileHandleString, 0, 0, TRUE, CREATE_NEW_CONSOLE, 0, 0, &structSUpInfo, &structProcInfo);

 if (!bSuccess) {
 printf("Error creating child process.\n");
 exit(EXIT_FAILURE);
 }
 else {
 printf("Child process created.\n");
 }

 WaitForSingleObject(structProcInfo.hProcess, INFINITE);

 CloseHandle(structProcInfo.hProcess);
 CloseHandle(structProcInfo.hThread);

 printf("Child process %d finished.\n", structProcInfo.dwProcessId);
 
 //now let's read from the file
 hFile = CreateFile(tszFileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
 uLI.QuadPart = 0;
 SetFilePointerEx(hFile, uLI, 0, FILE_BEGIN);
 //don't forget - ReadFile counts number of bytes read, not chars!

 bSuccess = ReadFile(hFile, tszBuffer, Max_Buffer - 1, &dwCharsRead, NULL);
 if (bSuccess) {
 printf("File read successfully.\n");
 }

 tszBuffer[dwCharsRead / sizeof(TCHAR)] = '';

 _tprintf(_T("Read from file: %s\n"), tszBuffer);


 return 0;
}

The ChildProcess program

// ChildProcess.cpp : Defines the entry point for the console application.


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

HANDLE hFile;
const int Max_Buffer = 2056;

//child process
int main(int argc, char *argv[])
{
 TCHAR tszBuffer[Max_Buffer];
 int iHNum;
 DWORD dwCharsRead, dwCharsWritten;
 BOOL bSuccess;

 printf("In the child process...\n");

 printf("Received argument %s.\n", argv[0]);
 //convert string argument to handle
 iHNum = atoi(argv[0]);

 hFile = (HANDLE)iHNum;
 printf("Handle value is %d\n", hFile);

 printf("Please enter text: ");
 bSuccess = ReadConsole(GetStdHandle(STD_INPUT_HANDLE), tszBuffer, Max_Buffer, &dwCharsRead, NULL);

 if (bSuccess == FALSE) {
 printf("Error reading text.\n");
 exit(EXIT_FAILURE);
 }

 tszBuffer[dwCharsRead] = '';
 _tprintf(_T("Text to be written to file: %s"), tszBuffer);

 bSuccess = WriteFile(hFile, tszBuffer, _tcslen(tszBuffer)*sizeof(TCHAR), &dwCharsWritten, 0);

 if (bSuccess) {
 printf("File written to successfully.\n");
 }
 else {
 printf("File not written to successfully.\n");
 printf("Error %d\n", GetLastError());
 Sleep(2000);
 exit(EXIT_FAILURE);
 }
 
 CloseHandle(hFile);
 printf("Finishing...");
 Sleep(2000);

 return 0;
}

When any process starts in Windows, it contains by default one thread of execution. The thread is what actually executes the code; the process functions as a container for the heap owned by the application, the global variables, and the environment strings. All threads in one process share the global variable space of their parent process.

The CreateThread() function has six parameters. The first parameter is a pointer to a SECURITY_ATTRIBUTES structure. If NULL, the handle to the thread cannot be inherited. The second argument is a DWORD value indicating the stack size for the thread. If 0, the default stack size for the executable is assigned. The third parameter is the name of the function that the thread starts. The fourth parameter is a void pointer used for passing in arguments to the thread. The fifth parameter indicates any flags we wish to pass in; we will put 0 here. The final parameter is a pointer to a DWORD value to store the thread’s ID.

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

volatile int iCount;
volatile int iCount2;

DWORD WINAPI CountThreadA(void *lpParameter) {
 while (true) {
 iCount++;
 Sleep(200);
 }
}

DWORD WINAPI CountThreadB(void *lpParameter) {
 while (true) {
 iCount2++;
 Sleep(500);
 }
}

int main()
{
 iCount = 1;
 iCount2 = 1;

 char szInput[56];
 HANDLE hThreadOne, hThreadTwo;
 DWORD dwThreadID1, dwThreadID2;

 //start up the threads
 hThreadOne = CreateThread(0, 0, CountThreadA, 0, 0, &dwThreadID1);
 hThreadTwo = CreateThread(0, 0, CountThreadB, 0, 0, &dwThreadID2);

 while (true) {
 printf("Enter A to see the count for the first thread, ");
 printf("B to see the count for the second thread, or ");
 printf("X to end the program: ");

 fgets(szInput, sizeof(szInput), stdin);
 
 if (szInput[0] == 'A'|| szInput[0] == 'a') {
 printf("Thread ID %d Count %d\n", dwThreadID1, iCount);
 }
 if (szInput[0] == 'B'|| szInput[0] == 'b') {
 printf("Thread ID %d Count %d\n", dwThreadID2, iCount2);
 }
 if (szInput[0] == 'X' || szInput[0] == 'x') {
 printf("Exiting");
 break;
 }
 }

 return 0;
}

Note the use of the volatile modifier for the iCount and iCount2 variables. Essentially, we use the volatile modifier for any global variable that will be modified in a multi-threaded application. The volatile keyword is used to basically tell the compiler no to apply any optimizations to the variable.

win32 Threads

A thread is fairly simple: it is created, it executes code, and then it exits.

Threads are created as a result of another thread calling the CreateThread() function, unless the thread is the primary thread for the process.

Usually, the primary thread for the process starts immediately after the thread has been created. If the process is created by calling the CreateProcess() function, however, the primary thread can be created in the suspended state by specifying the CREATE_SUSPENDED flag.

The various thread creation functions take a pointer to a function as a place to begin executing the thread; this function is known as the thread entry procedure. The function pointer must be a pointer to a function of type THREAD_START_ROUTINE.

CreateThread() is the basic win32 thread creation function. This function creates a new thread within the process of the caller. The new thread has its own stack, its own copy of the machine’s registers, and will be scheduled independently by the OS.

The CreateThread() function takes six parameters. The first parameter can be specified as NULL; it is supposed to be a pointer to SECURITY_ATTRIBUTES structure, which describes the security and inheritance properties of the new thread. Putting NULL here will cause the thread to be created with default security and the thread handle will not be inheritable by child processes of the current process.

The second parameter to CreateThread() is a DWORD value indicating the size of the new thread’s stack. The system will automatically round this value to the nearest page. If the parameter is 0, it will be the default size for the process. The third parameter is the address of the user function that the new thread will call when it begins executing. The fourth argument is a pointer to a parameter value to be passed into the function. The fifth parameter is a DWORD value representing creation flags for the thread. The final parameter is a pointer to a DWORD variable that receives the thread ID.

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

DWORD WINAPI threadCreate(LPVOID threadParam);
DWORD WINAPI threadCreateWithParam(LPVOID threadParam);

int _tmain(int argc, _TCHAR* argv[])
{
    DWORD dwThreadIDOne, dwThreadIDTwo;
    int iValue = 73;
    int iCounter = 0;

    CreateThread(NULL, NULL, &threadCreate, NULL, 0, &dwThreadIDOne);
    printf("Just created thread %d\n", dwThreadIDOne);

    Sleep(2000);

    CreateThread(NULL, NULL, &threadCreateWithParam, &iValue, 0, &dwThreadIDTwo);
    printf("Just created thread %d\n", dwThreadIDTwo);

    for (iCounter; iCounter < 4; iCounter++){
        printf("In main thread.\n");
        Sleep(500);
    }

    Sleep(3000);

    return 0;
}


DWORD WINAPI threadCreate(LPVOID threadParam){
    int i = 10;
    while (i-- > 0){
        printf("In the new thread.\n");
        Sleep(500);
    }

    //oddly named ERROR_SUCCESS
    //indicates everything is copacetic 
    return ERROR_SUCCESS;
}

DWORD WINAPI threadCreateWithParam(LPVOID threadParam){
    int *iParam = (int*)threadParam;
    int i = 5;
    while (i-- > 0){
        printf("In the thread that received %d.\n", *iParam);
        Sleep(500);
    }

    //aka return 0
    return ERROR_SUCCESS;
}

When threads are initially created, they are unaware of their identifier. From within the thread, we can acquire the unique numeric value assigned to the thread when it is created by calling the GetCurrentThreadId() function.

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

DWORD WINAPI makeThread(LPVOID lpThreadParam);

int _tmain(int argc, _TCHAR* argv[])
{
    DWORD dwThreadID;

    CreateThread(NULL, NULL, &makeThread, NULL, NULL, &dwThreadID);

    printf("Created thread %d from main thread %d.\n", dwThreadID, GetCurrentThreadId());

    Sleep(1000);
    
    return 0;
}


DWORD WINAPI makeThread(LPVOID lpThreadParam){
    printf("In thread %d.\n", GetCurrentThreadId());

    return ERROR_SUCCESS;
}

We can also acquire a HANDLE to the current thread by calling the GetCurrentThread() function. Note that this HANDLE is actually a pseudo-handle.

Threads of Execution via win32

The Windows NT kernel implements a threading schedule by taking control of the processor when a quantum is finished, pausing the running thread, and resuming the scheduled thread. Every time the thread is changed is known as a context switch. In order to perform a context switch, the Windows NT kernel must save the contents of the processor’s register for the pausing thread, and restore the processor’s register contents for the resuming thread. Of course, this imposes a time penalty, which is a factor when deciding upon thread quantum size.

Adding to the complexity of thread scheduling is the fact that some processes and threads are more important than other ones. Reflecting this, we can assign the Windows process and thread objects a priority. Priorities tell the scheduler to run some threads longer than others, in essence.

Note as well that interprocess context switches are more costly than intraprocess context switches.

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

int _tmain(int argc, _TCHAR* argv[])
{
	//initialize the SYSTEM_INFO data structure
	SYSTEM_INFO structSI;

	GetSystemInfo(&structSI);

	//display number of processors
	printf("%d processors.\n", structSI.dwNumberOfProcessors);

	return 0;
}

One of the nice features of a thread is the ability to suspend itself or be suspended. When a thread is suspended, the kernel knows not to include that thread in the schedule until it resumes. Once a thread switches from the suspended to the running state, it simply takes its place in the run schedule.

Threads are kernel objects, and the process that creates them references them through object handles. An active thread object is either suspended, ready to run, or running on a processor.

We use the CreateThread() API to start a new thread of execution. The CreateThread() system call makes a new entry in the system’s list of threads to execute. The CreateThread() call takes five parameters.

The first parameter describes the security to associate the new thread object. This information tells the system what accounts to allow and deny access to the thread for modification and synchronization. The first parameter is of type LPSECURITY_ATTRIBUTES. By specifying NULL for this parameter, we use the default security.

The second parameter is a DWORD value that defines the size of the stack the thread will receive. The process typically accepts the default by setting 0 for this parameter.

The third parameter is the address of the function to execute. Once this function finishes executing, the thread will terminate. Often, this function is called ThreadProc. A ThreadProc function is a user-defined function that returns a DWORD value and accepts a single parameter of type LPVOID.

The fourth parameter is a pointer to the value we wish to pass into the ThreadProc function, if any.

The second-to-last parameter is a process creation flag, of which there two that are allowable to use, the main one being CREATE_SUSPENDED. The final parameter, which is optional, is a pointer to a DWORD value to store the thread identifier for the new thread.

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

//prototype for function we will send 
//to CreateThread()
DWORD WINAPI ThreadProc(LPVOID lpParam);

int _tmain(int argc, _TCHAR* argv[])
{
	HANDLE hNewThread;

	hNewThread = CreateThread(
		NULL, //default security
		0, //default stack
		ThreadProc, //thread function name 
		NULL, //no argument to thread function
		0, //default creation flags
		NULL //don't care about thread ID
		);

	if(hNewThread != INVALID_HANDLE_VALUE){
		//success!
		printf("New thread created...\n");
	} else {
                exit(EXIT_FAILURE);
        }

	//do something in this thread
	for(int i = 0; i < 5; i++){
		printf("Hello from the old thread.\t");
		printf("(thread ID %d)\n", GetCurrentThreadId());
		Sleep(100);
	}

	//does the new thread get to finish? 
	//most likely not!
	CloseHandle(hNewThread);

	return 0;
}


DWORD WINAPI ThreadProc(LPVOID lpParam){
	for(int i = 0; i < 15; i++){
		Sleep(100);
		printf("\tHello from the new thread.\t");
		printf("(thread ID %d)\n", GetCurrentThreadId());
	}

	//return 0
	return 0;
}

Note that WINAPI is an alias, er, macro, for __stdcall. The __stdcall macro specifies that the callee cleans the stack, specifically, the parameters that were pushed onto the stack. The __stdcall convention is mainly used by the Windows API.

As we saw when running the program above, we need the parent thread to wait for the caller thread to exit… at least, if we want to be sure the new thread finishes doing whatever it is that it is doing. We do this with the WaitForSingleObject() function. The WaitForSingleObject() function waits until the object specified by the first HANDLE parameter is in the signaled state, or until the time out interval indicated in the second parameter has elapsed.

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

//prototype
DWORD WINAPI ThreadProc(LPVOID lpParam);

int _tmain(int argc, _TCHAR* argv[])
{
	DWORD dwRValue;
	HANDLE hThread = CreateThread(
		NULL, //default security
		0, //default stack size
		ThreadProc, //start address of function
		NULL, // no parameters
		0, //no creation flags
		NULL //don't need the creation ID
		);

	if(hThread == INVALID_HANDLE_VALUE){
		printf("Failed to create new thread...\n");
		exit(EXIT_FAILURE);
	}

	do {
		dwRValue = WaitForSingleObject(hThread, 200);
		printf("\n yo, you done yet??? \n");
	} while(dwRValue != WAIT_OBJECT_0);
	
	printf("New thread is finished!\n");

	CloseHandle(hThread);

	return 0;

}

DWORD WINAPI ThreadProc(LPVOID lpParam){
	for(int i = 0; i < 20; i++){
		printf("Doing Thangs... ");
		Sleep(100);
	}
	return 0;
}

Note that the Do…While loop above might not be so hot if we weren’t sure ahead of time that the new thread was going to be running for longer than one timeout interval.

The WaitForSingleObject() function’s return value is of type DWORD. The WAIT_TIMEOUT value indicates that the function has exceeded its timeout parameter. The WAIT_OBJECT_0, that’s a 0 as in zero by the way, value indicates the indicated object is now in the signaled state.

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

//let's define a custom parameter 
//to send to our new thread
struct structThreadParam{
	char cLetter;
	int iNumber;
};

DWORD WINAPI ThreadProc(LPVOID lpParam);

int _tmain(int argc, _TCHAR* argv[])
{
	structThreadParam structParam;
	structParam.cLetter = 'Z';
	structParam.iNumber = 90210;

	HANDLE hNewThread = CreateThread(
		NULL,
		0, 
		ThreadProc,
		&structParam,
		0,
		NULL
		);

	if(hNewThread != INVALID_HANDLE_VALUE){
		printf("New thread created.\n");
	} else {
		exit(EXIT_FAILURE);
	}

	//let's wait as long as it takes
	WaitForSingleObject(hNewThread, INFINITE);

	return 0;

}

DWORD WINAPI ThreadProc(LPVOID lpParam){
	//note that conversion is implicit in C
	//but let's be verbose
	structThreadParam *structPtr = (structThreadParam*)lpParam;
	printf("In thread %d: ", GetCurrentThreadId());
	printf("Received %c%d \n", structPtr->cLetter, structPtr->iNumber);

	return 0;
}

Note that we can use the INFINITE value to tell WaitForSingleObject() to wait until the specified thread is finished.

Interested in learning standard C? Check out my book at amazon.com/Big-Als-C-Standard-ebook/dp/B00A4JGE0M/

Creating Threads Using the win32 API

The CreateThread() API call can be used to create and run a thread. A thread needs to have code to execute, and we will provide this via a ThreadProc callback function. A ThreadProc function takes a single argument of type LPVOID and returns a DWORD value.

The CreateThread() function returns a thread handle that is valid only within our process. The handle refers to the kernel thread object managing the new thread. Most of the thread API class use this thread handle.

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

DWORD WINAPI threadFunc(LPVOID lPtr);

int _tmain(int argc, _TCHAR* argv[])
{

	HANDLE hThread;


	printf("In main.\n");

	hThread = CreateThread(
		NULL, //default security
		0, //default stack size
		threadFunc, //LPTHREAD_START_ROUTINE
		0, //argument to threadFunc
		0, //creation flags
		0
		);

	if(hThread!=NULL){
		printf("Thread created successfully.\n");
	} else {
		printf("Error creating thread.\n");
	}

	//give called thread time to finish
	Sleep(1500);

	return 0;
}

DWORD WINAPI threadFunc(LPVOID lPtr){
	printf("In the thread.\n");

	return 0;
}

Note that the CreateThread() function returns immediately, so that the calling routine and the new thread execute simultaneously. If the primary thread dies before the thread functions have finished executing, then all threads will be forcibly terminated, and will not have a chance to finish.

The CreateThread() function takes a number of parameters, most of which we should provide with a NULL or 0. The first parameter is a pointer to a SECURITY_ATTRIBUTES structure, which is used to govern who can access the new thread. We can set this parameter to NULL. The second parameter indicates the amount of stack space to be allocated for the new thread. Here we should specify 0, as this causes the stack size to default to the default size of the executable.

The third and fourth parameters are most important, as these specify the address of the ThreadProc function, and a pointer to any arguments we want to supply to it. These arguments are passed via a single void* pointer, so we must ensure that the data is cast back into the right types in the new thread.

The fifth parameter lets us supply flag values which determine how the thread behaves when it is created. Here we will pass 0.

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

DWORD WINAPI TPFunction(LPVOID lPtr);

int _tmain(int argc, _TCHAR* argv[])
{
	int num = 73;
	void *vPtr = &num;

	HANDLE hThreadHandle = CreateThread(
		NULL, //default security
		0, //default stack size
		TPFunction, //function
		vPtr, //argument
		0,
		0
		);

	for(int i = 0; i < 10; i++){
		printf("In main %d \t", i+1);
		Sleep(100);
	}

	return 0;
}

DWORD WINAPI TPFunction(LPVOID lPtr){
        //cast the void pointer to an int pointer
       //then dereference it
	printf("...Received %d in the thread.\n", *((int*)lPtr));

	return 0;
}

The final parameter is a pointer to a DWORD value that can be used to store the thread ID. The thread ID is a unique global value that any or thread or process can use to refer to our thread.

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

DWORD WINAPI theFunction(LPVOID lPtr);

int _tmain(int argc, _TCHAR* argv[])
{
	DWORD dwIDOne, dwIDTwo;
	HANDLE hThreadOne, hThreadTwo;
	int iValOne = 100;
	int iValTwo = iValOne * 2;

	hThreadOne = CreateThread(
		NULL,
		0,
		theFunction,
		(void*)&iValOne,
		0,
		&dwIDOne
		);
	
	if(hThreadOne!=NULL){
		printf("Thread %d created.\n", dwIDOne);
	}

	hThreadTwo = CreateThread(
		NULL,
		0,
		theFunction,
		(void*)&iValTwo,
		0,
		&dwIDTwo
		);

	if(hThreadTwo!=NULL){
		printf("Thread %d created.\n", dwIDTwo);
	}

	for(int i = 0; i < 10; i++){
		printf("In main...\n");
		Sleep(10 * i);
	}

	

	return 0;
}

DWORD WINAPI theFunction(LPVOID lPtr){
	printf("In the new thread, ID %d.\n", GetCurrentThreadId());
	Sleep(100 + *((int*)lPtr));
	printf("Finishing thread %d.\n", GetCurrentThreadId());

	return 0;
}

Note the use of the GetCurrentThreadId() function to get the current ID for the thread.

The GetExitCodeThread() function gets the exit code from the ThreadProc function associated with the handle. The GetExitCodeThread() function has two paramters, the HANDLE of the thread we want the exit code of, and a pointer to a DWORD value to store the exit code.

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

DWORD WINAPI theFunc(LPVOID lPtr);

int _tmain(int argc, _TCHAR* argv[])
{
	DWORD dwExitCode;
	DWORD dwThreadID;

	HANDLE hT = CreateThread(
		NULL,
		0,
		theFunc,
		0,
		0,
		&dwThreadID
		);
	
	//wait for the thread to exit
	while(TRUE){
		GetExitCodeThread(hT, &dwExitCode);
		if(dwExitCode == STILL_ACTIVE){
			printf("Thread %d is still running.\n", dwThreadID);
			Sleep(20);
			continue;
		}
		printf("Thread exit code was %d.\n", dwExitCode);
		break;
	}

	return 0;
}

DWORD WINAPI theFunc(LPVOID lPtr){
	for(int i = 0; i < 10; i++){
		Sleep(20 * i);
	}

	return 0;
}

Note that the GetExitCodeThread() function itself returns a Boolean value.