监视对指定目录的更改,并将有关更改的信息打印到控制台,该功能的实现不仅可以在内核层,在应用层同样可以。程序中使用ReadDirectoryChangesW
函数来监视目录中的更改,并使用FILE_NOTIFY_INFORMATION
结构来获取有关更改的信息。
ReadDirectoryChangesW 是Windows
操作系统提供的一个函数,用于监视目录的变化。它属于Windows API
的一部分,主要用于监视文件系统中目录的修改、新增、删除等变化,并通过回调函数向应用程序提供通知。
以下是该函数的声明:
BOOL ReadDirectoryChangesW(
HANDLE hDirectory,
LPVOID lpBuffer,
DWORD nBufferLength,
BOOL bWatchSubtree,
DWORD dwNotifyFilter,
LPDWORD lpBytesReturned,
LPOVERLAPPED lpOverlapped,
LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);
其中:
- hDirectory:要监视的目录的句柄。
- lpBuffer:接收变更通知的缓冲区。
- nBufferLength:缓冲区的大小。
- bWatchSubtree:如果为 TRUE,则监视目录树中的所有目录。如果为 FALSE,则仅监视指定的目录。
- dwNotifyFilter:指定要监视的变更类型,可以是文件夹或文件的新增、删除、修改等。
- lpBytesReturned:返回实际读取到的字节数。
- lpOverlapped:用于异步操作的 OVERLAPPED 结构。
- lpCompletionRoutine:指定一个回调函数,在异步操作完成时调用。
在使用这个函数时,通常会在回调函数中处理具体的文件变更信息。ReadDirectoryChangesW
通常用于异步操作,因此在调用时需要提供一个OVERLAPPED
结构或使用同步的方式等待变更。
如下代码中使用CreateThread
函数创建一个线程,并将MonitorFileThreadProc
运行起来,此函数使用带有FILE_LIST_directory
标志的CreateFile
打开指定的目录,该标志允许该函数监视目录。并使用ReadDirectoryChangesW
函数读取目录中的更改,传递一个缓冲区来存储更改,并指定要监视的更改类型。
使用WideCharToMultiByte
函数将宽字符文件名转换为多字节文件名,并将文件名与目录路径连接以获得文件的完整路径。然后,该功能将有关更改的信息打印到控制台。
#include <stdio.h>
#include <Windows.h>
#include <tlhelp32.h>
DWORD WINAPI MonitorFileThreadProc(LPVOID lParam)
{
char *pszDirectory = (char *)lParam;
BOOL bRet = FALSE;
BYTE Buffer[1024] = { 0 };
FILE_NOTIFY_INFORMATION *pBuffer = (FILE_NOTIFY_INFORMATION *)Buffer;
DWORD dwByteReturn = 0;
HANDLE hFile = CreateFile(pszDirectory, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (INVALID_HANDLE_VALUE == hFile)
return 1;
while (TRUE)
{
ZeroMemory(Buffer, sizeof(Buffer));
// 设置监控目录回调函数
bRet = ReadDirectoryChangesW(hFile,&Buffer,sizeof(Buffer),TRUE,
FILE_NOTIFY_CHANGE_FILE_NAME | // 修改文件名
FILE_NOTIFY_CHANGE_ATTRIBUTES | // 修改文件属性
FILE_NOTIFY_CHANGE_LAST_WRITE, // 最后一次写入
&dwByteReturn, NULL, NULL);
if (TRUE == bRet)
{
char szFileName[MAX_PATH] = { 0 };
// 将宽字符转换成窄字符,宽字节字符串转多字节字符串
WideCharToMultiByte(CP_ACP,0,pBuffer->FileName,(pBuffer->FileNameLength / 2),
szFileName,MAX_PATH,NULL,NULL);
// 将路径与文件连接成完整文件路径
char FullFilePath[1024] = { 0 };
strncpy(FullFilePath, pszDirectory, strlen(pszDirectory));
strcat(FullFilePath, szFileName);
switch (pBuffer->Action)
{
case FILE_ACTION_ADDED:
{
printf("添加: %s \n", FullFilePath); break;
}
case FILE_ACTION_REMOVED:
{
printf("删除: %s \n", FullFilePath); break;
}
case FILE_ACTION_MODIFIED:
{
printf("修改: %s \n", FullFilePath); break;
}
case FILE_ACTION_RENAMED_OLD_NAME:
{
printf("重命名: %s", szFileName);
if (0 != pBuffer->NextEntryOffset)
{
FILE_NOTIFY_INFORMATION *tmpBuffer = (FILE_NOTIFY_INFORMATION *)
((DWORD)pBuffer + pBuffer->NextEntryOffset);
switch (tmpBuffer->Action)
{
case FILE_ACTION_RENAMED_NEW_NAME:
{
ZeroMemory(szFileName, MAX_PATH);
WideCharToMultiByte(CP_ACP,0,tmpBuffer->FileName,
(tmpBuffer->FileNameLength / 2),
szFileName,MAX_PATH,NULL,NULL);
printf(" -> %s \n", szFileName);
break;
}
}
}
break;
}
case FILE_ACTION_RENAMED_NEW_NAME:
{
printf("重命名(new): %s \n", FullFilePath); break;
}
}
}
}
CloseHandle(hFile);
return 0;
}
int main(int argc, char * argv[])
{
char *pszDirectory = "C:\\";
HANDLE hThread = CreateThread(NULL, 0, MonitorFileThreadProc, pszDirectory, 0, NULL);
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
return 0;
}
运行后监控C盘所有文件的变化,并输出如下信息;