Working with Drives with win32

We can obtain information about any volume available locally on our machine or mounted over the network via the GetVolumeInformation() function. The GetVolumeInformation() function returns the volume name and serial number, along with the type of file system used on the volume and the length of the maximum file name for the file system in use on that volume.

The GetVolumeInformation() function has eight parameters.

The first parameter is a pointer to a string that contains the root directory of the volume. If we pass NULL as the argument the root of the current directory is used. The second argument is a pointer to a buffer that gets the name of a specified volume. The third argument is the length of this buffer, which can be a maximum of MAX_PATH + 1. The fourth argument is a pointer to a variable that receives the volume serial number; we can specify NULL if we do not care about the volume’s serial number. The fifth argument is optional; we will specify NULL for this argument.

The sixth argument is a pointer to a variable that stores system flags. There are a number of interesting system flags. For example, FILE_SUPPORTS_EXTENDED_ATTRIBUTES denotes that the specified volume supports extended attributes, chunks of application-specific metadata that an application can associate with a file. The FILE_PERSISTENT_ACLS flag denotes that the specified volume preserves and enforces Access Control Lists. The FILE_SUPPORTS_USN_JOURNAL flag tells us that the volume supports update sequence number journals. The update sequence number is the offset for a particular record in the change journal record stream. The FILE_SUPPORTS_TRANSACTIONS flag denotes that the specified volume supports transactions.

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


int _tmain(int argc, _TCHAR* argv[])
{
	printf("%08x FILE_SUPPORTS_EXTENDED_ATTRIBUTES\n", FILE_SUPPORTS_EXTENDED_ATTRIBUTES);
	printf("%08x FILE_PERSISTENT_ACLS\n", FILE_PERSISTENT_ACLS);
	printf("%08x FILE_SUPPORTS_USN_JOURNAL\n", FILE_SUPPORTS_USN_JOURNAL);
	printf("%08x FILE_SUPPORTS_TRANSACTIONS\n", FILE_SUPPORTS_TRANSACTIONS);
	printf("%08x FILE_SUPPORTS_ENCRYPTION\n", FILE_SUPPORTS_ENCRYPTION);

	return 0;
}

The second to last parameter is a pointer to a buffer to store the name of the file system, and the final parameter is the length of that buffer. The GetVolumeInformation() function returns True on success and False on failure.

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


int _tmain(int argc, _TCHAR* argv[])
{
	BOOL boolRValue;
	TCHAR wszVolumeName[MAX_PATH + 1];
	DWORD dwVolumeSerialNumber;
	DWORD dwFileSystemFlags;
	TCHAR wszSystemName[MAX_PATH + 1];

	boolRValue = GetVolumeInformation(
		NULL, //default to volume for current working directory
		wszVolumeName,
		MAX_PATH,
		&dwVolumeSerialNumber,
		NULL, //component max length
		&dwFileSystemFlags,
		wszSystemName,
		MAX_PATH
		);

	//output information
	//note the %ls format specifier is for printing wide strings
	printf("Volume name: %ls\n", wszVolumeName);
	printf("Volume serial number: %d\n", dwVolumeSerialNumber);
	printf("File system type: %ls\n", wszSystemName);

	printf("File system characteristics:\n");
	if(dwFileSystemFlags & FILE_SUPPORTS_ENCRYPTION){
		printf("The file system supports encryption.\n");
	} else {
		printf("The file system does not support encryption.\n");
	}
	if(dwFileSystemFlags & FILE_PERSISTENT_ACLS){
		printf("The volume preserves and enforces ACLs.\n");
	} else {
		printf("The volume does not preserve and enforce ACLs.\n");
	}
	if(dwFileSystemFlags & FILE_SUPPORTS_USN_JOURNAL){
		printf("The volume supports update sequence number journals.\n");
	} else {
		printf("The volume does not support update sequence number journals.\n");
	}
	if(dwFileSystemFlags & FILE_SUPPORTS_EXTENDED_ATTRIBUTES){
		printf("The volume supports extended attributes.\n");
	} else {
		printf("The volume does not support extended attributes.\n");
	}

	return 0;

}

The GetDriveType() function helps us determine the type of a drive connected to our system. The function takes one argument, a string denoting the root directory of the drive. If we pass NULL as an argument, the GetDriveType() function will display the drive type for the current working directory.

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

