One of the other solutions mentions that much information is available in the windows SDK documentation and I have indeed used this and many other sources that are available on the internet.
This solution is Windows specific.
For my solution, I have come up with a wrapper class that I call locked_text_file_t. This class manages all the details of the file locking and providing a line based access to the unicode text file.
For my purposes, I am using this class to write exception and error logs from a soft real-ime system so the file is opened without write buffering and it is immediately closed after each write. My GUI process also opens the file using the same locked_text_file_t class and provides a 'tail' like functionality always of displaying the end of file to the user. Specifically, it displays only those messages that have been added since the GUI process startup.
--- Begin LockedTextFile.h ----------------
#include "CoreUtilsApi.h"
#include "LargeInteger.h"
#include <string>
#include "..\nUsbDataServer\ni_typedefs.h"
#include <winscard.h>
class COREUTILS_API locked_text_file_t
{
public:
locked_text_file_t ();
virtual ~locked_text_file_t ();
operator HANDLE& () {return m_hFile; };
bool OpenOrCreate (LPCWSTR szFileName);
bool OpenExisting (LPCWSTR szFileName);
void Close ();
bool Lock (DWORD nBytes, large_integer_t Offset);
bool Unlock (DWORD nBytes, large_integer_t Offset);
bool Write (const wchar_t* pwszFormat, ...);
bool Writeln (const wchar_t* pwszFormat, ...);
bool Read (LPBYTE pBuffer, DWORD nBufLen, DWORD* nBytesRead = NULL);
bool Readln (std::wstring& line, large_integer_t* liNewFilePointer = NULL); bool Readln (wstr_list_t& lines, large_integer_t* liNewFilePointer = NULL);
bool GetFilePos (large_integer_t& Offset);
bool SetFilePos (large_integer_t Offset, large_integer_t* liNewFilePointer = NULL); bool OffsetFilePos (large_integer_t Offset, large_integer_t* liNewFilePointer = NULL); bool EndFilePos (large_integer_t Offset, large_integer_t* liNewFilePointer = NULL);
protected:
bool _Open (LPCWSTR szFileName, bool bCreate);
bool _Write (LPCBYTE pBuffer, DWORD nBytes, DWORD* nBytesWritten = NULL);
HANDLE m_hFile;
private:
locked_text_file_t (locked_text_file_t&);
locked_text_file_t& operator = (const locked_text_file_t&);
};
--- End LockedTextFile.h ----------------
#include "StdAfx.h"
#include "LockedTextFile.h"
#include "StringHelpers.h"
#include <winnt.h>
using namespace std;
locked_text_file_t::locked_text_file_t ()
: m_hFile (INVALID_HANDLE_VALUE)
{
}
locked_text_file_t::~locked_text_file_t ()
{
Close();
}
bool locked_text_file_t::_Open (LPCWSTR szFileName, bool bCreate)
{
bool bOK = false;
DWORD dwLastError = 0;
bool bFileCreated = false;
DWORD nCreateAttempts = 0;
do
{
do
{
m_hFile = CreateFile( szFileName,
GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, NULL); bOK = (m_hFile != INVALID_HANDLE_VALUE);
dwLastError = GetLastError();
if (dwLastError == ERROR_SHARING_VIOLATION)
{
Sleep(0);
}
} while ((!bOK) && (dwLastError == ERROR_SHARING_VIOLATION));
if (!bOK)
{
if ((dwLastError == ERROR_FILE_NOT_FOUND) && (bCreate))
{
nCreateAttempts++;
m_hFile = CreateFile( szFileName,
GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, NULL); bOK = bFileCreated = (m_hFile != INVALID_HANDLE_VALUE);
if (!bOK)
{
Sleep(0);
}
}
}
} while ((!bOK) && (nCreateAttempts < 3));
if (bOK)
{
if (bFileCreated)
{
byte UnicodeMarker [] = { 0xFF, 0xFE };
DWORD nBytes = 2;
bOK = _Write(UnicodeMarker, nBytes);
}
large_integer_t Offset;
bOK = Lock(1, Offset);
if (bOK)
{
EndFilePos(Offset);
}
if (!bOK)
{
Close();
}
}
return bOK;
}
bool locked_text_file_t::OpenOrCreate (LPCWSTR szFileName)
{
bool bOK = false;
if (m_hFile == INVALID_HANDLE_VALUE)
{
bOK = _Open(szFileName, true);
}
return bOK;
}
bool locked_text_file_t::OpenExisting (LPCWSTR szFileName)
{
bool bOK = false;
if (m_hFile == INVALID_HANDLE_VALUE)
{
bOK = _Open(szFileName, false);
}
return bOK;
}
void locked_text_file_t::Close ()
{
if (m_hFile != INVALID_HANDLE_VALUE)
{
CloseHandle(m_hFile);
m_hFile = INVALID_HANDLE_VALUE;
}
}
bool locked_text_file_t::Lock (DWORD nBytes, large_integer_t Offset)
{
bool bOK = false;
if (m_hFile != INVALID_HANDLE_VALUE)
{
OVERLAPPED oio;
memset(&oio, 0, sizeof(oio));
oio.Offset = Offset.LowPart();
oio.OffsetHigh = Offset.HighPart();
bOK = LockFileEx(m_hFile, LOCKFILE_EXCLUSIVE_LOCK,
0, nBytes, 0, &oio); }
return bOK;
}
bool locked_text_file_t::Unlock (DWORD nBytes, large_integer_t Offset)
{
bool bOK = false;
if (m_hFile != INVALID_HANDLE_VALUE)
{
OVERLAPPED oio;
memset(&oio, 0, sizeof(oio));
oio.Offset = Offset.LowPart();
oio.OffsetHigh = Offset.HighPart();
bOK = UnlockFileEx(m_hFile, 0, nBytes, 0, &oio); }
return bOK;
}
bool locked_text_file_t::_Write (LPCBYTE pBuffer, DWORD nBytes, DWORD* pBytesWritten)
{
bool bOK = false;
DWORD LocalBytesWritten = 0;
if (m_hFile != INVALID_HANDLE_VALUE)
{
if (pBytesWritten == NULL)
{
pBytesWritten = &LocalBytesWritten;
}
bOK = WriteFile(m_hFile, pBuffer, nBytes, pBytesWritten, NULL);
}
return bOK;
}
bool locked_text_file_t::Write (const wchar_t* pwszFormat, ...)
{
bool bOK = false;
if (m_hFile != INVALID_HANDLE_VALUE)
{
va_list argList;
va_start(argList, pwszFormat);
wchar_t* pwszResult = _FormatV(pwszFormat, argList);
va_end(argList);
DWORD nBytes = (DWORD)(wcslen(pwszResult) * sizeof(wchar_t));
bOK = _Write((LPCBYTE)pwszResult, nBytes);
free(pwszResult);
}
return bOK;
}
bool locked_text_file_t::Writeln (const wchar_t* pwszFormat, ...)
{
bool bOK = false;
if (m_hFile != INVALID_HANDLE_VALUE)
{
va_list argList;
va_start(argList, pwszFormat);
wchar_t* pwszResult = _FormatV(pwszFormat, argList);
va_end(argList);
DWORD nBytes = (DWORD)(wcslen(pwszResult) * sizeof(wchar_t));
bOK = _Write((LPCBYTE)pwszResult, nBytes);
if (bOK)
{
bOK = _Write((LPCBYTE)L"\r\n", 4);
}
free(pwszResult);
}
return bOK;
}
bool locked_text_file_t::Read (LPBYTE pBuffer, DWORD nBuflen, DWORD* pBytesRead)
{
bool bOK = false;
DWORD LocalBytesRead = 0;
if (m_hFile != INVALID_HANDLE_VALUE)
{
if (pBytesRead == NULL)
{
pBytesRead = &LocalBytesRead;
}
bOK = ReadFile(m_hFile, pBuffer, nBuflen, pBytesRead, NULL);
}
return bOK;
}
bool locked_text_file_t::Readln (wstring& line, large_integer_t* liNewFilePointer)
{
bool bOK = false;
line = L"";
if (m_hFile != INVALID_HANDLE_VALUE)
{
large_integer_t liCurFilePos;
GetFilePos(liCurFilePos);
large_integer_t liEndFilePos;
EndFilePos(liEndFilePos);
GetFilePos(liEndFilePos);
SetFilePos(liCurFilePos);
DWORD BufRequired = (liEndFilePos - liCurFilePos).QuadPart();
if (BufRequired > 0)
{
LPBYTE pBuffer = (LPBYTE)malloc(BufRequired + 1); DWORD BytesRead = 0;
if (Read(pBuffer, BufRequired, &BytesRead))
{
bOK = (BufRequired == BytesRead);
}
if (bOK)
{
DWORD nChars = BytesRead / sizeof(wchar_t);
DWORD nCharsUsed = 0;
wchar_t* pCh = (wchar_t*)pBuffer;
bool End = false;
do
{
End = (((*pCh == L'\r') && (*(pCh+1) == L'\n')) || (nChars == 0));
if (!End)
{
if (*pCh == L'\n')
{
*pCh = L' ';
}
pCh++;
nChars--;
nCharsUsed++;
}
} while (!End);
wchar_t* pChNull = pCh;
while (((*pCh == L'\r') || (*pCh == L'\n')) && (nChars))
{
pCh++;
nChars--;
nCharsUsed++;
}
*pChNull = NULL;
line = (wchar_t*)pBuffer;
large_integer_t liLocalFilePointer;
if (liNewFilePointer == NULL)
{
liNewFilePointer = &liLocalFilePointer;
}
*liNewFilePointer = liCurFilePos + (nCharsUsed * sizeof(wchar_t));
SetFilePos(*liNewFilePointer);
}
free(pBuffer);
}
}
return bOK;
}
bool locked_text_file_t::Readln (wstr_list_t& lines, large_integer_t* liNewFilePointer)
{
return false;
}
bool locked_text_file_t::GetFilePos (large_integer_t& liOffset)
{
bool bOK = false;
liOffset = large_integer_t();
if (m_hFile != INVALID_HANDLE_VALUE)
{
large_integer_t liDistanceToMove;
bOK = SetFilePointerEx(m_hFile, liDistanceToMove, &((LARGE_INTEGER&)liOffset), FILE_CURRENT);
}
return bOK;
}
bool locked_text_file_t::SetFilePos (large_integer_t liOffset, large_integer_t* liNewFilePointer)
{
bool bOK = false;
if (m_hFile != INVALID_HANDLE_VALUE)
{
large_integer_t liLocalFilePointer;
if (liNewFilePointer == NULL)
{
liNewFilePointer = &liLocalFilePointer;
}
bOK = SetFilePointerEx(m_hFile, liOffset, &(LARGE_INTEGER&)*liNewFilePointer, FILE_BEGIN);
}
return bOK;
}
bool locked_text_file_t::OffsetFilePos (large_integer_t liOffset, large_integer_t* liNewFilePointer)
{
bool bOK = false;
if (m_hFile != INVALID_HANDLE_VALUE)
{
large_integer_t liLocalFilePointer;
if (liNewFilePointer == NULL)
{
liNewFilePointer = &liLocalFilePointer;
}
bOK = SetFilePointerEx(m_hFile, liOffset, &(LARGE_INTEGER&)*liNewFilePointer, FILE_CURRENT);
}
return bOK;
}
bool locked_text_file_t::EndFilePos (large_integer_t liOffset, large_integer_t* liNewFilePointer)
{
bool bOK = false;
if (m_hFile != INVALID_HANDLE_VALUE)
{
large_integer_t liLocalFilePointer;
if (liNewFilePointer == NULL)
{
liNewFilePointer = &liLocalFilePointer;
}
bOK = SetFilePointerEx(m_hFile, liOffset, &(LARGE_INTEGER&)*liNewFilePointer, FILE_END);
}
return bOK;
}
--- End LockedTextFile.cpp ----------------