Working with Temporary Files Using win32

To retrieve the designated directory for temporary files, we can use the GetTempPath() function. The function takes two arguments, the first argument indicates the size of the buffer to store the path in TCHARs, and the second argument is the buffer to store the path itself.

The GetTempFileName() function generates a unique file name, with the .tmp suffix, in a specified directory, and optionally will create the file. The function takes four arguments. The first argument is the directory for the temporary file. We can use the GetTempPath() function to fetch the path to to the directory for temporary files, or we can use the current directory by entering a period here. The second argument is the prefix of the temporary name; it should be three characters or less. The third argument is usually set to zero. If the value is not zero, the file will not be created. The final argument is the buffer that receives the temporary file name. The length of this buffer should at the least be set to MAX_PATH.

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

int main()
{
 TCHAR tchTmpPath[MAX_PATH];
 TCHAR tchTmpBuffer[MAX_PATH];

 //GetTempPath() returns nonzero on success
 //size is in TCHARs
 if (GetTempPath(MAX_PATH, tchTmpPath) == 0) {
 printf("Error getting temporary file directory.\n");
 exit(EXIT_FAILURE);
 }

 //returns 0 on failure
 if (GetTempFileName(tchTmpPath, _T("TMP"), 0, tchTmpBuffer) == 0) {
 printf("Error getting temporary file name.\n");
 exit(EXIT_FAILURE);
 }

 _tprintf(_T("Temporary file created: %s\n"), tchTmpBuffer);

 return 0;
}

For our next example, we will implement a variant of the *nix touch command that updates the file access and modification time of all the files in the directory to the current system time. We will use the FindFirstFile() and FindNextFile() functions to list all the files in the current working directory.

The GetSystemTimeAsFileTime() function retrieves the current system date and time and stores it in a FILETIME structure.

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

int UpdateFileTime(const TCHAR *tchFileNamePtr);

int main()
{
	WIN32_FIND_DATA structWIN32Data;
	HANDLE hSearchFile;

	hSearchFile = FindFirstFile(_T("*"), &structWIN32Data);

	if (hSearchFile == INVALID_HANDLE_VALUE) {
		printf("Error getting search handle.\n");
		exit(EXIT_FAILURE);
	}
	//use do-while loop to loop through 
        //all files in the current directory
	do {
		if (UpdateFileTime(structWIN32Data.cFileName) > 0) {
			_tprintf(_T("Error updating %s\n"), structWIN32Data.cFileName);
		}
	} while (FindNextFile(hSearchFile, &structWIN32Data));

    return 0;
}