int _tmain(int argc, _TCHAR* argv[])
{
	
	switch(GetDriveType(NULL)){
	case 0:
		printf("Error.\n");
		break;
	case 1:
		printf("Drive does not exist.\n");
		break;
	case 2:
		printf("Removable media.\n");
		break;
	case 3:
		printf("Fixed disk.\n");
		break;
	case 4:
		printf("Network drive.\n");
		break;
	case 5:
		printf("CD-ROM drive.\n");
		break;
	}
	
	return 0;

}

That’s all for today. If you can, please take a look at my book on C programming: http://www.amazon.com/Big-Als-C-Standard-ebook/dp/B00A4JGE0M/

Advertisements

Files and Directories using win32

The CreateDirectory() function is used to create a new directory. It takes two arguments, the first is the directory name, and the second is the security settings for the new directory. The RemoveDirectory() function deletes directories. It takes only one argument, the path of the directory to be deleted.

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

int _tmain(int argc, _TCHAR* argv[])
{
	char name[56] = "aDirectory";

	if(CreateDirectory(name, NULL)){
		printf("Directory %s created.\n", name);
	}

	if(RemoveDirectory(name)){
		printf("Directory %s removed.\n", name);
	}

	return 0;
}

We can use the GetFileAttributes() function to make sure a file or directory exists.

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

bool CheckIfFileObjectExists(const char *filename);
bool CheckIfDirectory(const char *filename);
bool CheckIfFile(const char *filename);

int _tmain(int argc, _TCHAR* argv[])
{
	HANDLE hFile;
	char dirName[56] = "TestDirectory";
	char fileName[56] = "TestFile.txt";

	if(CheckIfFileObjectExists(dirName)){
		printf("File object '%s' exists.\n", dirName);
		if(CheckIfDirectory(dirName)){
			printf("It is a directory.\n");
		}
	} else {
		printf("Directory '%s' does not exist.\n", dirName);
		CreateDirectory(dirName, NULL);
	}

	hFile = CreateFile(fileName, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);

	if(hFile == INVALID_HANDLE_VALUE){
		printf("Error creating file %s\n", fileName);
	} else {
		printf("File %s created.\n", fileName);
		CloseHandle(hFile);
	}

	if(CheckIfFileObjectExists(fileName)){
		printf("%s exists.\n", fileName);
		if(CheckIfDirectory(fileName)){
			printf("%s is a directory.\n", fileName);
		} else {
			printf("%s is not a directory.\n", fileName);
		}
		if(CheckIfFile(fileName)){
			printf("%s is a file.\n", fileName);
			printf("Deleting file now...");
			DeleteFile(fileName);
		}
	}

	return 0;
}

bool CheckIfFileObjectExists(const char *filename){
	DWORD dwAttributes = GetFileAttributes(filename);
	return (bool)(dwAttributes != INVALID_FILE_ATTRIBUTES);
}

bool CheckIfDirectory(const char *filename){
	DWORD dwAttributes = GetFileAttributes(filename);
	return (bool)(dwAttributes & FILE_ATTRIBUTE_DIRECTORY);
}

bool CheckIfFile(const char *filename){
	DWORD dwAttributes = GetFileAttributes(filename);
	return (bool)!(dwAttributes & FILE_ATTRIBUTE_DIRECTORY);
}

The FindFirstFile() function searches through a directory for a given file. Since the FindFirstFile() function accepts a wildcard, we can use it to list all the files in a directory. The FindFirstFile() function takes two arguments, the first of which is the file to find, the second of which is a pointer to a WIN32_FIND_DATA structure.

If the FindFirstFile() function executes successfully, we get a search handle that can be used in subsequent find operations. If FindFirstFile() fails, we get an INVALID_HANDLE_VALUE returned.

The FindNextFile() is used to continue a search. The FindNextFile() function returns true if it finds another file, and false if it doesn’t.

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

void listDirectoryContents(const char *filename);

int _tmain(int argc, _TCHAR* argv[])
{
	listDirectoryContents("C:\\Users\\AL");
	listDirectoryContents("C:\\Users\\AL\\Downloads");
	return 0;
}

