Good evening!
I need to get information about new files in certain directory (Windows 10, C++).
To do this:
I call ReadDirectoryChangesW in overlapped mode and point an event to be signaled when ReadDirectoryChangesW will finish
wait until event will be signalled (WaitForMultipleObjects)
call GetOverlappedResult to get ReadDirectoryChangesW info about new files.
All actions above are repeated in a loop.
It works well, but if I create two or more files simultaneously (copy several files and paste them in the Windows Explorer), then in the step (3) I get info about one file only.
But if I rename a file, then I correctly get from ReadDirectoryChangesW two records – about old and new file names.
Buffer (FILE_NOTIFY_INFORMATION) for ReadDirectoryChangesW likely does not overflow – I allocate 10 kb, information about new files is about 50-200 bytes (their paths and names are short enough).
If I do copy/paste 1 file, then 1 file etc, than each copying is reported normally.
It seems ReadDirectoryChangesW/GetOverlappedResult return info about 1 file only if new files appear "fast" (copy/paste several files), but how can I get info about other files ?
Here is my code:
#include <iostream>
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <tchar.h>
#include <assert.h>
void RefreshDirectory(LPTSTR, HANDLE, DWORD);
void RefreshTree(LPTSTR, HANDLE, DWORD);
void WatchDirectory(LPTSTR);
int _tmain(int argc, TCHAR* argv[])
{
if (argc != 2)
{
_tprintf(TEXT("Usage: %s <dir>\n"), argv[0]);
return 0;
}
WatchDirectory(argv[1]);
}
/*#define FILE_ACTION_ADDED 0x00000001
#define FILE_ACTION_REMOVED 0x00000002
#define FILE_ACTION_MODIFIED 0x00000003
#define FILE_ACTION_RENAMED_OLD_NAME 0x00000004
#define FILE_ACTION_RENAMED_NEW_NAME 0x00000005 */
const WCHAR * ActionText[] = { L"-", L"file added", L"file removed", L"file modified", L"file ranamed (old)", L"file renamed (new)" };
#define MIN_ACTION_CODE 1
#define MAX_ACTION_CODE 5
void DisplayFileInfo(LPVOID FileInfoRecords, DWORD FileInfoLength) {
//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 {
// next record
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);
// output information about files changes
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);
}
void WatchDirectory(LPTSTR lpDir)
{
DWORD dwWaitStatus;
HANDLE dwChangeHandles[2];
TCHAR lpDrive[4];
TCHAR lpFile[_MAX_FNAME];
TCHAR lpExt[_MAX_EXT];
std::wcout << L"Watchng for: " << lpDir << std::endl;
// split the path from parameters
_tsplitpath_s(lpDir, lpDrive, 4, NULL, 0, lpFile, _MAX_FNAME, lpExt, _MAX_EXT);
lpDrive[2] = (TCHAR)'\\';
lpDrive[3] = (TCHAR)'\0';
int EventsNumber = 1;
// flags for ReadDirectoryChangesW
DWORD Flags = FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_FILE_NAME;
// create a handle for a directory to look for
HANDLE 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;
ExitProcess(err);
}
// --- initialyze data for ReadDirectoryChangesW ---
DWORD nBufferLength = 10000;
LPVOID lpBuffer = malloc(nBufferLength);
BOOL bWatchSubtree = TRUE;
DWORD BytesReturned = 0;
// --- create an event for "Overlapped" ---
HANDLE hEvent = CreateEvent(NULL, FALSE /*manual reset = true*/, FALSE /* initial state*/, NULL);
if (hEvent == NULL)
{
printf("\n Cannot create event.\n");
ExitProcess(GetLastError());
}
bool first = true;
while (TRUE)
{
// Wait for notification.
// =============================================================
OVERLAPPED Overlapped;
Overlapped.hEvent = hEvent;
LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine = NULL;
// request info about changes in overlapped-mode
BOOL res_rdc = ReadDirectoryChangesW(hDir,
lpBuffer,
nBufferLength,
bWatchSubtree,
Flags,
&BytesReturned,
&Overlapped,
lpCompletionRoutine);
bool ok_rdc = (res_rdc != 0);
if (ok_rdc) {
if (first)
printf("\nWaiting for notification...\n");
// wait for overlapped-function
dwChangeHandles[0] = Overlapped.hEvent;
dwWaitStatus = WaitForMultipleObjects(EventsNumber, dwChangeHandles,
FALSE, 3000);
switch (dwWaitStatus) {
case WAIT_OBJECT_0: {
printf("\n WAIT_OBJECT_0 .\n");
DWORD NumberOfBytesTransferred = 0;
BOOL ok_gor = GetOverlappedResult(hDir, &Overlapped, &NumberOfBytesTransferred, FALSE);
if (ok_gor == 0) {
//
DWORD err = GetLastError();
if (err == ERROR_IO_INCOMPLETE)
std::wcout << L"Err (GetOverlappedResult) = ERROR_IO_INCOMPLETE" << std::endl;
else
std::wcout << L"Err (GetOverlappedResult) = " << err << std::endl;
}
else {
// overplapped function(ReadDirectoryChangesW) exits normally
std::wcout << L"GetOverlappedResult = OK. Bytes = " << NumberOfBytesTransferred << std::endl;
// display files changes, received from ReadDirectoryChangesW
DisplayFileInfo(lpBuffer, NumberOfBytesTransferred /*FileInfoLength*/);
}
break;
}
case WAIT_TIMEOUT:
//
if (first)
printf("\nNo changes in the timeout period.\n");
break;
default:
printf("\n ERROR: Unhandled dwWaitStatus.\n");
ExitProcess(GetLastError());
break;
}
}
else {
// ошибка
DWORD err = GetLastError();
std::wcout << L"ReadDirectoryChangesW error = " << err << std::endl;
}
// =============================================================
first = false;
}
free(lpBuffer);
}