File Locking with win32

Windows provides four functions for locking and unlocking files. The LockFIle() and LockFileEx() functions engage locks, and the Unlock() and UnlockFileEx() functions remove locks.

The LockFile() function takes five arguments and returns a Boolean value. The first argument is a handle to the file. The second argument is a  DWORD value indicating the low-order bits of the starting offset in the file where the lock should begin. The third argument is a DWORD value indicating the high-order bits of the starting offset in the file where the lock should begin; we will set this value to 0. The fourth argument is the low-order bits of the byte length in the file to be locked. The fifth argument is the high-order bits of the byte length in the file to be locked, we will set this to 0, as well.

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

int main(void){

	HANDLE hFileHandle;
	BOOL bSuccess;
	char *szFilename = "testing.txt";
	char szBuffer[256];

	//create new file
	//with both reading and writing authority
	//and reading and writing sharing
	hFileHandle = CreateFile(szFilename, GENERIC_READ | GENERIC_WRITE,
		FILE_SHARE_READ | FILE_SHARE_WRITE, 0, CREATE_ALWAYS, 0, 0);

	if(hFileHandle == INVALID_HANDLE_VALUE){
		printf("Error creating handle to new file.\n");
		exit(EXIT_FAILURE);
	}

	//lock the file for the buffer length
	bSuccess = LockFile(hFileHandle, 0, 0, sizeof(szBuffer), 0);
	if(bSuccess){
		printf("File lock on %s secured.\n", szFilename);
	}

	//now let's release the lock
	bSuccess = UnlockFile(hFileHandle, 0, 0, sizeof(szBuffer), 0);

	if(bSuccess){
		printf("File lock on %s released.\n", szFilename);
	}

	//close the handle
	CloseHandle(hFileHandle);
	return 0;

}

We can see here how easy it is to lock and later unlock a range of bytes. Locks cannot overlap a previously locked region, and locks are not inherited by child processes. Our next program will demonstrate this by using a bit of magic via the GetModuleFileName() function, which will fetch us the name of the executable that created the current process.

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


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

	//in parent process
	if(!(argv[1])){
		TCHAR szFileName[512];

		//create file
		HANDLE hCreate = CreateFile(_T("test.txt"), GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, 0, 0);
		CloseHandle(hCreate);

		//get executable for this program
		GetModuleFileName(NULL, szFileName, _tcslen(szFileName));
		_tprintf(_T("Filename for this executable is %s.\n"), szFileName);
		
		_tcscat_s(szFileName, _T(" childprocess"));

		//create child process
		STARTUPINFO structSI;
		PROCESS_INFORMATION structPI;

		GetStartupInfo(&structSI);

		CreateProcess(
			NULL,
			szFileName, //command line
			0, //process attributes
			0, //thread attributes
			FALSE, //handle inheritance is false
			0, //no creation flags
			NULL, //use parent's environment
			NULL, //use parent's directory
			&structSI,
			&structPI
			);

		HANDLE hFileHandle = CreateFile(_T("test.txt"), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);

		bSuccess = LockFile(hFileHandle, 0, 0, 1000, 0);

		if(!bSuccess){
			printf("Lock error %d in process %d.\n", GetLastError(), GetCurrentProcessId());
		} else {
			printf("File locked in process %d.\n", GetCurrentProcessId());
		}

		//wait for child process to close
		WaitForSingleObject(structPI.hProcess, INFINITE);
		//close this process's handle to the child process
		CloseHandle(structPI.hProcess);

		//unlock file
		if(UnlockFile(hFileHandle, 0, 0, 1000, 0)){
			printf("File unlocked in parent process.\n");
		}

		CloseHandle(hFileHandle);

	} else {
		printf("In child process (%d).\n", GetCurrentProcessId());

		//sleep to make sure parent process gets lock first
		Sleep(1000);

		HANDLE hFileHandle = CreateFile(_T("test.txt"), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);

		//this shouldn't work
		bSuccess = LockFile(hFileHandle, 0, 0, 1000, 0);
		
		if(!bSuccess){
			printf("Lock error %d in process %d.\n", GetLastError(), GetCurrentProcessId());
		} 

		CloseHandle(hFileHandle);

	}

	return 0;
}

The LockFileEx() and UnlockFileEx() functions enable us to block on lock requests. If the file handle is opened for asynchronous I/O, then by default LockFileEx() will operate asynchronously, unless the LOCKFILE_EXCLUSIVE_LOCK flag is specified. If the file handle is not opened for asynchronous I/O, then LockFileEx() blocks until the lock is granted or an error is given.

The LockFileEx() function has six parameters. The first parameter is the file handle for setting the lock on. The second parameter lets us set one or both flags, LOCKFILE_FAIL_IMMEDAITELY and LOCKFILE_EXLUSIVE_LOCK. The LOCKFILE_FAIL_IMMEDIATELY flag controls what happens when the LockFileEx() function tries to lock a range of bytes that already has been locked by another process. If this flag is specified, then LockFileEx() returns immediately with a lock violation, otherwise LockFileEx() will wait for the requested block to become available.

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

int _tmain(int argc, _TCHAR* argv[])
{
	
	printf("LOCKFILE_FAIL_IMMEDIATELY = %d\n", LOCKFILE_FAIL_IMMEDIATELY);
	printf("LOCKFILE_EXCLUSIVE_LOCK = %d\n", LOCKFILE_EXCLUSIVE_LOCK);
	printf("Both = %d\n", LOCKFILE_FAIL_IMMEDIATELY | LOCKFILE_EXCLUSIVE_LOCK);

	return 0;
}

While the LockFile() function by defaults obtains an exclusive lock, the LockFileEx() function get a shared lock unless the flag LOCKFILE_EXCLUSIVE_LOCK is specified.

The sixth and final parameter for LockFileEx() is a pointer to an OVERLAPPED structure, that contains two fields we can set, one that indicates what offset we should begin the lock from, and the other being a handle to an event to trigger when the operation has been completed. We will use ZeroMemory() to initialize the OVERLAPPED structure.

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

int _tmain(int argc, _TCHAR* argv[])
{
	BOOL bSuccess;
	HANDLE hFileHandle;
	TCHAR *filename = _T("thefile.txt");
	OVERLAPPED structOverlap;

	hFileHandle = CreateFile(filename, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0);

	if(hFileHandle == INVALID_HANDLE_VALUE){
		printf("Error creating file.\n");
		exit(EXIT_FAILURE);
	} else {
		_tprintf(_T("File %s created.\n"), filename);
	}

	//initialize structure
	ZeroMemory(&structOverlap, sizeof(structOverlap));

	bSuccess = LockFileEx(hFileHandle, LOCKFILE_EXCLUSIVE_LOCK, 0, 100, 0, &structOverlap);

	if(bSuccess){
		_tprintf(_T("Lock on file %s.\n"), filename);
	}

	//unlock file
	bSuccess = UnlockFileEx(hFileHandle, 0, 100, 0, &structOverlap);

	if(bSuccess){
		printf("File unlocked.\n");
	}

	return 0;
}
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s