void listDirectoryContents(const char *filename){

	DWORD dwAttrib = GetFileAttributes(filename);
	//note negation operator
	if(!(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)){
		printf("%s is not a directory", filename);
		return;
	}

	//create search filename with wildcard (*)
	char buffer[256];
	sprintf_s(buffer, "%s\\*", filename);

	LARGE_INTEGER unionFilesize;
	WIN32_FIND_DATA structWFD;
	HANDLE hSrch = FindFirstFile(buffer, &structWFD);

	if(hSrch == INVALID_HANDLE_VALUE){
		printf("Error %x\n", GetLastError);
		return;
	}

	printf("\nFiles in %s:\n", filename);

	if(structWFD.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){
		printf("%s \t (directory)\n", structWFD.cFileName);
	} else {
		//get filesize
		unionFilesize.HighPart = structWFD.nFileSizeHigh;
		unionFilesize.LowPart = structWFD.nFileSizeLow;
		printf("%s \t %lld bytes\n", structWFD.cFileName, unionFilesize.QuadPart);
	}

	while(FindNextFile(hSrch, &structWFD)!=0){
		if(structWFD.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){
			printf("%s \t (directory)\n", structWFD.cFileName);
		} else {
			unionFilesize.HighPart = structWFD.nFileSizeHigh;
			unionFilesize.LowPart = structWFD.nFileSizeLow;
			printf("%s \t %lld bytes\n", structWFD.cFileName, unionFilesize.QuadPart);
		}
	}

	FindClose(hSrch);

}

The FindClose() function is used to close the search handle. It’s a good idea to explicitly close the handle.

Copying Files Using win32 API Calls

The CopyFile() function takes source and destination filenames, plus a flag that specifies whether or not an existing file destination should be overwritten. The CopyFile() function returns a Boolean value.

#include <windows.h>


int _tmain(int argc, _TCHAR* argv[])
{
	char *filename = "testfile.txt";
	char buffer[256] = "If the fool would persist in his folly he would become wise.";
	DWORD bytesWritten;

	HANDLE hFile = CreateFile(
		filename,
		GENERIC_WRITE,
		0,
		NULL,
		CREATE_ALWAYS,
		NULL,
		NULL
		);

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

	if(WriteFile(hFile, buffer, strlen(buffer), &bytesWritten, NULL)){
		printf("File written to.\n");
	} else {
		printf("File not written to.\n");
	}

	CloseHandle(hFile);

	//will fail if file already exists
	if(CopyFile(filename, "testfile_copy.txt", TRUE)){
		printf("File copied.\n");
	} else {
		printf("File not copied.\n");
	}

	//will not fail if file already exists.
	if(CopyFile(filename, "testfile_copytwo.txt", FALSE)){
		printf("File copied.\n");
	} else {
		printf("File not copied.\n");
	}

	return 0;
}

The CopyFileEx() function allows us to copy a large file and monitor the process, or even cancel it. The CopyFileEx() function takes six arguments. The first two arguments are the source file name and the destination file name. The third argument is a pointer to a progress routine; this can be set to NULL. The fourth parameter is the argument to be passed on to the callback function; we can likewise set this parameter to NULL. The fifth parameter is a Boolean value that specifies whether or not we want to cancel the copy operation. The final parameter consists of flags that tell how the file is to be copied.

#include <Windows.h>

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

	printf("%u (COPY_FILE_FAIL_IF_EXISTS): ", COPY_FILE_FAIL_IF_EXISTS);
	printf("The copy operation fails immediately if the taret file already exists.\n\n");

	printf("%u (COPY_FILE_COPY_SYMLINK): ", COPY_FILE_COPY_SYMLINK);
	printf("If the source file is a symbolic link, then the destination file is also a symbolic link pointing to whatever file the source symbolic link is pointing to.\n\n");

	printf("%u (COPY_FILE_NO_BUFFERING): ", COPY_FILE_NO_BUFFERING);
	printf("The copy operation is done using unbuffered I/O, bypassing system I/O cache resources.\n\n");

	printf("%u (COPY_FILE_RESTARTABLE): ", COPY_FILE_RESTARTABLE);
	printf("Progress of the copy is tracked in the target file in case the copy fails. The failed copy can then be started at a later time.");
	
	return 0;
}

The third and fourth arguments to CopyFileEx() specify the address of and the argument for a progress routine, which is a callback routine that is called whenever a portion of the file has been copied. We can place NULL here for these arguments, but instead let’s define a simple progress routine. While the progress routine is application-defined, much of the “work” involving the function’s many parameters is handled for us behind the scenes. The first thing to remember when defining a copy progress routine is that the LARGE_INTEGER type is a union, and if we are working on a 64 bit machine, and we are, we should just access the QuadPart field for each parameter of that type. The second thing to remember is that the function should return the value PROGRESS_CONTINUE in order to keep copying the file.

