我有一个应用程序需要通过ReadDirectoryChangesW
监视主驱动器的文件更改。但是,启用UAC时,它不起作用。
所有Windows API调用都成功,但我没有收到任何更改的通知。
我可以通过单独监视根目录中的每个目录来解决这个问题,但这是一个问题,因为如果目录太多,它可能会导致蓝屏。
是否有可行的方法绕过UAC并在整个主驱动器上接收文件更改通知?
相关的CreateFile
和ReadDirectoryChangesW
如下。如果它不起作用,directory
是C:\。如果我监视任何辅助驱动器(即E:\,F:\,G:\)它按预期工作。没有一个调用返回错误。
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);
有趣的是,.NET System.IO.FileSystemWatcher
确实正常工作,它使用的功能和参数与我使用的完全相同,但行为正确。
答案 0 :(得分:1)
首先,最好是使用ReadDirectoryChangesW
API运行提升的应用程序为您的app创建清单文件,并将requireAdministrator
设置为requestedExecutionLevel
级别。请查看here以供参考。
如果您正在使用,请尝试从FILE_SHARE_WRITE
来电中删除CreateFile
。
另一种选择是让您的程序作为服务运行,我不确定这对您的需求有多适用。您可以发布一些代码,说明如何获取文件句柄以及您传递给ReadDirectoryChangesW
答案 1 :(得分:1)
这是一些有效的测试代码,供将来参考。
#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;
}
答案 2 :(得分:0)
您可以像这样自行调整流程的权限:
// 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);
}
}
}
这也适用于非特权进程(例如普通用户进程)。