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.

Advertisements

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.

Diving into win32 Processes

A process from the Windows perspective is a kernel object that characterizes a certain address space within which threads execute. As with any other kernel object, a process has a reference count indicating how many open handles there are to itself. A process is created via a call to the CreateProcess() function, and remains in existence until all open handles to the process have been closed, at which point the reference count for the process object is zero, and there are no more active threads in the process.

The GetCurrentProcess() function returns a pseudo handle to the current process. A handle is typically an integer value, although technically speaking we ought to consider a handle as opaque, meaning that we only really need to focus on how to us it, not what it is, as such. A pseudo handle is a special integral value, which again we should consider opaque, which is why we acquire it via the GetCurrentProcess() function, rather than hard-coding it as a constant value.

Once we have a handle to the process, there are a number of functions we can call to provide us with information about the process. The GetProcessId() function takes the handle to the process and returns the ID of the process.

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

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

    hCurrProc = GetCurrentProcess();

    //pseudo handle will be (HANDLE)-1
    _tprintf(_T("Handle literal value %d\n"), (int)hCurrProc);

    //process ID
    _tprintf(_T("Process ID of current process %d\n"), GetCurrentProcessId());
    //returns the same data, but this call can be used with any process 
    _tprintf(_T("Ditto %d\n"), GetProcessId(hCurrProc));


    return 0;
}

An IO_COUNTERS structure is used to store I/O information for a process or a job object. The win32 GetProcessIoCounters() function enables us to get a pointer to an IO_COUNTERS structure that contains the I/O accounting information for the process.

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

int _tmain(int argc, _TCHAR* argv[])
{
    IO_COUNTERS structIOCount;
    HANDLE hCurrProcess = GetCurrentProcess();
    //second argument needs to be the address of an IO_COUNTERS
    //structure
    if (GetProcessIoCounters(hCurrProcess, &structIOCount)){
        _tprintf(_T("Got I/O Information for Process %d\n"), GetProcessId(hCurrProcess));
        _tprintf(_T("The number of read operations performed: %d\n"), structIOCount.ReadOperationCount);
        _tprintf(_T("The number of write operations performed: %d\n"), structIOCount.WriteOperationCount);
        _tprintf(_T("The number of bytes read: %d\n"), structIOCount.ReadTransferCount);
        _tprintf(_T("THe number of bytes written: %d\n"), structIOCount.WriteTransferCount);
    }


    return 0;
}

The CreateProcess() function returns a handle to a new process object that will manage the lifetime of the indicated running program. The CreateProcess() function takes ten arguments

The first parameter for CreateProcess() is a pointer to the null-terminated name of the program to run; if this parameter is NULL, the program indicated in the second argument will be executed. This second argument is likewise a null-terminated string; it indicates the program to execute, along with any command-line arguments. The third argument is a pointer to a SECURITY_ATTRIBUTES structure; if this argument is NULL, the default security attributes are used, and the process handle returned by the function is not inheritable. The fourth argument is likewise a pointer to a SECURITY_ATTRIBUTES structure; if NULL, the default attributes are used, and the thread handle is not inheritable. The fifth argument is a Boolean value indicating whether or not the new process should inherit the inheritable handles of the calling process. The sixth argument is a DWORD value that indicates the creation flags, which can be OR-ed together to form combinations. This argument can also be used to control the priority of the new process. The seventh argument is a pointer to an environment block for the new process. If we put NULL here than the process will be passed a copy of the current environment. The eight argument is a string that specifies the initial default drive and directory of the process. The ninth argument is a pointer to a STARTUPINFO structure, and the tenth argument is a pointer to a PROCESS_INFORMATION structure that will be filled in with details about the newly created process.

#include <Windows.h>

int _tmain(int argc, _TCHAR* argv[])
{
    STARTUPINFO structSI;
    PROCESS_INFORMATION structPI;
    BOOL bRValue;
    ZeroMemory(&structSI, sizeof(structSI));
    //important!
    structSI.cb = sizeof(STARTUPINFO);

    bRValue = CreateProcess(
        NULL, //lpApplicationName
        "notepad.exe", //lpCommandLine
        NULL, //lpProcessAttributes
        NULL, //lpThreadAttributes
        TRUE, //bInheritHandles
        0, //dwCreationFlags
        NULL, //lpEnvironment
        NULL, //lpCurrentDirectory
        &structSI, //lpStartupInfo
        &structPI //lpProcessInformation
        );

    if (bRValue){
        printf("Process created.\n");
    }

    return 0;
}