#include <Windows.h>

DWORD CALLBACK CopyProgressRoutine(
	LARGE_INTEGER TotalFileSize,
	LARGE_INTEGER TotalBytesTransferred,
	LARGE_INTEGER StreamSize,
	LARGE_INTEGER StreamBytesTransferred,
	DWORD dwStreamNumber,
	DWORD dwCallbackReason,
	HANDLE hSourceFile,
	HANDLE hDestinationFile,
	LPVOID lpData
	){
		int percentage = (double(TotalBytesTransferred.QuadPart) / double(TotalFileSize.QuadPart)) * 100;

		printf("%%%d copied\n", percentage);

		return PROGRESS_CONTINUE;
}


int _tmain(int argc, _TCHAR* argv[])
{
	char *filename="C:\\Program Files (x86)\\Windows NT\\Accessories\\wordpad.exe";

	bool returnVal;

	returnVal = CopyFileEx(
		filename,
		"wordpad_copy",
		(LPPROGRESS_ROUTINE)CopyProgressRoutine,
		NULL,
		NULL,
		COPY_FILE_NO_BUFFERING);

	if(returnVal){
		printf("%s copied to current directory.\n", filename);
	} else {
		printf("%s not copied to current directory.\n", filename);
		printf("Error %u.\n", GetLastError());
	}
	
	return 0;
}

Note that CALLBACK is defined as __stdcall, which is the standard calling convention for win32 system calls. With the __stdcall calling convention, the callee cleans the stack.

Getting Windows File Attributes

The GetFileAttributes() function retrieves the attribute settings for a file. It returns a DWORD value that can be AND-ed using the << operator to extract information.

Note that the GetFileAttributes() function takes a string filename and not a handle.

int _tmain(int argc, _TCHAR* argv[])
{
	char *file = "C:";
	DWORD dwAttrib;

	dwAttrib = GetFileAttributes(file);

	if(dwAttrib == 0xFFFFFFFF){
		printf("Error getting attributes.\n");
		exit(EXIT_FAILURE);
	}

	if(dwAttrib & FILE_ATTRIBUTE_NORMAL){
		printf("File is a normal file.\n");
	} 
	if(dwAttrib & FILE_ATTRIBUTE_DIRECTORY){
		printf("File is a directory.\n") ;
	} 
	if (dwAttrib & FILE_ATTRIBUTE_DEVICE){
		printf("File is a device.\n");
	} 
	if(dwAttrib & FILE_ATTRIBUTE_ARCHIVE){
		printf("File is an archive.\n");
	} 
	if(dwAttrib & FILE_ATTRIBUTE_COMPRESSED){
		printf("File is compressed.\n");
	} 
	if(dwAttrib & FILE_ATTRIBUTE_HIDDEN){
		printf("File is hidden.\n");
	} 
	if(dwAttrib & FILE_ATTRIBUTE_ENCRYPTED){
		printf("File is encrypted.\n");
	} 

	return 0;
}

The GetFileAttributes() function has a corresponding SetFileAttributes() function that can be used to set the attributes for a file. The SetFileAttributes() function takes two arguments, the first is the file name, and the second is a DWORD value that specifies the new attributes.

#include "windows.h"

void printAttributes(DWORD fileAttributes);

int _tmain(int argc, _TCHAR* argv[])
{
	char *filename = "test.txt";
	DWORD dwCurrentAttrib, dwNewAttrib;

	HANDLE hwFile = CreateFile(
		filename,
		GENERIC_READ | GENERIC_WRITE,
		0,
		NULL,
		CREATE_ALWAYS,
		FILE_ATTRIBUTE_NORMAL,
		NULL
		);

	if(hwFile==INVALID_HANDLE_VALUE){
		printf("Error opening/creating file.\n");
		exit(EXIT_FAILURE);
	} else {
		CloseHandle(hwFile);
	}

	dwCurrentAttrib = GetFileAttributes(filename);
	printAttributes(dwCurrentAttrib);

	dwNewAttrib = FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_READONLY;
	if(SetFileAttributes(filename, dwNewAttrib)){
		printf("File attributes modified successfully.\n");
	} else {
		printf("File attributes could not be modified.\n");
		exit(EXIT_FAILURE);
	}

	dwCurrentAttrib = GetFileAttributes(filename);
	printAttributes(dwCurrentAttrib);

	if(SetFileAttributes(filename, FILE_ATTRIBUTE_NORMAL)){
		printf("File attributes modified successfully.\n");
	}

	printAttributes(GetFileAttributes(filename));
	

	return 0;
}

