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/

Review of Console I/O with win32 functions

The SetConsoleMode() function can be used to set the input and output modes of the console’s input and output buffers. The function takes two arguments; the first argument is the handle to the console, which can be acquired via the GetStdHandle() function.

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


int _tmain(int argc, _TCHAR* argv[])
{
	HANDLE hInput = GetStdHandle(STD_INPUT_HANDLE);
	HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);

	//check to see we got the input handle
	if(hInput == INVALID_HANDLE_VALUE){
		printf("Error getting input handle.\n");
		exit(EXIT_FAILURE);
	} else {
		printf("Input handle acquired.\n");
	}

	//check to see we got the output handle
	if(hOutput == INVALID_HANDLE_VALUE){
		printf("Error getting output handle.\n");
		exit(EXIT_FAILURE);
	} else {
		printf("Output handle acquired.\n");
	}

	return 0;
}

The second argument to the SetConsoleMode() function is a DWORD value that specifies how the characters are processed. There are five commonly used flags that are in fact enabled by default; ENABLE_LINE_INPUT, which specifies that the ReadConsole() function returns when it encounters a carriage return characters; ENABLE_ECHO_INPUT, which echoes characters to the screen as they are read; ENABLE_PROCESSED_INPUT, which processes backspace, carriage return, and line feed characters; ENABLE_PROCESSED_OUTPUT, which processes backspace, carriage return, and line feed characters; and ENABLE_WRAP_AT_EOL_OUTPUT, which enables line wrap for both normal and echoed output.

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

int _tmain(int argc, _TCHAR* argv[])
{
	printf("ENABLE_LINE_INPUT %u\n", ENABLE_LINE_INPUT);
	printf("ENABLE_ECHO_INPUT %u\n", ENABLE_ECHO_INPUT);
	printf("ENABLE_PROCESSED_INPUT %u\n", ENABLE_PROCESSED_INPUT);
	printf("ENABLE_PROCESSED_OUTPUT %u\n", ENABLE_PROCESSED_OUTPUT);
	printf("ENABLE_WRAP_AT_EOL_OUTPUT %u\n", ENABLE_WRAP_AT_EOL_OUTPUT);

	return 0;
}

The SetConsoleMode() function returns True if the function succeeds and False, otherwise.

It is possible to perform console I/O using ReadFile() and WriteFile(), but it is somewhat easier just to use the console I/O functions ReadConsole() and WriteConsole(). The ReadConsole() and WriteConsole() functions process TCHAR values, rather than bytes, and operates according to the console mode.

The ReadConsole() function has five parameters. While the fifth parameter can be set to NULL, the rest must be filled out. The first parameter is a handle to the console input buffer that has GENERIC_READ access. The second parameter is a pointer to a buffer that stores the data read from the console input buffer. The third argument is the number of characters to be read. The fourth argument is a pointer to a DWORD value that stores the number of characters actually read. The ReadConsole() function returns True if the read action succeeds.

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

int _tmain(int argc, _TCHAR* argv[])
{
	const int Buffer_Length = 1024;
	TCHAR szBuffer[Buffer_Length];
	DWORD dwLength, dwRead;
	HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE);
	
	if(hIn==INVALID_HANDLE_VALUE){
		printf("Invalid handle value.\n");
		exit(EXIT_FAILURE);
	}

	//get length of buffer
	dwLength = _tcslen(szBuffer);

	printf("Please enter the text: ");

	if(ReadConsole(hIn, szBuffer, dwLength, &dwRead, NULL)){
		printf("Read from console.\n");
		printf("%d characters read (includes carriage return / line feed).\n", dwRead);
	} else {
		printf("Failed to read from console.\n");
		exit(EXIT_FAILURE);
	}

        CloseHandle(hIn);

	return 0;
}

Please note that a process can only have one console at a time.

The WriteConsole() function is essentially the same as the ReadConsole() function. The first parameter passed to WriteConsole() is a handle to the console screen buffer. The second parameter is the buffer storing the TCHAR values we wish to write to the console. The third parameter indicates the number of characters we want to write. The fourth parameter is a pointer to a DWORD value that stores the number of characters actually written. We will again pass NULL for the fifth parameter.

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