The PROCESS_INFORMATION structure has four members. The hProcess member is the handle to the newly created process. The calling program should use the CloseHandle() function to release its reference to the newly created process kernel object. The hThread member is the handle to the primary thread of the new process. The dwProcessId member stores the system-wide unique identifier to the new process. The dwThreadId member stores the system-wide unique identifier to the primary thread of the new process.

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

int _tmain(int argc, _TCHAR* argv[])
{
    STARTUPINFO structSI;
    PROCESS_INFORMATION structPI;

    ZeroMemory(&structSI, sizeof(structSI));
    structSI.cb = sizeof(structSI);

    //createprocess returns Boolean value
    if (CreateProcess(NULL, "notepad.exe", NULL, NULL, FALSE,
        0, NULL, NULL, &structSI, &structPI)){
        //display the information stored in structPI
        printf("Notepad.exe created as process %d and thread %d\n",
            structPI.dwProcessId, structPI.dwThreadId);
    }

    //let's close the newly created process and thread handles
    CloseHandle(structPI.hProcess);
    CloseHandle(structPI.hThread);

}

Each process has a handle and a system-wide unique identifier. From the calling program, we can access these values via the PROCESS_INFORMATION data structure. From within the process itself, an application can determine its own pseudo-handle via the GetCurrentProcess() call. Note that a pseudo-handle is only valid in the context of the calling process, as it is not a real handle.

The TerminateProcess() function can be used to terminate another process. Generally speaking, this function should not be called unless in an emergency.

Any process can get the exit status of a process it has a handle to by using the GetExitCodeProcess() function. The GetExitCodeProcess() function returns a Boolean value and accepts two parameters, the first being the handle to the process and the second being a pointer to a DWORD value to store the exit code.

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

int _tmain(int argc, _TCHAR* argv[])
{
    STARTUPINFO structSInfo;
    PROCESS_INFORMATION structPInfo;
    DWORD dwExitCode;
    BOOLEAN boolRValue;

    ZeroMemory(&structSInfo, sizeof(structSInfo));

    //start notepad
    boolRValue = CreateProcess(
        NULL, //application name
        "notepad.exe", //command line
        NULL, //Inherit security attributes
        NULL, //process attributes
        TRUE, //inherit handles
        0, //no creation flags
        NULL, //environment
        NULL, //current directory
        &structSInfo,
        &structPInfo
        );

    if (boolRValue){
        printf("Notepad.exe created as process %d, thread %d.\n", structPInfo.dwProcessId, structPInfo.dwThreadId);
    }
    else {
        printf("Error creating new process.");
        exit(EXIT_FAILURE);
    }

    //wait for a few seconds
    Sleep(3000);

    //terminate notepad.exe
    TerminateProcess(structPInfo.hProcess, 0);

    //get exit code
    boolRValue = GetExitCodeProcess(structPInfo.hProcess, &dwExitCode);

    if (boolRValue){
        printf("notepad.exe's exit code is %d\n", dwExitCode);
    }

    CloseHandle(structPInfo.hProcess);
    CloseHandle(structPInfo.hThread);

    return 0;

}

One last item to cover is waiting for a process to exit – this is the simplest, and perhaps most limiting, way to synchronize processes. To wait for a process to exit, we simply call  WaitForSingleObject(). The WaitForSingleObject() function takes two arguments, the first is the handle to the process, and the second specifies how long we want the process to wait for the other process to end.

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

