I have implemented a program that traces changes in a given folder using ReadDirectoryChangesW
in overlapped mode with a completion routine.
It works good.
I am planning to reconstruct it to make a more universal library - the completion routine should save file changes in a queue.
Question: how can I pass a parameter to the completion routine cr
, that will point to my queue for use in cr
?
When the completion routine cr
called, and file changes are displayed, I need to continue tracing: see line DoRDC = true; // RESTART WATCHING
.
It is possible because it runs in a loop while (_kbhit() == 0) {
.
After reconstruction I will eliminate this loop.
My code is below. The main function - WatchDirectory();
void DisplayFileInfo(LPVOID FileInfoRecords, DWORD FileInfoLength) {
// Display file changes. Called from completion routine "cr".
//ActionText[0] = L"-";
FILE_NOTIFY_INFORMATION* fi = (FILE_NOTIFY_INFORMATION*)FileInfoRecords;
if (FileInfoLength == 0) {
std::wcout << L"No file info!" << std::endl;
return;
}
int RecNum = 1;
DWORD OffsetToNext = 0;
do {
fi = (FILE_NOTIFY_INFORMATION*)(((char*)fi) + OffsetToNext);
std::wstring wfname;
std::wstring wActionName;
if ((fi->Action < MIN_ACTION_CODE) || (fi->Action > MAX_ACTION_CODE))
wActionName = L"Unknown code";
else
wActionName = ActionText[fi->Action];
int slen = fi->FileNameLength / sizeof(WCHAR);
wfname.assign(fi->FileName, slen);
std::wcout << L"Rec " << RecNum << L": Action = " << wActionName << L"(" << fi->Action << L") Offset = " << fi->NextEntryOffset <<
L" File = " << wfname << std::endl;
OffsetToNext = fi->NextEntryOffset;
assert(RecNum < 50);
RecNum++;
} while (OffsetToNext > 0);
}
LPVOID lpBuffer = NULL;
bool DoRDC = true;
VOID WINAPI cr(DWORD dwErrorCode,
DWORD dwNumberOfBytesTransfered,
LPOVERLAPPED lpOverlapped) {
// Completion routine
DoRDC = true; // RESTART WATCHING
std::wcout << L" ------------- lpCompletionRoutine --------------- " << std::endl;
std::wcout << L" ErrCode = " << dwErrorCode
<< L" BytesTransferred = " << dwNumberOfBytesTransfered << std::endl;
if (dwErrorCode != 0) {
return;
}
std::wcout << L"..............." << std::endl;
DisplayFileInfo(lpBuffer, dwNumberOfBytesTransfered);
}
bool WatchDirectory(LPTSTR lpDir)
{
DWORD dwWaitStatus;
HANDLE dwChangeHandles[2];
TCHAR lpDrive[4];
TCHAR lpFile[_MAX_FNAME];
TCHAR lpExt[_MAX_EXT];
HANDLE hDir = INVALID_HANDLE_VALUE;
std::wcout << L"3_Watching for: " << lpDir << std::endl;
_tsplitpath_s(lpDir, lpDrive, 4, NULL, 0, lpFile, _MAX_FNAME, lpExt, _MAX_EXT);
lpDrive[2] = (TCHAR)'\\';
lpDrive[3] = (TCHAR)'\0';
int EventsNumber = 1;
DWORD Flags = FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_FILE_NAME;
hDir = CreateFile(lpDir, GENERIC_READ, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);
if (hDir == INVALID_HANDLE_VALUE) {
DWORD err = GetLastError();
std::wcout << L"ERROR: CreateFile(folder to trace) function failed = " << err << std::endl;
return false;
///ExitProcess(err);
}
DWORD nBufferLength = 30000;
lpBuffer = malloc(nBufferLength);
BOOL bWatchSubtree = TRUE;
DWORD BytesReturned = 0;
HANDLE hEvent = CreateEvent(NULL, FALSE /*manual reset = true*/, FALSE /* initial state*/, NULL);
if (hEvent == NULL) {
printf("\n Cannot create event.\n");
CloseHandle(hDir);
return false;
//ExitProcess(GetLastError());
}
// =============================================================
OVERLAPPED Overlapped;
//---Overlapped.hEvent = hEvent;
LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine = cr;
// =============================================================
DoRDC = true;
while (_kbhit() == 0) {
if (DoRDC) {
ZeroMemory(&Overlapped, sizeof(Overlapped));
BOOL res_rdc = ReadDirectoryChangesW(hDir,
lpBuffer,
nBufferLength,
bWatchSubtree,
Flags,
&BytesReturned,
&Overlapped,
lpCompletionRoutine);
bool ok_rdc = (res_rdc != 0);
if (ok_rdc) {
}
else {
DWORD err = GetLastError();
std::wcout << L"ReadDirectoryChangesW error = " << err << std::endl;
}
DoRDC = false;
}
// Wait for notification.
//std::wcout << L"SleepEx" << std::endl;
SleepEx(1000, TRUE);
}
CloseHandle(hEvent);
CloseHandle(hDir);
free(lpBuffer);
}
Define a custom struct
that contains an OVERLAPPED
as its 1st data member, and any additional members you need, such as (a pointer to) your queue.
Then, pass a pointer to that struct
in the LPOVERLAPPED
parameter of ReadDirectoryChangesW()
. cr()
can then cast the LPOVERLAPPED
pointer back to your struct
to access its members. This works because the 1st data member of a struct is guaranteed to have the same memory address as the struct itself.
This is a common approach to passing around custom data in overlapped I/O operations.
For example:
struct MY_OVERLAPPED : OVERLAPPED
{
// extra members as needed...
};
VOID WINAPI cr(DWORD dwErrorCode,
DWORD dwNumberOfBytesTransfered,
LPOVERLAPPED lpOverlapped) {
...
MY_OVERLAPPED *lpMyOverlapped = static_cast<MY_OVERLAPPED*>(lpOverlapped);
// use lpMyOverlapped members as needed...
...
}
bool WatchDirectory(LPTSTR lpDir)
{
...
MY_OVERLAPPED Overlapped;
// populate Overlapped members as needed...
...
BOOL res_rdc = ReadDirectoryChangesW(..., &Overlapped, cr);
...
}