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.

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;
}