void printAttributes(DWORD fileAttributes){
	printf("Attributes: ");
	if(fileAttributes == INVALID_FILE_ATTRIBUTES){
		printf("File attribute is invalid.\n");
		return;
	}
	if(fileAttributes & FILE_ATTRIBUTE_NORMAL){
		printf("Normal file. ");
	}
	if(fileAttributes & FILE_ATTRIBUTE_DIRECTORY){
		printf("Directory. ");
	}
	if(fileAttributes & FILE_ATTRIBUTE_HIDDEN){
		printf("File is hidden. ");
	}
	if(fileAttributes & FILE_ATTRIBUTE_READONLY){
		printf("File is read only. ");
	}
	if(fileAttributes & FILE_ATTRIBUTE_ARCHIVE){
		printf("Archive. ");
	}
	if(fileAttributes & FILE_ATTRIBUTE_TEMPORARY){
		printf("Temporary. ");	
	}
	printf("\n");
}

Note that the SetFileAttributes() function returns a Boolean value.

File Manipulation with the win32 API

Our first program starts by opening a file handle to a file we will create, testfile.txt. The file handle is opened using the CreateFile() call. This call returns a handle to the new file, or else it return INVALID_HANDLE_VALUE, which means there has been a problem. We will also acquire a handle to standard input using the GetStdHandle() API call.

We will then create a loop that reads in text from standard input via the ReadFile() call and writes it to the file via the WriteFile() call.

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

int _tmain(int argc, _TCHAR* argv[])
{
	DWORD dwBytesRead, dwBytesWritten, dwTotal;
	char szBuffer[512];
	HANDLE hIn;

	HANDLE hFile = CreateFile(
		"testfile.txt", // filename
		GENERIC_WRITE, //open for writing
		0, //no sharing
		NULL, //no security
		CREATE_NEW, //new file
		FILE_ATTRIBUTE_NORMAL, //no special attributes
		NULL); //no template file

	//check for error
	if(hFile == INVALID_HANDLE_VALUE){
		printf("Error creating file.\n");
		return -1;
	}

	//get the handle to standard input
	hIn = GetStdHandle(STD_INPUT_HANDLE);
	//set total written to 0
	dwTotal = 0;
	printf("Enter the text to write to the file. Enter '!' to finish.\n");
	while(TRUE){
		if((ReadFile(hIn, szBuffer, sizeof(szBuffer), &dwBytesRead, NULL))==FALSE){
			printf("Error reading from standard input.\n");
			return -2;
		}
		if(szBuffer[0]=='!'){
			printf("Finishing...\n");
			break;
		} else {
			szBuffer[dwBytesRead] = '';
		}

		//write the text to the file.
		if((WriteFile(hFile, szBuffer, strlen(szBuffer), &dwBytesWritten, NULL))==FALSE){
			printf("Error writing to the file.\n");
			return -3;
		}
		dwTotal += dwBytesWritten;
	} //end while loop

	printf("%u bytes written.\n", dwTotal);


	return 0;
}

The access mode specified in the second parameter to CreateFile() describes how we want to access the file, we can specify GENERIC_READ, GENERIC_WRITE, or a combination of the two.

The share mode is specified in the third parameter. It states whether we want the file to be shareable by other processes. If we specify 0 here, then the file is not to be shared. If we specify FILE_SHARE_READ, then other other processes may have read access to the file. If we specify FILE_SHARE_WRITE, then other processes may have write access to the file.

The fourth parameter specifies the security settings that we want applied to the file when it is created. Since we do not want any special security settings, we passed NULL for this parameter, which gave us the default settings.

The creation flags say what action to take when opening the file; here we must specify one of five values. CREATE_NEW will create a new file, and fail if the file already exists. CREATE_ALWAYS will create a new file, overwriting the file if it already exists. OPEN_EXISTING opens an existing file, and fails if the file doesn’t exist. OPEN_ALWAYS opens a file, creating it if it doesn’t exist. TRUNCATE_EXISTING opens a file and truncates its length to zero.