int _tmain(int argc, _TCHAR* argv[])
{
    STARTUPINFO structSI;
    PROCESS_INFORMATION structPI;
    DWORD dwWaitRValue, dwExitCode;

    ZeroMemory(&structSI, sizeof(structSI));
    structSI.cb = sizeof(structSI);

    CreateProcess(
        NULL,
        "powershell.exe",
        NULL,
        NULL,
        FALSE,
        CREATE_NEW_CONSOLE, //creation flag
        NULL,
        NULL,
        &structSI,
        &structPI
        );

    //print some information
    printf("Powershell.exe was created as process ID %d.\n", structPI.dwProcessId);

    //wait for process to exit
    while (true){
        dwWaitRValue = WaitForSingleObject(structPI.hProcess, 1000);
        if (dwWaitRValue == WAIT_OBJECT_0){
            printf("\nProcess ID %d exited.\n", structPI.dwProcessId);
            break;
        }
        if (dwWaitRValue == WAIT_TIMEOUT){
            printf("Powershell.exe (Process %d) has not exited yet.\n", structPI.dwProcessId);
        }
    }

    //get exit code
    if (GetExitCodeProcess(structPI.hProcess, &dwExitCode)){
        printf("Powershell.exe exited with code %d.\n", dwExitCode);
    }

    CloseHandle(structPI.hProcess);
    CloseHandle(structPI.hThread);

    return 0;

}

This covers most of what we need to know about the basics of creating and managing processes in Windows.

Creating Processes using win32

The CreateProcess() function is a real beast of a function as it takes no less than 10 parameters. Let’s tackle this function by first taking a look at some of the parameters, which are complicated in and of themselves.

The STARTUPINFO structure contains information which is used to control how the process behaves and appears on startup. The STARTUPINFO structure has no less than 18 fields.

The GetStartupInfo() function is used to fetch the contents of the STARTUPINFO structure that was specified when the process was created. It’s only parameter is a pointer to a STARTUPINFO structure.The GetStartupInfo() function does not return a value.

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

int _tmain(int argc, _TCHAR* argv[])
{
	STARTUPINFO structSInfo;
	structSInfo.cb = sizeof(STARTUPINFO);

	GetStartupInfo(&structSInfo);

	_tprintf(_T("Desktop: %s\n"), structSInfo.lpDesktop);
	_tprintf(_T("Console: %s\n"), structSInfo.lpTitle);

	return 0;
}

The creation flags field dwFlags of the STARTUPINFO structure can take a number of values.

The STARTF_USECOUNTCHARS value specifies that the dwXCountChars and dwYCountChars members contain additional information. These screen buffer width and height values are used to determine the size of the console window.

The STARTF_USEFILLATTRIBUTE value specifies that the dwFillAttribute member contains additional information. The dwFillAttribute member is used to set the console fill attributes.

The STARTF_USEPOSITION value indicates that the dwX and dwY members hold additional information. The dwX and dwY members set the window position.

The STARTF_USESTDHANDLES value causes the process handles to be set to the values specified in the hStdInput, hStdOutput, and hStdError members.

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

int _tmain(int argc, _TCHAR* argv[])
{
	//use the value of the STARTUPINFO wShowWindow member
	printf("STARTF_USESHOWWINDOW = %d\n", STARTF_USESHOWWINDOW);
	//use the dwX and dwY STARTUPINFO members
	printf("STARTF_USEPOSITION = %d\n", STARTF_USEPOSITION);
	//use the dwFillAttribute member of STARTUPINFO
	printf("STARTF_USEFILLATTRIBUTE = %d\n", STARTF_USEFILLATTRIBUTE);
	//use the hStdInput, hStdOutput, and hStdError members of STARTUPINFO
	printf("STARTF_USESTDHANDLES = %d\n", STARTF_USESTDHANDLES);

	return 0;
}

The CreateProcess() function itself has a dwFlags parameter,  dwCreationFlags, which should not be confused with the dwFlags field of the STARTUPINFO structure The dwFlags parameter is the sixth parameter of the CreateProcess() function. It is used to specify flags which control the creation of the process, and its priority.

The CREATE_DEFAULT_ERROR_MODE process creation flag indicates that the new process does not inherit the error mode of the calling process. Instead, the new process gets the default error mode.

The CREATE_NEW_CONSOLE process creation flag indicates that the new process has a new console, instead of inheriting the parent’s console, which is the default.