int UpdateFileTime(const TCHAR *tchFileNamePtr) {
	FILETIME structFT;
	HANDLE hFile;

	_tprintf(_T("Updating file time for %s\n"), tchFileNamePtr);
	//retrieves the current date and time
	GetSystemTimeAsFileTime(&structFT);
	hFile = CreateFile(tchFileNamePtr, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	//could not get handle?
	if (hFile == INVALID_HANDLE_VALUE) {
		return GetLastError();
	}

	//change the file time
	SetFileTime(hFile, NULL, &structFT, &structFT);

	//close the file handle
	CloseHandle(hFile);

	return 0;

}

File locking is a limited method of managing concurrency between process and threads.

Windows can lock files so that no other process, or thread within the process, can access the locked region of the file. File locks can be shared or exclusive. Any attempt to write to the locked region, or to obtain a conflicting lock, will fail.

The LockFileEx() function locks a byte range in an open file for either shared or exclusive access. The first argument to the function is the handle of an open file, which must at least have GENERIC_READ access. The second argument is a flag that determines the lock mode and whether to wait for the lock to become available.

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

int main()
{
 //exclusive, read-write lock (default is write lock)
 printf("LOCKFILE_EXCLUSIVE_LOCK = %d\n", LOCKFILE_EXCLUSIVE_LOCK);
 
 //return immediately with FALSE if lock can't be 
 //acquired
 printf("LOCKFILE_FAIL_IMMEDIATELY = %d\n", LOCKFILE_FAIL_IMMEDIATELY);

 return 0;
}

We’ll look at file locking in greater depth at another time.

Declaring, Assigning, and Dereferencing Pointers

Pointers are basically the black magic that most of the other programming languages don’t want us to know about, or at least not be able to access directly. There’s good reason for this, actually, but let’s live dangerously.

Pointers are memory locations that store addresses. Pointers are not a data type, although they usually have the data type indicated for the purposes of dereferencing.

A data type is a memory location that can store an element taken from a set of possible values. For example, the int type can store a whole number selected from the range of whole numbers that is delimited for us in the limits.h header file.

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

int main()
{
 //let's declare some basic data types
 char chValue;
 int iValue;
 double dValue;

 //now let's declare a pointer
 //we set the data type of the variable
 //the pointer is meant to point to
 //and put an * in front of the variable name
 char *chPtr;
 int *iPtr;
 double *dPtr;

 return 0;
}

To create a pointer, we simply put an asterisk, *, in front of the pointer’s variable name.

We cannot store data of a data type in a pointer. We can only store memory addresses in a pointer. To access the memory address of a variable, we use the address-of operator, &.

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

int main()
{
 double dValue = 19.99;
 int iValue = 266613336;

 //to assign a memory location to a pointer
 //we use the assignment operator and the address-of 
 //operator
 double *dPtr = &dValue;
 int *iPtr = &iValue;

 printf("The address of dValue is %p\n", &dValue);
 printf("The dPtr stores the address %p\n", dPtr);

 printf("The address of iValue is %p\n", &iValue);
 printf("The address stored in iPtr is %p\n", iPtr);

 return 0;
}

The dereference operator is also an asterisk, *, which is the same character used to declare a pointer. The asterisk when placed in a declaration creates a pointer, when used with that same pointer outside the declaration, it tells the program to fetch the value stored in the memory location indicated by the pointer, rather than the memory location itself.

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

int main()
{
 //declare and initialize int variable
 int iValue = 1138;
 //declare and initialize int pointer
 int *iPtr = &iValue;

 //access address using address-of operator
 printf("%d (%p)\n", iValue, &iValue);
 //access value using dereference operator
 printf("%d (%p)\n", *iPtr, iPtr);

 return 0;
}

The compiler needs to know the type of the variable the pointer points to in order to know how many bytes to read.

As we have seen, a variable is named variable location that can store a value of a certain type. Each variable consists of two parts – the value that it stores, and the memory location it stores the value at. The variable’s address in memory is called its lvalue, and the variable’s content is called its rvalue. The lvalue is what is to the left of the assignment operator, the “bucket”, and the rvalue is what is to the right of the assignment operator, “what fills the bucket”.

With functions in C that pass function arguments by value, the rvalue of the argument is copied onto the function stack. With functions in C that pass function arguments by value, the lvalue of the argument is copied onto the function stack. Passing by reference enables the function to modify the values it receives as parameters and have those changes persist beyond the lifetime of the function. Passing by reference also saves memory, since pointers are always of uniform size, which is typically either 32 or 64 bits.

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


void swap(int *a, int *b) {
	printf("a points to %p and b points to %p\n", a, b);
	printf("but a is at %p and b is at %p\n", &a, &b);
	int temp = *a;
	*a = *b;
	*b = temp;
}

int main()
{
	int iA = 73;
	int iB = 1337;

	printf("iA = %d and iB = %d\n", iA, iB);
	printf("iA is at %p and b is at %p\n", &iA, &iB);

	//use address-of operator to pass in 
	//variable addresses as arguments
	swap(&iA, &iB);

	printf("Now iA = %d and iB = %d\n", iA, iB);

    return 0;
}

In fact, all functions in C pass values by value, in that a separate, distinct copy is made on the function’s stack, and exists solely for the lifetime of the function. However, when we pass by reference, we create a copy of the argument’s memory location, and not the value the argument is storing. In this way, we can modify variables that have been declared outside the called function.

Mutexes with win32

A mutex is a win32 kernel object that enables any thread in the system to acquire mutually exclusive ownership of a resource. A mutex can therefore be used to synchronize access to a resource between threads belonging to different processes.

Mutexes are created with the CreateMutex() function. The CreateMutex() function takes three parameters. The first parameter is a pointer to a SECURITY_ATTRIBUTES structure; if NULL, the mutex is created with default security attributes. The second argument is a Boolean flag indicating whether or not the calling thread is to own the mutex after returning from the function. The third and final argument is a null terminated string specifying the name of the mutex object. Passing NULL here will create a mutex with no name, which are only visible to threads within the process that created the mutex.

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

int main()
{
 HANDLE hMutex = CreateMutex(NULL, TRUE, "MutexExample");

 if (hMutex == NULL) {
 printf("Error creating mutex.\n");
 exit(EXIT_FAILURE);
 }

 if (GetLastError() == ERROR_ALREADY_EXISTS) {
 //the mutex already is in existence
 printf("The mutex alread exists.\n");
 exit(EXIT_FAILURE);
 }
 else {
 printf("Mutex created.\n");
 printf("...releasing mutex.\n");
 //release ownership of the mutex
 ReleaseMutex(hMutex);
 }

 //close mutex handle
 CloseHandle(hMutex);

 return 0;
}

Note that to close the handle to a mutex, we use the CloseHandle() function.

If we want to get a handle to a mutex that has been previously created by another thread either in the same or in a different process, we can use the OpenMutex() function. The OpenMutex() function, like the CreateMutex() function, has three parameters.

The first parameter is a DWORD value indicating a bitfield flag that specifies the type of access the calling thread would like to have to the mutex named in the third parameter. The second argument is a Boolean value indicating whether or not the returned handle can be inherited by processes created when the calling process using the CreateProcess() function. The value NULL is returned to indicate failure.

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

int main()
{
 HANDLE hMutexOne, hMutexTwo;
 TCHAR *tszMutexName = _T("AnExmpMutex");

 hMutexOne = CreateMutex(NULL, TRUE, tszMutexName);

 if (hMutexOne == NULL) {
 printf("Unable to create mutex.\n");
 printf("Error no. %d\n", GetLastError());
 exit(EXIT_FAILURE);
 }

 if (GetLastError() == ERROR_ALREADY_EXISTS) {
 printf("Error! Handle already exists.\n");
 exit(EXIT_FAILURE);
 }

 printf("Mutex created!\n");

 hMutexTwo = OpenMutex(MUTEX_ALL_ACCESS, TRUE, tszMutexName);

 if (hMutexTwo == NULL) {
 printf("Unable to open mutex.\n");
 exit(EXIT_FAILURE);
 }
 else {
 printf("Mutex opened.\n");
 printf("Releasing opened mutex.\n");
 ReleaseMutex(hMutexTwo);
 }


 printf("Releasing created mutex.\n");
 ReleaseMutex(hMutexOne);

 printf("Closing handles.\n");
 CloseHandle(hMutexOne);
 CloseHandle(hMutexTwo);

 return 0;
}

Note that the OpenMutex() function does not attempt to acquire ownership of the mutex.

A mutex can be in one of two states: signaled or nonsignaled. A mutex is in the signaled state when no thread owns the mutex. Threads take ownership of mutexes via the OpenMutex() or CreateMutex() functions.

Ownership of a mutex is released by a call to the ReleaseMutex() function, which only has one parameter, the handle to the mutex to be released.

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

const int Num_Threads = 5;
DWORD WINAPI ChildThreadFunc(LPVOID lpMutex);

int main()
{
 int i = 0;
 DWORD dwNewThreadID;
 HANDLE hChildThreads[Num_Threads];
 //create mutext with this thread the owner of the mutex
 HANDLE hMutex = CreateMutex(NULL, TRUE, NULL);

 while (i < Num_Threads) {
 hChildThreads[i] = CreateThread(NULL, NULL, &ChildThreadFunc, (void*)hMutex, 0, &dwNewThreadID);
 printf("In parent thread, child thread ID %p created.\n", dwNewThreadID);
 i++;
 }

 printf("In parent thread, releasing mutex.\n");
 ReleaseMutex(hMutex);

 Sleep(6000);

 return 0;
}

DWORD WINAPI ChildThreadFunc(LPVOID lpMutex) {
 //convert parameter to mutex handle
 HANDLE hMutex = (HANDLE)lpMutex;
 //save the thread ID
 DWORD dwThreadID = GetCurrentThreadId();
 DWORD dwResult = WaitForSingleObject(hMutex, INFINITE);
 if (dwResult == WAIT_OBJECT_0) {
 printf("Thread %p acquired mutex.\n", dwThreadID);
 //hold the mutex
 Sleep(1000);
 printf("Thread %p releasing the mutex.\n", dwThreadID);
 ReleaseMutex(hMutex);
 }
 else {
 return 1;
 }
 return 0;
}

Note that only one thread ever owns a mutex at a given time.

File Attributes and Directory Processing

We can search a directory with the functions FindFirstFile(), FindNextFile(), and FindClose().

The FindFirstFile() function accepts two parameters, a string pointer indicating the file name, and a pointer to a WIN32_FIND_DATA structure. We use the FindFirstFile() function to obtain a search handle, which is the return value of the function.

The WIN32_FIND_DATA structure has 9 fields. The first field, dwFileAttributes, is a DWORD value indicating the file attributes via bit settings. The next three fields are FILETIME structures that indicate the creation time, last access time, and last write time of the file. The next two values are DWORD values that indicate the high order and low order value of the file size, in bytes.  The value of the high-order DWORD value is 0, unless the size of the file is greater than MAXDWORD. 

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

int main()
{
 WIN32_FIND_DATA structFindData;

 //find the cpp file in this folder
 TCHAR tszFilename[] = _T("*.cpp");
 HANDLE hFile;
 hFile = FindFirstFile(tszFilename, &structFindData);

 //our source code is certainly less than this number
 printf("The size of MAXDWORD is %lu\n", MAXDWORD);

 if (hFile == INVALID_HANDLE_VALUE) {
 printf("Error finding file.\n");
 }

 if (structFindData.nFileSizeHigh == 0) {
 _tprintf(_T("The size of %s is %d bytes\n"), structFindData.cFileName, structFindData.nFileSizeLow);
 }

 return 0;
}

Note that the cFileName field is not the pathname; it is just the file name by itself.

In order to find multiple files matching the search criteria, we can call the FindNextFile() function, passing in the search handle we obtained from the FindFirstHandle() function.

The FindNextFile() function returns a Boolean value, true if the function found another file, false if the file did not find another file or received bad arguments. We can use GetLastError() to check if the functions failed to find another matching file.

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

int main()
{
 WIN32_FIND_DATA structWin32;
 //find all files/directories in C drive
 HANDLE hSearch = FindFirstFile(_T("C:\\*"), &structWin32);

 _tprintf(_T("%s\n"), structWin32.cFileName);

 //FindNextFile returns a Boolean
 while (FindNextFile(hSearch, &structWin32)) {
 _tprintf(_T("%s\n"), structWin32.cFileName);
 }
 if (GetLastError() == ERROR_NO_MORE_FILES) {
 printf("No more files...\n");
 }

 //FindClose to close search handle
 FindClose(hSearch);

 return 0;
}

Be sure and use the FindClose() function to close the search handle, not CloseHandle().

We can obtain a file’s full pathname using the GetFullPathName() function. The GetFullPathName() function has four parameters. The first parameter is the name of the file. The second parameter is the buffer size in TCHARs of the buffer that is to get the null-terminated string for the drive and path. The third argument is a pointer to the buffer that receives the null-terminated string for the drive and path. The fourth parameter is optional.

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

int main()
{
 
 WIN32_FIND_DATA structFData;
 HANDLE hFindFile;
 TCHAR tszBuffer[MAX_PATH];
 hFindFile = FindFirstFile(_T("."), &structFData);

 if (hFindFile == INVALID_HANDLE_VALUE) {
 printf("Error %d.\n", GetLastError());
 exit(EXIT_FAILURE);
 }

 GetFullPathName(structFData.cFileName, MAX_PATH, tszBuffer, NULL);

 printf("%s \n%s\n\n", tszBuffer, structFData.cFileName);

 FindClose(hFindFile);

 return 0;
}

Please note that the GetFullPathName() function is not recommended for multi-threaded programs.

File Pointers and File Attributes in win32

Windows keeps a file pointer for each open file handle, indicating the current position in the file. When we open the file with CreateFile(), the pointer is set to the start of the file. The WriteFile() and ReadFile() functions transfer data to or from that position and increment the file pointer by the number of bytes processed. We can move the file pointer around the file using the SetFilePointerEx() function, which can handle 64-bit pointers.

The SetFilePointerEx() function uses the LARGE_INTEGER union, which is a 64-bit data type, for both the requested position and the actual position reached. The return value is a Boolean value that indicates success or failure. The function takes four parameters. The first parameter is the handle to the file. The second and third parameters are of type LARGE_INTEGER; the third parameter is a pointer to a LARGE_INTEGER. The final parameter is a DWORD value, which can be set to one of three named constants, FILE_BEGIN, FILE_CURRENT, and FILE_END.

#include "stdafx.h"

#include <Windows.h>

int main()
{
 HANDLE hFile;
 LARGE_INTEGER liMoveTo;
 LARGE_INTEGER liNewLocation;
 DWORD dwBytesWritten, dwBytesRead;
 TCHAR *tszText = _T("The President has been kidnapped by ninjas. Are you a bad enough dude to rescue the President?");
 TCHAR tszBuffer[256];

 hFile = CreateFile(_T("test.txt"), GENERIC_ALL, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);

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

 if (WriteFile(hFile, tszText, _tcsclen(tszText) * sizeof(TCHAR), &dwBytesWritten, 0)) {
 printf("%d bytes written to the file.\n", dwBytesWritten);
 }
 else {
 printf("Error writting to the file.\n");
 exit(EXIT_FAILURE);
 }

 //for the union LARGE_INTEGER
 //we only need to set the field 'QuadPart'
 //let's jump ahead 44 characters
 liMoveTo.QuadPart = 44 * sizeof(TCHAR);

 SetFilePointerEx(hFile, liMoveTo, &liNewLocation, FILE_BEGIN);

 if (ReadFile(hFile, tszBuffer, 256, &dwBytesRead, 0)) {
 printf("File read from successfully.\n");
 printf("%d bytes read.\n", dwBytesRead);
 }

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

 _tprintf(_T("%s\n"), tszBuffer);

 return 0;
}

We can get the file size via the GetFileSizeEx() function. Like SetFilePointerEx(), the GetFileSizeEx() function uses the LARGE_INTEGER union.

#include "stdafx.h"

#include <Windows.h>

int main()
{
 HANDLE hFile;
 LARGE_INTEGER liLength;
 

 hFile = CreateFile(_T("test.txt"), GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);

 if (hFile == INVALID_HANDLE_VALUE) {
 printf("Error acquiring file handle.");
 exit(EXIT_FAILURE);
 }

 GetFileSizeEx(hFile, &liLength);

 printf("The file size is %lld bytes\n", liLength.QuadPart);


 return 0;
}

File have attribute bits that store special information about the file. We can examine attribute bits with the GetFileAttributes() function. The GetFileAttributes() function returns a DWORD value that stores the attribute bits.

#include <Windows.h>

int main()
{
 DWORD dwValue = GetFileAttributes(_T("test.txt"));

 if (dwValue == INVALID_FILE_ATTRIBUTES) {
 printf("Error retrieving the file attribtues for the file.\n");
 exit(EXIT_FAILURE);
 }

 if (dwValue & FILE_ATTRIBUTE_ARCHIVE) {
 printf("Archive.\n");
 }
 if (dwValue & FILE_ATTRIBUTE_DIRECTORY) {
 printf("Directory.\n");
 }
 if (dwValue & FILE_ATTRIBUTE_HIDDEN) {
 printf("File is hidden.\n");
 }
 if (dwValue & FILE_ATTRIBUTE_NORMAL) {
 printf("File is normal.\n");
 }
 if (dwValue & FILE_ATTRIBUTE_ENCRYPTED) {
 printf("File is encrypted.\n");
 }
 if (dwValue & FILE_ATTRIBUTE_TEMPORARY) {
 printf("File is temporary.\n")
 }

 return 0;
}

It is also possible to set the file attributes using the SetFileAttributes() function. The SetFileAttributes() function accepts a file name and a DWORD value that represents a set of attribute bits to be set.

#include "stdafx.h"

#include <Windows.h>

int main()
{
 DWORD dwFileAttributes = FILE_ATTRIBUTE_ENCRYPTED | FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_HIDDEN;

 if (SetFileAttributes(_T("test.txt"), dwFileAttributes)) {
 printf("File attributes altered.\n");
 }

 return 0;
}

Note that we combine multiple attributes with the binary or operator, |.

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.

Consoles and win32

We can perform console I/O with the ReadFile() and WriteFile() functions, but it’s easier to use the specific console I/O functions, ReadConsole() and WriteConsole(). The ReadConsole() and WriteConsole() functions both accept TCHAR, or generic characters. TCHAR characters can be used to describe either ANSI single byte characters or wide double byte Unicode characters; thus, the TCHAR type allows us to be narrow/wide neutral.

The GetStdHandle() function gives us a mechanism for retrieving the standard input, STDIN, the Standard Output, STDOUT, and the standard error handles, STDERR. The GetStdHandle() function takes a single parameter that can be one of three values, STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, and STD_ERROR_HANDLE.

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

int main()
{
 HANDLE hStdin, hStdout, hStderr;

 hStdin = GetStdHandle(STD_INPUT_HANDLE);
 if (hStdin != INVALID_HANDLE_VALUE) {
 printf("Handle to standard input acquired.\n");
 }
 //combine the steps
 if ((hStdout = GetStdHandle(STD_OUTPUT_HANDLE)) != INVALID_HANDLE_VALUE) {
 printf("Handle to standard output acquired.\n");
 }

 if ((hStderr = GetStdHandle(STD_ERROR_HANDLE)) != INVALID_HANDLE_VALUE){
 printf("Handle to standard error acquired.\n");
 }

 return 0;
}

The SetConsoleMode() and GetConsoleMode() functions allow us to modify and view the console mode for the input or screen buffer. The console mode describes how characters are processed via flags; five commonly used flags, which are enabled by default, are ENABLE_LINE_INPUT, ENABLE_ECHO_INPUT, ENABLE_PROCESSED_INPUT, ENABLE_PROCESSED_OUTPUT, and ENABLE_WRAP_AT_EOL_OUTPUT. Both the SetConsoleMode() and GetConsoleMode() functions return a Boolean value indicating success or failure.

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

void PrintConsoleInputFlags(DWORD dwFlags);
void PrintConsoleOutputFlags(DWORD dwFlags);

int main()
{
 HANDLE hStdin, hStdout;
 DWORD dwStdinFlags, dwStdoutFlags;
 //get handles to stdin and stdout
 hStdin = GetStdHandle(STD_INPUT_HANDLE);
 hStdout = GetStdHandle(STD_OUTPUT_HANDLE);

 if (hStdin == INVALID_HANDLE_VALUE || hStdout == INVALID_HANDLE_VALUE) {
 printf("Error getting stdin or stdout.\n");
 exit(EXIT_FAILURE);
 }

 //GetConsoleMode returns Boolean value indicating success
 if (GetConsoleMode(hStdin, &dwStdinFlags)) {
 printf("Console mode for stdin acquired.\n");
 }

 if (GetConsoleMode(hStdout, &dwStdoutFlags)) {
 printf("Console mode for stdout acquired.\n");
 }

 printf("\nFor stdin: \n");
 PrintConsoleInputFlags(dwStdinFlags);
 

 printf("\nFot stdout: \n");
 PrintConsoleOutputFlags(dwStdoutFlags);

 return 0;
}

void PrintConsoleInputFlags(DWORD dwFlags) {
 if (dwFlags & ENABLE_ECHO_INPUT) {
 printf("Echo input enabled.\n");
 }
 if (dwFlags & ENABLE_INSERT_MODE) {
 printf("Insert mode enabled.\n");
 }
 if (dwFlags & ENABLE_LINE_INPUT) {
 printf("Line input enabled.\n");
 }
 if (dwFlags & ENABLE_MOUSE_INPUT) {
 printf("Mouse input enabled.\n");
 }
 if (dwFlags & ENABLE_PROCESSED_INPUT) {
 printf("Processed input enabled.\n");
 }
}

void PrintConsoleOutputFlags(DWORD dwFlags) {
 if (dwFlags & ENABLE_PROCESSED_OUTPUT) {
 printf("Processed output enabled.\n");
 }
}

The ReadConsole() and WriteConsole() functions both return TRUE if and only if the action succeeds. The ReadConsole() function takes a handle to the input buffer, and then two length parameters indicating lengths in generic characters, not bytes. The WriteConsole() function is essentially the same, except the buffer is a pointer to a constant.

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


int main()
{
 const int Buffer_Length = 2056;

 HANDLE hStdin, hStdout;
 DWORD dwCharsRead, dwCharsWritten;
 TCHAR tszBuffer[Buffer_Length];

 BOOL bSuccess;

 //get handles for standard input
 //and standard output
 hStdin = GetStdHandle(STD_INPUT_HANDLE);
 hStdout = GetStdHandle(STD_OUTPUT_HANDLE);

 if (hStdin == INVALID_HANDLE_VALUE) {
 exit(EXIT_FAILURE);
 }
 else {
 printf("Handle to stdin acquired.\n");
 }

 if (hStdout == INVALID_HANDLE_VALUE) {
 exit(EXIT_FAILURE);
 }
 else {
 printf("Handle to stdout acquired.\n");
 }

 printf("Please enter in some text!\n");

 bSuccess = ReadConsole(hStdin, tszBuffer, Buffer_Length - 2, &dwCharsRead, NULL);

 //replace the enter with the null character
 if (bSuccess) {
 printf("Successfully read from console.\n");
 printf("%d chars read.\n", dwCharsRead);
 tszBuffer[dwCharsRead - 2] = '';
 }

 bSuccess = WriteConsole(hStdout, tszBuffer, _tcslen(tszBuffer), &dwCharsWritten, NULL);

 if (bSuccess) {
 printf("\nSuccessfully wrote to console.\n");
 printf("%d chars written.\n", dwCharsWritten);
 }


 //close the handles!
 CloseHandle(hStdin);
 CloseHandle(hStdout);

 return 0;
}

Note that a Windows process can only have one console.