Diving into File Handling with win32

I’d like to review creating and managing files using win32, and maybe go a bit more in depth as well.

The second to last parameter for CreateFile() specifies a range of attributes for the file in the form of flags. These flags are logically organized into three groups; we will cover the first two groups here. The first group represents a set of file attributes. FILE_ATTRIBUTE_HIDDEN keeps the file from being included in an ordinary directory listing. FILE_ATTRIBUTE_ENCRYPTED will cause the file or directory to be encrypted; in the case of a directory, this means that all newly created files and subdirectories will be, by default, encrypted. FILE_ATTRIBUTE_NORMAL is the default; note that this flag is only valid when used by itself. FILE_ATTRIBUTE_READONLY makes the file available for reading, but not writing or deletion. FILE_ATTRIBUTE_TEMPORARY means that the file is being used for temporary storage.

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

int _tmain(int argc, _TCHAR* argv[])
{
    
    printf("FILE_ATTRIBUTE_READONLY \t %d 0x%x\n", FILE_ATTRIBUTE_READONLY, FILE_ATTRIBUTE_READONLY);
    printf("FILE_ATTRIBUTE_HIDDEN \t %d 0x%x\n", FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_HIDDEN);
    printf("FILE_ATTRIBUTE_NORMAL \t %d 0x%x\n", FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_NORMAL);
    printf("FILE_ATTRIBUTE_TEMPORARY \t %d 0x%x\n", FILE_ATTRIBUTE_TEMPORARY, FILE_ATTRIBUTE_TEMPORARY);
    printf("FILE_ATTRIBUTE_ENCRYPTED \t %d 0x%x\n", FILE_ATTRIBUTE_ENCRYPTED, FILE_ATTRIBUTE_ENCRYPTED);

    return 0;

}

The FILE_FLAG_BACKUP_SEMANTICS flag indicates that the file is being opened or created for a backup or restore operation. Note that we must specify this flag if we want to work with a directory. The FILE_FLAG_DELETE_ON_CLOSE flag indicates that the file should be deleted as soon as all of its handles are closed. The FILE_FLAG_OVERLAPPED flag indicates that the file or device is to be opened or created for asynchronous I/O. If this flag is specified, the file can be used for simultaneous read and write operations. The FILE_FLAG_RANDOM_ACCESS is pretty self-explanatory, the file will be accessed randomly. The FILE_FLAG_NO_BUFFERING flag indicates that the file or device is to be opened with no system caching for data reads and writes.

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

int _tmain(int argc, _TCHAR* argv[])
{
    
    printf("FILE_FLAG_DELETE_ON_CLOSE %d.\n", FILE_FLAG_DELETE_ON_CLOSE);
    printf("FILE_FLAG_OVERLAPPED %d.\n", FILE_FLAG_OVERLAPPED);
    printf("FILE_FLAG_BACKUP_SEMANTICS %d.\n", FILE_FLAG_BACKUP_SEMANTICS);
    printf("FILE_FLAG_RANDOM_ACCESS %d.\n", FILE_FLAG_RANDOM_ACCESS);
    printf("FILE_FLAG_NO_BUFFERING %d.\n", FILE_FLAG_NO_BUFFERING);

    return 0;

}

The OPEN_ALWAYS creation disposition will always give us a file handle to the file, unless there has been a grievous error of some sort. If the file doesn’t exist, it will be created, and if it does exist, the function will still succeed but the last-error code will be set to ERROR_ALREADY_EXISTS.

#include <Windows.h>

int _tmain(int argc, _TCHAR* argv[])
{
    char *szFileName = "kilroy.txt";
    HANDLE hFile;
    //create the file!
    //if file already exists, then we will open it
    //otherwise it will be created and then opened
    hFile = CreateFile(
        szFileName,
        GENERIC_WRITE,
        FILE_SHARE_READ,
        NULL,
        OPEN_ALWAYS,
        FILE_ATTRIBUTE_NORMAL,
        NULL
        );

    if (hFile == INVALID_HANDLE_VALUE){
        printf("Failed to create/open file %s.\n", szFileName);
    }
    else {
        if (GetLastError() == ERROR_ALREADY_EXISTS){
            printf("File already exists, opening...\n");
        }
        else {
            printf("File created, opening...\n");
        }
    }

    //close the handle
    CloseHandle(hFile);

    return 0;

}