The CREATE_NEW_PROCESS_GROUP process creation flag indicates that the new process is the root process of a new process group. The process group is made up of all processes that are descendants of this process.

The CREATE_SUSPENDED process creation flag indicates that the new process is created in a suspended state, and will not run until the ResumeThread() function is called.

The DETACHED_PROCESS process creation flag indicates that the new process does not inherit its parent’s console, as per the default. Note that this flag cannot be used with the CREATE_NEW_CONSOLE flag.

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

int main(void){
	
	//creates a new console for the process
	printf("CREATE_NEW_CONSOLE %d\n", CREATE_NEW_CONSOLE);
	
	//make the process the root of a new process group
	printf("CREATE_NEW_PROCESS_GROUP %d\n", CREATE_NEW_PROCESS_GROUP);

	//primary thread of new process is created in a suspended state
	printf("CREATE_SUSPENDED %d\n", CREATE_SUSPENDED);

	//prevents the new process from accessing the console
	//belonging to the parent process
	printf("DETACHED_PROCESS %d\n", DETACHED_PROCESS);

	return 0;

}

The dwCreationFlags parameter can also hold priority flags. The four main priority flags are REALTIME_PRIORITY_CLASS, HIGH_PRIORITY_CLASS, NORMAL_PRIORITY_CLASS, and IDLE_PRIORITY_CLASS.

We can find out the process priority for the current process using the GetPriorityClass() function, which takes a single parameter, the HANDLE to the current process. We can acquire a HANDLE to the current process using the GetProcessHandle() function.

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

int main(void){
	
	DWORD dwProcessPriority;

	//GetPriorityClass() 
	//returns 0 if it fails
	dwProcessPriority = GetPriorityClass(GetCurrentProcess());

	if(dwProcessPriority > 0){
		if(dwProcessPriority & REALTIME_PRIORITY_CLASS){
			printf("Highest priority process.\n");
		}
		if(dwProcessPriority & HIGH_PRIORITY_CLASS){
			printf("High priority process.\n");
		}
		if(dwProcessPriority & NORMAL_PRIORITY_CLASS){
			printf("Normal priority process.\n");
		}
		if(dwProcessPriority & IDLE_PRIORITY_CLASS){
			printf("Background process.\n");
		}
	}

	return 0;

}

We can change the priority for a process using the SetPriorityClass() function. The SetPriorityClass() function takes two parameters, the first being a HANDLE to the process, and the second being a DWORD value indicating the desired priority.

#include <windows.h>
#include <stdio.h>

int main(void){
	
	HANDLE hCurrentProc = GetCurrentProcess();

	if(SetPriorityClass(hCurrentProc, REALTIME_PRIORITY_CLASS)){
		printf("Process set to realtime priority.\n");
	} else {
		printf("Process not set to realtime priority.\n");
	}

	if(SetPriorityClass(hCurrentProc, HIGH_PRIORITY_CLASS)){
		printf("Process set to high priority.\n");
 	} else {
		printf("Process not set to high priority.\n");
	}

	return 0;

}

The win32 PROCESS_INFORMATION structure contains information about a newly created process and its primary thread. It has four members. The first member is a HANDLE to the newly created process. The second member is a HANDLE to the primary thread of the newly created process. Both of these handles need to be closed when we are through with them.

We will pass the PROCESS_INFORMATION structure to the CreateProcess() function by reference, which is to say via pointer.

#include <windows.h>
#include <stdio.h>

int main(void){
	
	STARTUPINFO structSI;
	PROCESS_INFORMATION structPI;
	BOOL bRetVal;

	//get startup info for current process
	GetStartupInfo(&structSI);

	//create child process
	bRetVal = CreateProcess(
		0, //executable module name
		"notepad.exe", //command line
		0, //process security attributes
		0, //thread security attributes
		FALSE, //inheritance flag
		CREATE_NEW_CONSOLE, //creation flags
		NULL, //pointer to environment block
		NULL, //current directory name
		&structSI, //pointer to startup info struct
		&structPI //pointer to process info struct
		);

	if(bRetVal==FALSE){
		printf("Error creating new process.\n");
		exit(EXIT_FAILURE);
	} else {
		printf("Process created.\n");
	}

	//close handles to new process
	CloseHandle(structPI.hProcess);
	CloseHandle(structPI.hThread);


	return 0;

}