int _tmain(int argc, _TCHAR* argv[])
{
	DWORD dwCount, dwMsgLen;
	HANDLE hIn, hOut;
	BOOL bSuccess;
	TCHAR szBuffer[2056], szWriteBuffer[2112];

	hIn = GetStdHandle(STD_INPUT_HANDLE);
	hOut = GetStdHandle(STD_OUTPUT_HANDLE);

	if(hIn == INVALID_HANDLE_VALUE || hOut == INVALID_HANDLE_VALUE){
		printf("Error getting the handles to the console.\n");
		exit(EXIT_FAILURE);
	}

	//read the text
	printf("Please enter some text: ");

	bSuccess = ReadConsole(hIn, szBuffer, _tcslen(szBuffer), &dwCount, NULL);

	if(!bSuccess){
		printf("Error reading from the console.\n");
		exit(EXIT_FAILURE);
	}

	
	dwMsgLen = dwCount - 2;
	

	szBuffer[dwMsgLen] = '';

	//TCHAR mapping to sprintf()
	swprintf_s(szWriteBuffer, _T("You wrote: '%s' [%d TCHAR written.]\n"), szBuffer, dwMsgLen);

	WriteConsole(hOut, szWriteBuffer, _tcslen(szWriteBuffer), &dwCount, NULL);

	//close the handles!
	CloseHandle(hIn);
	CloseHandle(hOut);

	return 0;
}

If you have any interest in learning standard C, take a look at my book http://www.amazon.com/Big-Als-C-Standard-ebook/dp/B00A4JGE0M/

Review of File Handling using win32

The CreateFile() function opens a file for read and/or write access. The CreateFile() function also opens mail slots, named pipes, communication ports, etc. The function returns a HANDLE to the desired file. The CreateFile() function takes seven arguments, which we have looked at in detail in earlier posts.

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

int main(void){
	
	HANDLE newFile;

	newFile = CreateFile(
		"newfile.txt",
		GENERIC_READ | GENERIC_WRITE,
		0, //no sharing
		0, //inherit security
		CREATE_ALWAYS,
		0, //file flags
		0
		);

	if(newFile!=INVALID_HANDLE_VALUE){
		printf("New file created.\n");
 	} else {
		printf("New file not created.\n");
	}

	return 0;

}

Note that the CreateFile() function returns a handle on success, or INVALID_HANDLE_VALUE.

The CloseHandle() function closes file handles. Generally speaking, it’s a good idea to explicitly close all file handles. The CloseHandle() function returns a Boolean value indicated whether or not the file handle was closed properly.

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

int main(void){
	
	HANDLE hFile = CreateFile(
		"textfile.txt",
		GENERIC_WRITE,
		FILE_SHARE_READ,
		0,//inherit security
		CREATE_NEW, //create only if no previous file
		0,
		0
		);

	if(hFile != INVALID_HANDLE_VALUE){
		printf("File created successfully.\n");
 	} else {
		printf("File not created successfully.\n");
		exit(EXIT_FAILURE);
 	}

	if(CloseHandle(hFile)){
		printf("File handle closed.\n");
	} else {
		printf("File handle not closed.\n");
	}
 
	return 0;

}

The WriteFile() function takes five arguments. The first argument is the handle to a file that has already been opened. The second argument is the buffer to be written to the file. The third argument is the number of bytes to write; typically this is the size of the buffer, in bytes, not characters. The fourth argument is a pointer to a DWORD variable that will store the number of bytes

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

int main(void){
	
	char *szFilename = "textfile.txt";
	char *szSpaceGhost = "Are you getting enough oxygen?";
	DWORD dwBytesWritten;
	HANDLE hWriteFile;
	BOOL bValue;

	hWriteFile = CreateFile(
		szFilename,
		GENERIC_WRITE,
		0,
		0,
		CREATE_ALWAYS,
		0,
		0
		);

	if(hWriteFile!=INVALID_HANDLE_VALUE){
		printf("File handle opened for %s\n", szFilename);
	} else {
		printf("Error opening file %s\n", szFilename);
		printf("Error %d\n", GetLastError());
	}

	//WriteFile() function returns a Boolean value
	bValue = WriteFile(hWriteFile, szFilename, strlen(szFilename), &dwBytesWritten, NULL);

	if(bValue){
		printf("File %s written to successfully.\n", szFilename);
	}

	if(CloseHandle(hWriteFile)){
		printf("File handle closed.\n");
	}

	return 0;

}

The ReadFile() function is a block-reading function. A buffer is passed to ReadFile() as well as the number of bytes to be read, and the function retrieves that number of bytes from the file starting at the current offset. As with WriteFile(), the first argument is a handle to an open file.

#include 
#include 