#include 
#include 

int _tmain(int argc, _TCHAR* argv[])
{
	HANDLE hOne, hTwo, hThree;

	char *name = "test.txt";

	hOne = CreateFile(
		name,
		GENERIC_READ,
		0,
		NULL,
		OPEN_EXISTING,
		FILE_ATTRIBUTE_NORMAL,
		NULL
		);

	//won't work the first time we run the program
	//will work all other times
	if(hOne == INVALID_HANDLE_VALUE){
		printf("Error reading file %s with OPEN_EXISTING\n", name);
	} else {
		printf("File %s opened with OPEN_EXISTING\n", name);
		CloseHandle(hOne);
	}

	hTwo = CreateFile(
		name,
		GENERIC_WRITE,
		0, //no sharing
		NULL, //default security
		CREATE_NEW,
		FILE_ATTRIBUTE_NORMAL,
		NULL
		);

	//will work the first time we run the program
	//won't work all others
	if(hTwo == INVALID_HANDLE_VALUE){
		printf("Error creating new file %s with CREATE_NEW\n", name);
	} else {
		printf("File %s created with CREATE_NEW\n", name);
		CloseHandle(hTwo);
	}

	hThree = CreateFile(
		name,
		GENERIC_WRITE,
		0,
		NULL,
		CREATE_ALWAYS,
		FILE_ATTRIBUTE_NORMAL,
		NULL
		);

	//will always work
	if(hThree == INVALID_HANDLE_VALUE){
		printf("Error creating new file %s with CREATE_ALWAYS\n", name);
	} else {
		printf("File %s created with CREATE_ALWAYS\n", name);
	}


	return 0;
}

Note that the CloseHandle() function is used to close the file via its associated handle.

The attributes parameter sets the file flags and attributes. There are a number of file flags available. For example, the FILE_ATTRIBUTE_HIDDEN flag makes the file hidden in normal directory listings, the FILE_ATTRIBUTE_READONLY flag indicates that the file is read-only, the FILE_ATTRIBUTE_NORMAL flag indicates that the file has no other attributes set, and the FILE_ATTRIBUTE_TEMPORARY flag shows that the file is a temporary file.

The first two arguments passed to the ReadFile() function are a handle to the file to be read from and a pointer to a buffer to receive the input. The third parameter is the number of bytes to be read, and the fourth parameter is a pointer to an integer variable that will store the number of bytes that are actually read. The final parameter is used for asynchronous I/O, we will place NULL here.

The WriteFile() function is essentially the same as the ReadFile() function, except the buffer is written from, not written to. Both the ReadFile() and WriteFile() functions return TRUE if everything is copacetic and FALSE otherwise.

#include 
#include 

int _tmain(int argc, _TCHAR* argv[])
{
	HANDLE hWrite, hRead;
	char *filename = "example.txt";
	char szBuffer[256];
	char szText[256] = "The morning sun has vanquished the horrible night.";
	DWORD dwBytesWritten, dwBytesRead;

	hWrite = CreateFile(
		filename,
		GENERIC_WRITE,
		0,
		NULL,
		CREATE_ALWAYS,
		FILE_ATTRIBUTE_NORMAL,
		NULL
		);

	if(hWrite == INVALID_HANDLE_VALUE){
		printf("Error creating file handle.\n");
		return -1;
	}

	//use strlen() and not sizeof() when writing to a file
	if(WriteFile(hWrite, szText, strlen(szText), &dwBytesWritten, NULL)){
		printf("File Written to Successfully.\n");
	} else {
		printf("Error writing to file.\n");
		return -2;
	}

	CloseHandle(hWrite);

	hRead = CreateFile(
		filename,
		GENERIC_READ,
		0,
		NULL,
		OPEN_EXISTING,
		FILE_ATTRIBUTE_NORMAL,
		NULL
		);

	if(hRead == INVALID_HANDLE_VALUE){
		printf("Error creating file handle.\n");
		return -3;
	}

	if(ReadFile(hRead, szBuffer, sizeof(szBuffer), &dwBytesRead, NULL)){
		printf("File Read from Successfully.\n");
	} else {
		printf("Error reading from file.\n");
		return -4;
	}

	szBuffer[dwBytesRead] = '';

	printf("Text: %s\n", szBuffer);

	CloseHandle(hRead);

	return 0;
}

