本文介绍通过ReadDirectoryChangesW()来编写一个监视目录变化的程序。
对目录及目录中的文件实时监控,可以有效地发现文件被改动的情况。就好像在本地安装IIS服务器,并搭建一个网站平台,有时候会遭到黑客的篡改,而程序员无法及时地恢复被篡改的页面,导致出现了非常不好的影响。如果能及时地发现网页被篡改,并及时地恢复本来的页面就好了,那么该如何做呢?
下面通过一个简单的例子来介绍如何监控某目录及目录下文件的变动情况。首先需要了解的函数为ReadDirectoryChangesW(),其定义如下:
- BOOL ReadDirectoryChangesW(
- HANDLE hDirectory,
- LPVOID lpBuffer,
- DWORD nBufferLength,
- BOOL bWatchSubtree,
- DWORD dwNotifyFilter,
- LPDWORD lpBytesReturned,
- LPOVERLAPPED lpOverlapped,
- LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
- );
参数说明如下。
hDirectory:该参数指向一个要监视目录的句柄。该目录需要用 FILE_LIST_DIRECTORY的访问权限打开。
lpBuffer:该参数指向一个内存的缓冲区,它用来存放返回的结果。结果为一个 FILE_NOTIFY_INFORMATION 的数据结构。
nBufferLength:表示缓冲区的大小。
bWatchSubtree:该参数为 TRUE 时,表示监视指定目录下的文件及子目录下的文件操作。如果该参数为 FALSE,则只监视指定目录下的文件,不包含子目录下的文件。
dwNotifyFilter:该参数指定要返回何种文件变更后的类型,该参数的常量值参见 MSDN。
lpBytesReturned:该参数返回传给 lpBuffer 结果的字节数。
lpOverlapped:该参数执行一个 OVERLAPPED 结构体,该结构体用于异步操作,否则该数据为 NULL。
ReadDirectoryChangesW()函数的使用非常简单,下面通过一个例子介绍其使用。该例子是对E盘目录进行监控,将程序编写完成后对E盘进行简单的文件操作,以观察程序的输出结构。完整的代码如下:
- #include <windows.h>
- #include <stdio.h>
- extern "C"
- BOOL
- WINAPI
- ReadDirectoryChangesW(
- __in HANDLE hDirectory,
- __out_bcount_part(nBufferLength, *lpBytesReturned) LPVOIDlpBuffer,
- __in DWORD nBufferLength,
- __in BOOL bWatchSubtree,
- __in DWORD dwNotifyFilter,
- __out LPDWORD lpBytesReturned,
- __inout LPOVERLAPPED lpOverlapped,
- __in_opt LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
- );
- DWORD WINAPI ThreadProc(LPVOID lpParam)
- {
- BOOL bRet = FALSE;
- BYTE Buffer[1024] = { 0 };
- FILE_NOTIFY_INFORMATION *pBuffer = (FILE_NOTIFY_INFORMATION *)Buffer;
- DWORD BytesReturned = 0;
- HANDLE hFile = CreateFile("e:\\",
- FILE_LIST_DIRECTORY,
- FILE_SHARE_READ|FILE_SHARE_DELETE|FILE_SHARE_WRITE,
- NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL);
- if ( INVALID_HANDLE_VALUE == hFile )
- {
- return 1;
- }
- printf("monitor... \r\n");
- while ( TRUE )
- {
- ZeroMemory(Buffer, 1024);
- bRet = ReadDirectoryChangesW(hFile,&Buffer,sizeof(Buffer),TRUE,
- FILE_NOTIFY_CHANGE_FILE_NAME | // 修改文件名
- FILE_NOTIFY_CHANGE_ATTRIBUTES | // 修改文件属性
- FILE_NOTIFY_CHANGE_LAST_WRITE , // 最后一次写入
- &BytesReturned,NULL, NULL);
- if ( bRet == TRUE )
- {
- char szFileName[MAX_PATH] = { 0 };
- // 宽字符转换多字节
- WideCharToMultiByte(CP_ACP,0,pBuffer->FileName,
- pBuffer->FileNameLength / 2,szFileName,
- MAX_PATH,NULL,NULL);
- switch(pBuffer->Action)
- {
- // 添加
- case FILE_ACTION_ADDED:
- {
- printf("添加 : %s\r\n", szFileName);
- break;
- }
- // 删除
- case FILE_ACTION_REMOVED:
- {
- printf("删除 : %s\r\n", szFileName);
- break;
- }
- // 修改
- case FILE_ACTION_MODIFIED:
- {
- printf("修改 : %s\r\n", szFileName);
- break;
- }
- // 重命名
- case FILE_ACTION_RENAMED_OLD_NAME:
- {
- printf("重命名 : %s", szFileName);
- if ( pBuffer->NextEntryOffset != 0 )
- {
- 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 \r\n", szFileName);
- break;
- }
- }
- }
- break;
- }
- case FILE_ACTION_RENAMED_NEW_NAME:
- {
- printf("重命名(new) : %s\r\n", szFileName);
- }
- }
- }
- }
- CloseHandle(hFile);
- return 0;
- }
- int main(int argc, char* argv[])
- {
- HANDLE hThread = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
- if ( hThread == NULL )
- {
- return -1;
- }
- WaitForSingleObject(hThread, INFINITE);
- CloseHandle(hThread);
- return 0;
- }
将程序编译连接并运行,在E盘下进行简单的操作,查看程序对E盘的监视输出记录,如图1所示。
图1 目录监控输出记录
对于目录监视的这个例子,可以将其改为一个简单的文件防篡改程序。首先将要监视的文件目录进行备份,然后对文件目录进行监视,如果有文件发生了修改,那么就使用备份目录下的指定文件恢复被修改的文件。