Creating Processes Using win32

When Windows creates a process it calls the CreateProcess() API. The CreateProcess() function does the work of instantiating a process object in the object manager subsystem.

The CreateProcess() call creates a new process and its primary thread. The function has a whopping 10 parameters, although many of them accept NULL for arguments.

The first two arguments to CreateProcess() are pointers to strings, lpApplicationName and lpCommandLine.The first argument can be NULL, as long as we specify the executable we wish to run in the second argument, lpCommandLine.

The third and fourth arguments are both pointers to SECURITY_ATTRIBUTES structures. We will pass in NULL for these parameters. The fifth argument is a Boolean value that indicates whether the new process inherits handles from the calling process. We will put FALSE in for this parameter.

The sixth argument, dwCreationFlags, specifies additional flags that control the priority class and the creation of the process. For now, let’s put in 0, indicating no flags.

We will pass NULL for the seventh and eighth arguments. The seventh argument is a pointer to the environment block for the new process. For this parameter we will pass NULL, which makes the new process inherit the environment of the calling process. The eighth argument is supposed to be the full path to the current directory for the process we are creating. We will use NULL for this parameter, which means the process will have the same current directory as the calling process.

The final two parameters are important. The ninth parameter is a pointer to a STARTUPINFO structure. The STARTUPINFO structure has a member, cb, that is a DWORD that indicates the size of the structure in bytes. The final parameter is a pointer to a PROCESS_INFORMATION structure that stores information about the new process.

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

int _tmain(void)
{
	STARTUPINFO structSi;
	PROCESS_INFORMATION structPi;

	memset(&structSi, 0, sizeof(structSi));
	structSi.cb = sizeof(structSi);
	memset(&structPi, 0, sizeof(structPi));

	if(!CreateProcess(
		NULL,
		"notepad.exe",
		NULL, //process handle (not inheritable)
		NULL,//thread handle (not inheritable)
		FALSE,//handle inheritance set to FALSE
		0, // no creation flags
		NULL, //use parent's environment block
		NULL, //use parent's starting directory
		&structSi,
		&structPi)
		){
			printf("Problem creating new process.\n");
			return 1;
	}

	return 0;
}

Remember, the final parameter to CreateProcess() is the return buffer for handles and IDs of the new process object and its main thread.

The dwCreationFlags parameter, the sixth parameter, indicates what behavior the system should give to the new process. An often-used flag is CREATE_SUSPENDED, which tells the main thread in the new process to suspend immediately. Another frequently used flag is CREATE_NEW_CONSOLE, which tells the new process to start its own console window rather than use the parent’s.

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

int _tmain(void)
{
	STARTUPINFO structStartInfo;
	ZeroMemory(&structStartInfo, sizeof(structStartInfo));
	structStartInfo.cb = sizeof(structStartInfo);

	PROCESS_INFORMATION structProcInfo;
	ZeroMemory(&structProcInfo, sizeof(structProcInfo));

	BOOL bOK;

	bOK = CreateProcess(
		NULL,
		"cmd.exe",
		NULL, //default security for process
		NULL, //default security for thread
		FALSE, //don't inherit the handle,
		CREATE_NEW_CONSOLE, //create new console
		NULL, //new environment
		NULL, //current directory
		&structStartInfo,
		&structProcInfo);

	if(bOK){
		printf("Process created successfully.\n");
		printf("Process has ID %u.\n", structProcInfo.dwProcessId);
	}

	return 0;
}

Note that creating a new process object also creates its main thread.

The WaitForSingleObject() function takes two arguments. The first argument is the handle of an object to wait for. The second argument is the time-out interval in milliseconds; we can pass INFINITE as the second argument, so that the function’s time-out interval never lapses. The WaitForSingleObject() function returns when either the specified object is in the signaled state, or the time out interval elapses. A process object’s state is signaled when the process terminates.

#include 
#include 