int main(void){
	
	char *szFilename = "textfile.txt";
	char *szWriteBuffer = "That is mahogany!";
	char szReadBuffer[2056];
	DWORD dwBytesRead, dwBytesWritten;

	HANDLE hFile = CreateFile(
		szFilename,
		GENERIC_WRITE,
		0,
		0,
		OPEN_EXISTING,
		0,
		0
		);

	if(hFile==INVALID_HANDLE_VALUE){
		printf("Error accessing file for writing.\n");
		exit(EXIT_FAILURE);
	}

	if(WriteFile(hFile, szWriteBuffer, strlen(szWriteBuffer) * sizeof(char), &dwBytesWritten,NULL)){
		printf("File %s written to, %d bytes.\n", szFilename, dwBytesWritten);
	}

	//close the handle
	CloseHandle(hFile);

	hFile = CreateFile(
		szFilename,
		GENERIC_READ,
		0,
		0,
		OPEN_EXISTING,
		0,
		0
		);

	if(hFile==INVALID_HANDLE_VALUE){
		printf("Error accessing file for reading.\n");
		exit(EXIT_FAILURE);
	}

	if(ReadFile(hFile, szReadBuffer, sizeof(szReadBuffer), &dwBytesRead, NULL)){
		printf("File %s read from, %d bytes.\n", szFilename, dwBytesRead);
	} else {
		printf("Error reading form file.\n");
		exit(EXIT_FAILURE);
	}

	//append termination character to szReadBuffer;
	//using dwBytesRead value set in the ReadFile() function
	szReadBuffer[dwBytesRead] = '';

	printf("%s\n", szReadBuffer);

	return 0;

}

We will cover accessing file metadata and review copying and moving files at a later date. If you get the chance, take a look at my book on C at http://www.amazon.com/Big-Als-C-Standard-ebook/dp/B00A4JGE0M/.

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;

}

Process Information via win32

The GetCommandLine() function can be used to retrieve the command line that was used to start the program. The GetCommandLine() function returns a pointer to the command line string that started the process.

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

int _tmain(int argc, _TCHAR* argv[])
{
	_TCHAR *szCommandLine = GetCommandLine();

	_tprintf(_T("The command line is: %s\n"), szCommandLine);

	return 0;
}

Note that _TCHAR is the official version of TCHAR; in practice, they both map to the same thing.

The environment strings of the process can be manipulated using four different functions.

The GetEnvironmentStrings() function returns a pointer to the block of environment strings. The return value points to a block of null-terminated strings, each of which follows the pattern key=value. The final string is followed by an extra null.

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

int _tmain(int argc, _TCHAR* argv[])
{
	TCHAR *szEnvStrings = GetEnvironmentStrings();
	int iLen; 
	do{
		_tprintf(_T("%s\n"), szEnvStrings);
		iLen = _tcslen(szEnvStrings);
		//move down the block by the number of 
		//TCHARs in the string, plus one 
		//for the null character
		szEnvStrings+=(iLen+1);
	}while (*szEnvStrings != '');

	return 0;
}

In order to modify individual environment variables, the functions GetEnvironmentVariable() and SetEnvironmentVariable() are available.

The GetEnvironmentVariable() function takes three parameters. The first parameter holds the name, and the second parameter is a pointer to a buffer to hold the value. The third parameter gives the size of the buffer. The GetEnvironmentVariable() function returns 0 if the variable is not found; otherwise, it returns the number of characters written to the buffer.

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

int _tmain(int argc, _TCHAR* argv[])
{
	const int Buffer_Length = 256;

	TCHAR szBuffer[Buffer_Length];

	TCHAR *szEnvVar[] = {_T("USERPROFILE"), _T("COMPUTERNAME"), _T("TEMP")};
	
	if(GetEnvironmentVariable(*szEnvVar, szBuffer, Buffer_Length)){
		_tprintf(_T("Environment variable %s retrieved.\n"), *szEnvVar);
		_tprintf(_T("%s = %s\n"), *szEnvVar, szBuffer);
	}

	if(GetEnvironmentVariable(*(szEnvVar+1), szBuffer, Buffer_Length)==0){
		_tprintf(_T("Error getting variable %s.\n"), *(szEnvVar+1));
	} else {
		_tprintf(_T("Environment variable %s retrieved successfully.\n"), *(szEnvVar+1));
		_tprintf(_T("%s = %s\n"), *(szEnvVar+1), szBuffer);
	}

	if(GetEnvironmentVariable(szEnvVar[2], szBuffer, Buffer_Length)>0){
		_tprintf(_T("Environment variable %s retrieved successfully.\n"), szEnvVar[2]);
		_tprintf(_T("%s = %s\n"), szEnvVar[2], szBuffer);
	}

	return 0;
}

The SetEnvironmentVariable() function is used to change the value of an environment variable or create a new one. The SetEnvironmentVariable() function returns a Boolean value of True if the function call is successful, and False if it is not.

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

int _tmain(int argc, _TCHAR* argv[])
{
	const int Buffer_Size = 256;
	const TCHAR *szCPEnvVar = _T("ALVAL");

	TCHAR szBuffer[Buffer_Size];

	if(SetEnvironmentVariable(szCPEnvVar, _T("42"))){
		_tprintf(_T("%s set.\n"), szCPEnvVar);
	}

	if(GetEnvironmentVariable(szCPEnvVar, szBuffer, Buffer_Size)>0){
		_tprintf(_T("%s = %s\n"), szCPEnvVar, szBuffer);
	}

	return 0;
}