The WriteFile() function takes five arguments.The first argument or parameter is a handle to the file or I/O device. The second parameter is a pointer to the buffer that holds the data to be written. The third argument controls the number of bytes to be written. The fourth argument is a pointer to a DWORD value that stores the number of bytes written during a synchronous write operation. The final parameter is a pointer to an OVERLAPPED structure; this parameter is necessary if we are to perform asynchronous I/O.

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

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

    HANDLE hFile;
    char *szFileName = "kilroy.txt";
    char szBuffer[Buffer_Size];
    DWORD dwBytesWritten;
    BOOLEAN boolRVal;

    hFile = CreateFile(szFileName, GENERIC_WRITE, NULL, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

    if (hFile == INVALID_HANDLE_VALUE){
        exit(EXIT_FAILURE);
    }

    strncpy_s(szBuffer, "What strength! But don't forget there are many guys like you all over the World.", Buffer_Size);

    //let's write!
    boolRVal = WriteFile(hFile, szBuffer, strlen(szBuffer), &dwBytesWritten, NULL);

    if (boolRVal){
        printf("%s written to successfully!\n", szFileName);
        printf("%d bytes written.\n", dwBytesWritten);
    }

    //close the handle afterwards
    CloseHandle(hFile);

    return 0;

}

The WriteFile() function is designed for both synchronous and asynchronous operations. For asynchronous write operations, the handle must have been opened with CreateFile() using the FILE_FLAG_OVERLAP flag.

The ReadFile() function is a block-reading function. As with WriteFile(), we pass it in a buffer, only this time we indicate the number of bytes to be read, and the function retrieves the specified number of bytes from the file starting at the current offset.

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