Remember that with ReadFile() and WriteFile() we must pass a file handle, which is not the same thing as a file name.

Unicode and win32

The ANSI character set is 8 bit, which gives it the ability to represent 256 characters.

The char data type stores one character. Unicode or wide characters do not affect the meaning of the char data type in C.

#include <stdio.h>

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

	char z = 'a';
	char *pZ = &z;

	printf("%c\n", z);
	printf("sizeof(z) = %u byte\n", sizeof(z));
	//pointer will be either 4 or 8 bytes, depending on settings
	printf("%c \t %p\n", *pZ, pZ);
	printf("sizeof(pZ) = %u bytes\n", sizeof(pZ));

	return 0;
}

Unicode is a uniform two-byte system. The first Unicode standard was published in 1990. Unicode is used internally by Windows and also by the Java programming language.

Wide characters in C are based on the wchar_t data type. The whcar_t data type is defined as being the same as an unsigned short, which should make it at least 16 bits in size.

#include <stdio.h>

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

	wchar_t a = 'a';
	wchar_t arr[3] = {'H', 'i', ''};

	printf("%c \t is %u bytes.\n", a, sizeof(a));
	printf("%s \t is %u bytes.\n", arr, sizeof(arr));

	return 0;
}

When we initialize a pointer to a wide-character string, we should preference the string literal (the text in double quotation marks being assigned) with a capital letter L. The capital L, for long, preceding the first double quotation mark tells the compiler that the string should be stored with wide characters.

The LPWSTR type specifies a pointer to a sequence of Unicode (wide) characters that may or may not be terminated by a null character.

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

int _tmain(int argc, _TCHAR* argv[])
{
	wchar_t *pPtr = L"Nice boat!";
	wchar_t arrStr[] = L"Do you have stairs in your house?";
	//pointer to wide string
	LPWSTR lpStr = arrStr;
}

To find the length of a regular character string, we use the strlen() function. However, the strlen() function doesn’t work so well with a null-terminated wchar_t array. The wide-character version of the strlen() function is wcslen().

The wprintf() function is the wide string equivalent of the printf() function. The wprintf() function replaces format identifiers in the format string in the same manner that printf() does.

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

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

	char *strOne = "El Psy Congroo.";
	wchar_t *wStrTwo = L"Han shot first.";
	wchar_t *wStrThree = L"You are likely to be eaten by a grue.";
	

	printf("String 1 is %u characters long.\n", strlen(strOne));
	printf("String 2 is %u characters long.\n", wcslen(wStrTwo));
	printf("String 3 is %u characters long.\n", wcslen(wStrThree));

	printf("String 1 is %s\n", strOne);
	wprintf(L"String 2 is %s\n", wStrTwo);
	wprintf(L"String 3 is %s\n", wStrThree);

	return 0;
}

Note that in wprintf() the format string itself is a wide character string.

Similarly, the wcsncpy() function is the wide character version of the strncpy() function. It copies a specified number of characters from a source to a destination string. Likewise, the wcsncat() function is the wide character equivalent of the strncat() function, which appends a specified number of characters from a source to a destination string.

Microsoft has produced a set of generic string functions; they are generic because they can refer to either the Unicode or non-Unicode versions of the functions. The preprocessor will map the generic function onto the right one for the character set to use.

For example, the _tcspcy() function will copy a generic string; it maps onto the strcpy() or wcscpy() functions, depending. We can also use the _T() macro in place of the L prefix used for Unicode strings.

The type used for string parameters to these functions is TCHAR. TCHAR will, depending on the situation, be mapped to either char or WCHAR. We can then write our code in terms of TCHAR and its derived pointer types, LPTSTR, which is a pointer to a TCHAR, and LPCTSTR, which is a const pointer to a TCHAR.

#include 
#include 

int _tmain(int argc, _TCHAR* argv[])
{
	TCHAR *tchString = _T("On the Internet, no one knows you're a dog.");
	LPTSTR tchPtrString = tchString;
	TCHAR tchBuffer[256];

	_tprintf(_T("The string is: %s\n"), tchString);
	_tprintf(_T("The string is still: %s\n"), tchPtrString);

	_tcscpy(tchBuffer, tchString);

	return 0;
}

Remember, the Tchar.h header file contains a set of alternatives for the normal functions requiring string parameters. This header file is provided by Microsoft, and is not part of the ANSI standard.

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.