int _tmain(void)
{
	STARTUPINFO structSInfo;
	PROCESS_INFORMATION structPInfo;
	HANDLE hProcess;

	memset(&structSInfo, 0, sizeof(structSInfo));
	structSInfo.cb = sizeof(structSInfo);

	memset(&structPInfo, 0, sizeof(structPInfo));

	if(!CreateProcess(NULL, "mspaint.exe", NULL, NULL, FALSE, 0, NULL, NULL, &structSInfo, &structPInfo)){
		printf("Process failed.\n");
		return 1;
	} else {
		printf("Process created with PID %u.\n", structPInfo.dwProcessId);
	}

	if(WaitForSingleObject(structPInfo.hProcess, INFINITE)==STATUS_WAIT_0){
		printf("Process %u ended.\n", structPInfo.dwProcessId);
	}
	
	return 0;
}

Note that a process ID is not the same thing as a process handle. We cannot directly manipulate a process from outside the process unless we have the handle.

Introduction to win32 Processes

The OS sees applications that are currently running on the system as process objects. We can interact with process objects via a HANDLE, a system-supplied unique number. Any process running in the system can call GetCurrentProcess() to get a handle to identify itself.

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


int _tmain(void)
{

	HANDLE thisProcess = GetCurrentProcess();
	
	//GetProcessId() takes the HANDLE to a process
	//as its argument
	DWORD processID = GetProcessId(thisProcess);

	printf("The process ID for this process is %u\n", processID);

	return 0;
}

The process handle is often used as a parameter in API calls. We will look at two of these calls, the GetPriorityClass() call, and the GetProcessIoCounters() call. The GetPriorityClass() call takes only one argument, the HANDLE for the process. The GetProcessIoCounters() function takes two arguments. The first argument is the file handle, and the second argument is a pointer to an IO_COUNTERS structure, which holds the return data. The GetProcessIoCounters() call is used to get accounting information for all I/O operations performed by the specified process.

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

void printPriority(DWORD dwPriority);
void printProcessIOCount(const IO_COUNTERS *structIOCounters);

int _tmain(void)
{

	HANDLE hProcess = GetCurrentProcess();

	DWORD dwPID;
	DWORD dwPriority;
	IO_COUNTERS structIOCounters;

	dwPID = GetProcessId(hProcess);
	dwPriority = GetPriorityClass(hProcess);
	GetProcessIoCounters(hProcess, &structIOCounters);

	//present the retrieved data
	printf("Process No. %u\n", dwPID);
	printPriority(dwPriority);
	printProcessIOCount(&structIOCounters);

	return 0;
}//end _tmain()

//display the I/O statistics of the process
void printProcessIOCount(const IO_COUNTERS * structIOCounters){
	printf("\n%u read operations performed,", structIOCounters->ReadOperationCount);
	printf(" %u bytes read.\n", structIOCounters->ReadTransferCount);
	printf("%u write operations performed, ", structIOCounters->WriteOperationCount);
	printf("%u bytes written.\n", structIOCounters->WriteTransferCount);


}//end printProcessIOCount()

//display the priority of the process
void printPriority(DWORD dwPriority)
{
	switch(dwPriority)
	{
		case HIGH_PRIORITY_CLASS:
			printf("High");
			break;
		case NORMAL_PRIORITY_CLASS:
			printf("Normal");
			break;
		case IDLE_PRIORITY_CLASS:
			printf("Idle");
			break;
		case REALTIME_PRIORITY_CLASS:
			printf("Realtime");
			break;
		default:
			printf("Unknown");
			break;
	} //end switch(dwPriority)

	printf(" priority.");
} //end printPriority()

The CreateProcess() function creates a new process. The CreateProcess() function takes ten arguments. The first argument is of type LPCTSTR and indicates the name of the program we wish to run.

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

int _tmain(void)
{
	LPCTSTR prgm = "notepad.exe";
	LPCTSTR prgm2 = "mspaint.exe";

	printf("%s\n", prgm);
	printf("%s\n", prgm2);

	return 0;
}

The name for the first argument is lpFileName. Note that this argument must include the file extension of the program we wish to run. This argument is optional, which means we can specify NULL for it.

We will look more at CreateProcess() at a later date.