int _tmain(int argc, _TCHAR* argv[])
{
    const int Buffer_Size = 256;
    char *szFileName = "kilroy.txt";
    char szInBuffer[Buffer_Size];
    HANDLE hFile;
    DWORD dwBytesRead;

    hFile = CreateFile(szFileName, GENERIC_READ, NULL, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

    if (hFile == INVALID_HANDLE_VALUE){
        exit(EXIT_FAILURE);
    }

    if (ReadFile(hFile, szInBuffer, Buffer_Size, &dwBytesRead, NULL)){
        printf("%d bytes read.\n", dwBytesRead);
        //use dwBytesRead to place a string termination character
        //at the correct location in the buffer.
        szInBuffer[dwBytesRead] = '';
        printf("%s\n", szInBuffer);
    }

    CloseHandle(hFile);

    return 0;

}

The win32 API has several functions that are useful for retrieving file information. Many of these functions require an open file handle rather than the file’s name.

The GetFileTime() function fetches three different pieces of time information concerning a file: the creation time, the last access time, and the last write time. Each of these times is stored in a FILETIME structure. Data from the FILETIME structure should be extracted via win32 function calls, namely FileTimeToSystemTime() , which converts a FILETIME structure to a SYSTEMTIME structure, which is easy to work with.

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

void DisplayTime(FILETIME structFT);

int _tmain(int argc, _TCHAR* argv[])
{
    TCHAR *szFilename = _T("kilroy.txt");
    FILETIME structFTCreate, structFTLastWrite, structFTLastAccess;
    BOOLEAN boolSuccess;

    HANDLE hFile = CreateFile(szFilename, GENERIC_READ, FILE_SHARE_WRITE, 0, OPEN_ALWAYS, 0, 0);

    if (hFile == INVALID_HANDLE_VALUE){
        printf("Error no. %d\n", GetLastError());
        exit(EXIT_FAILURE);
    }
    else {
        printf("Acquiring file times...\n");
    }

    boolSuccess = GetFileTime(hFile, &structFTCreate, &structFTLastAccess, &structFTLastWrite);

    printf("Creation time: \n");
    DisplayTime(structFTCreate);
    printf("\n");
    printf("Last write time: \n");
    DisplayTime(structFTLastWrite);

    return 0;
}

void DisplayTime(FILETIME structFT){
    SYSTEMTIME structST;
    FileTimeToSystemTime(&structFT, &structST);
    printf("Month: %d Day: %d Year: %d ", structST.wMonth, structST.wDay, structST.wYear);
    printf("%d:%d\n", structST.wHour, structST.wMinute);
}

The FILETIME structure contains two 32-bit values. The FileTimeToSystemTime() function convert this amalgamated 64 bit value into a local time suitable for output.

The GetFileSize() function returns the size of the file in bytes; however, a more suitable variant for contemporary programming is GetFileSizeEx(). The GetFileSizeEx() function accepts two arguments, the first being a handle to the file, and the second is a pointer to a LARGE_INTEGER union.

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

int _tmain(int argc, _TCHAR* argv[])
{
    HANDLE hFile;
    TCHAR *szFilename = _TEXT("kilroy.txt");
    LARGE_INTEGER structLargeInt;

    hFile = CreateFile(szFilename, GENERIC_READ, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, NULL, NULL);

    if (hFile == INVALID_HANDLE_VALUE){
        printf("Error no. %d\n", GetLastError());
        exit(EXIT_FAILURE);
    }
    else {
        GetFileSizeEx(hFile, &structLargeInt);
        //only the QuadPart member of the LARGE_INTEGER structure concerns us
        printf("Size: %d bytes.\n", structLargeInt.QuadPart);
    }

    return 0;
}
Advertisements

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/.

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.

The win32 API CreateFile() and CloseFile() Functions

The win32 CreateFile() function opens existing files and creates new ones.

Normally, file and directory names used as API function arguments can be up to 255 characters long, and the pathnames are limited to MAX_PATH characters. A period separates a file’s name from its extension, which typically indicate the file’s type.

The first argument to a win32 CreateFile() function is the lpName, which is a pointer to the null-terminated string that names the file, pipe, or other object to create.  The lpName argument is of type LPCTSTR.

The second argument to CreateFile() is dwAccess, which specifies the read and write access using the GENERIC_READ and GENERIC_WRITE flags, or a combination thereof.

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

int main(void){

    DWORD x = GENERIC_READ;
    DWORD y = GENERIC_WRITE;

    printf("%x\n", GENERIC_READ);
    printf("%x\n", x);
    printf("%x\n", GENERIC_WRITE);
    printf("%x\n", y);
    printf("%x\n", GENERIC_READ | GENERIC_WRITE);

    return 0;

}

The third argument to CreateFile() is dwShareMode, it is a bitwise OR combination of three values, 0, FILE_SHARE_READ, and FILE_SHARE_WRITE.

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

int main(void){

    DWORD read = FILE_SHARE_READ;
    DWORD write = FILE_SHARE_WRITE;
    DWORD readWrite = FILE_SHARE_READ | FILE_SHARE_WRITE;
    DWORD noShare = 0;

    printf("FILE_SHARE_READ: other processes can open this file for concurrent reads (%x)\n\n", read);
    printf("FILE_SHARE_WRITE: other processes can open this file for concurrent writes (%x)\n\n", write);
    printf("FILE_SHARE_READ | FILE_SHARE_WRITE: other processes can open this file for concurrent read/writes (%x)\n\n", readWrite);
    printf("0: no other HANDLEs can be opened on this file, even from within this process (%x)\n\n", noShare);

    return 0;

}

The lpSecurityAttributes parameter is a pointer that points to a SECURITY_ATTRIBUTES structure. If this attribute is set to NULL, the file handle cannot be inherited. A SECURITY_ATTRIBUTES structure has three fields, nLength, of type DWORD, lpSecurityDescriptor, of type LPVOID, and bInheritHandle, of type BOOL. The second field is a pointer to another structure, of type SECURITY_DESCRIPTOR.

For now, we will set the lpSecurityAttributes parameter to NULL.

The dwCreate parameter specifies whether to create a new file, overwrite an existing file, open an existing file, etc.

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

int main(void){

    printf("Create a new file; fail if the file already exists (CREATE_NEW %x)\n", CREATE_NEW);
    printf("Create a new file; overwrite the file if it already exists (CREATE_ALWAYS %x)\n", CREATE_ALWAYS);
    printf("Open an existing file; fail if the file does not exist (OPEN_EXISTING %x)\n", OPEN_EXISTING);
    printf("Open the file; create the file if it does not exist (OPEN_ALWAYS %x)\n", OPEN_ALWAYS);
    printf("Set the file length to zero; fail if the file does not exist (TRUNCATE_EXISTING %x)", TRUNCATE_EXISTING);

    return 0;

}

The dwAttrAndFlags parameter specifies the file attributes and flags of the file itself, not the file’s handle; thus, this parameter only comes into play if the file is being created and is ignored if the file already exists and is merely being opened.

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

int main(void){

    DWORD normal = FILE_ATTRIBUTE_NORMAL;
    DWORD readonly = FILE_ATTRIBUTE_READONLY;
    DWORD temp = FILE_FLAG_DELETE_ON_CLOSE;
    DWORD randAccess = FILE_FLAG_RANDOM_ACCESS;
    DWORD seqAccess = FILE_FLAG_SEQUENTIAL_SCAN;

    printf("Normal file %x\n", normal);
    printf("Read only file, cannot be deleted by application %x\n", readonly);
    printf("Temporary file %x\n", temp);
    printf("Random-access file %x\n", randAccess);
    printf("Sequential-access file %x\n", seqAccess);

    return 0;

}

In most cases, the final parameter, hTemplateFile, is set to NULL.

Windows has a single function called CloseHandle() to close handles and release system resources. This function returns a Boolean value indicating the success or failure of the function.

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

int main(void){

    const LPSECURITY_ATTRIBUTES default_security = NULL;

    HANDLE ourHandle = CreateFile(
        _T("theTest.txt"),
        GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ,
        default_security,
        CREATE_ALWAYS,
        FILE_ATTRIBUTE_NORMAL,
        NULL
        );

    CloseHandle(ourHandle);

    return 0;

}

Note the use of the _T() macro for converting the char * string literal to the correct format.

Introduction to Creating Files on Windows using C

NTFS is Window’s go-to filesystem that supports encryption, fault tolerance, compression, and large files. In addition to NTFS, the FAT32 filesystem is often seen on memory sticks.

Unlike in *nix, the pathname separator is the backslash, although the forward slash can be used as well. In general, we should use backslashes just to be safe. Likewise, the full pathname of a file on disk starts with the drive name, such as C: or D:. Finally, filenames are case-insensitive. 

The CreateFile() function opens existing files and creates new ones. The CreateFile() function accepts seven arguments. The second, third, fifth and sixth arguments are of type DWORD; a WORD is an unsigned 16 bit integer, and a DWORD is double that, at 32 bits. Note that the a DWORD is always 32 bits long, even on current 64 bit machines.

The first argument is of type LPCTSTR, which is a const char pointer. LPCTSTR supports unicode (16-bit, rather than 8 bit) character strings as well as ASCII (8 bit) character strings. Pretty much any data type with STR in it represents a string.  Note that when using Visual Studio, we should change our project’s character properties to Not Set. The easiest way to do this is by pressing Alt-F7 and then setting Character Set under Project Defaults to Not Set. 

Essentially, the first argument of type LPCTSTR is a pointer to a null-terminated string representing the pathname, which is normally limited to 260 characters. The fourth argument is of type LPSECURITY_ATTRIBUTES, which is a data structure.

The second argument, which is a DWORD, an unsigned 32 bit integer, accepts either GENERIC_READ or GENERIC_WRITE. To open a file for read/write we can use the bitwise or operator |, as in GENERIC_READ | GENERIC_WRITE.

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

int main(void){

printf("GENERIC READ = 0x%x\n", GENERIC_READ);
printf("GENERIC WRITE = 0x%x\n", GENERIC_WRITE);
printf("READ + WRITE = 0x%x\n", GENERIC_READ | GENERIC_WRITE);

printf("Press any key to continue...");

getchar();

return 0;

}

The third argument is the share mode. If this is set to 0, no other processes, including the one making the call, can access the file. If we specify FILE_SHARE_READ, other processes can access the file for concurrent read access. FILE_SHARE_WRITE allows concurrent writing to the file.

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

int main(void){

//one, basically
printf("FILE_SHARE_READ = 0x%x\n", FILE_SHARE_READ);
//two, basically
printf("FILE_SHARE_WRITE = 0x%x\n", FILE_SHARE_WRITE);

printf("Press any key to continue...");

getchar();

return 0;

}

The SECURITY_ATTRIBUTES structure contains three members, one of which is a pointer to a SECURITY_DESCRIPTOR structure. For now, we will just pass NULL as the argument. 

The fifth argument specifies whether to create, overwrite, open, or truncate the file. If we use CREATE_NEW here, it will create a new file, and more importantly, it will fail if the file already exists. If we specify CREATE_ALWAYS, however, the system will instead overwrite any preexisting files. Likewise, OPEN_EXISTING will fail if the file doesn’t already exist, but OPEN_ALWAYS will go go ahead and create the file if it hasn’t been created already. 

The sixth argument is used to define file attributes and flags for the file. 

 

The CreateFile() function either returns a HANDLE to an open file object, or else an INVALID_HANDLE_VALUE in the event of failure. On current machines, a HANDLE is an unsigned 64 bit value. 

#include <stdio.h>

int main(void){

HANDLE ourHandle;


ourHandle = CreateFile("newfile.txt", GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);

if(ourHandle==INVALID_HANDLE_VALUE){
printf("Problem creating file.\n");
printf("Error %x\n", GetLastError());
} else {
printf("File created successfully.\n");
}

printf("Press any key to continue...");

getchar();

return 0;

}

Although Windows closes all open handles on exit, it’s a good idea to close handles no matter what using the CloseHandle() function. Note that the CloseHandle() function returns a Boolean value indicating success or failure.

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

int main(void){

HANDLE theHandle;
LPCTSTR filepath = "another.txt";
bool closed;

theHandle = CreateFile(filepath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

if(theHandle!=INVALID_HANDLE_VALUE){
printf("%s created successfully\n", filepath);
}

closed = CloseHandle(theHandle);

if(closed){
printf("HANDLE to %s now closed.\n", filepath);
}

printf("Press any key to continue...");

getchar();

return 0;

}

The win32 CreateFile() function is a bit more complicated than the *nix open() function, which can take as little as two arguments, with the first argument being the pathname and the second argument being the flags for reading or writing the file or directory, and an optional third argument to specify file and directory permissions.