Reputation: 13973
I have an application that needs to monitor the primary drive for file changes via ReadDirectoryChangesW
. However, when UAC is enabled, it doesn't work.
All of the Windows API calls succeed, but I'm not notified of any changes.
I can work around this by individually monitoring each directory in the root, but this is a problem, because it can potentially cause a blue screen if there are too many directories.
Is there an acceptable way to get around UAC and receive file change notifications on the entire primary drive?
The relevant CreateFile
and ReadDirectoryChangesW
is below. In the case where it doesn't work, directory
is C:\. If I monitor any secondary drive (i.e. E:\, F:\, G:\) it works as expected. None of the calls return errors.
HANDLE fileHandle = CreateFileW(directory.c_str(), FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);
BOOL success = ReadDirectoryChangesW(fileHandle, watched.buffer.data(),
watched.buffer.size(), TRUE,
FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE,
NULL, &watched.overlapped, NULL);
Interestingly, the .NET System.IO.FileSystemWatcher
does work correctly, and it uses the exact same functions and parameters as I'm using, but it behaves correctly.
Upvotes: 3
Views: 985
Reputation: 36318
Here's some working test code, for future reference.
#include <Windows.h>
#include <stdio.h>
int main(int argc, char ** argv)
{
HANDLE filehandle;
BYTE buffer[65536];
DWORD dw;
FILE_NOTIFY_INFORMATION * fni;
OVERLAPPED overlapped = {0};
overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (overlapped.hEvent == NULL)
{
printf("CreateEvent: %u\n", GetLastError());
return 1;
}
filehandle = CreateFile(L"C:\\",
FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
NULL);
if (filehandle == INVALID_HANDLE_VALUE)
{
printf("CreateFile: %u\n", GetLastError());
return 1;
}
for (;;)
{
if (!ReadDirectoryChangesW(filehandle, buffer, sizeof(buffer),
TRUE,
FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE,
NULL, &overlapped, NULL))
{
printf("ReadDirectoryChangesW: %u\n", GetLastError());
return 1;
}
printf("Queued OK.\n");
if (!GetOverlappedResult(filehandle, &overlapped, &dw, TRUE))
{
printf("GetOverlappedResult: %u\n", GetLastError());
return 1;
}
printf("%u bytes read.\n", dw);
fni = (FILE_NOTIFY_INFORMATION *)buffer;
for (;;)
{
printf("Next entry offset = %u\n", fni->NextEntryOffset);
printf("Action = %u\n", fni->Action);
printf("File name = %.*ws\n",
fni->FileNameLength / 2,
fni->FileName);
if (fni->NextEntryOffset == 0) break;
fni = (FILE_NOTIFY_INFORMATION *)
(((BYTE *)fni) + fni->NextEntryOffset);
}
}
printf("All done\n");
return 0;
}
Upvotes: 1
Reputation: 43575
You can adjust the privileges of your process yourself like this:
// enable the required privileges for this process
LPCTSTR arPrivelegeNames[] = { SE_BACKUP_NAME,
SE_RESTORE_NAME,
SE_CHANGE_NOTIFY_NAME
};
for (int i=0; i<(sizeof(arPrivelegeNames)/sizeof(LPCTSTR)); ++i)
{
CAutoGeneralHandle hToken;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, hToken.GetPointer()))
{
TOKEN_PRIVILEGES tp = { 1 };
if (LookupPrivilegeValue(NULL, arPrivelegeNames[i], &tp.Privileges[0].Luid))
{
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);
}
}
}
This works also for non-privileged processes (a.k.a. normal user processes).
Upvotes: 0
Reputation: 4775
First it is best for applications that use the ReadDirectoryChangesW
API to run elevated make a manifest file for you app and set requireAdministrator
as the requestedExecutionLevel
level. Check here for reference.
Try removing FILE_SHARE_WRITE
from the CreateFile
call if you are using it.
Another option is to make your program run as a service, im not sure how applicable this is to your needs. You could post some code as to how you are getting the file handle and what are you passing to ReadDirectoryChangesW
Upvotes: 1