
本文介绍通过ReadDirectoryChangesW()来编写一个监视目录变化的程序。
实时监控目录和目录中的文件,可以有效地发现文件的变化。就像在当地安装一样IIS服务器,并建立一个网站平台,有时被黑客篡改,程序员无法及时恢复被篡改的页面,导致非常糟糕的影响。如果你能及时发现网页被篡改,并及时恢复原始页面,那么该怎么办呢?
以下是如何监控目录和目录下文件的变化的简单例子。首先要知道的函数是ReadDirectoryChangesW()其定义如下:
BOOLReadDirectoryChangesW(HANDLEhDirectory,LPVOIDlpBuffer,DWORDnBufferLength,BOOLbWatchSubtree,DWORDdwNotifyFilter,LPDWORDlpBytesReturned,LPOVERLAPPEDlpOverlapped,LPOVERLAPPED_COMPLETION_ROUTINElpCompletionRoutine);参数说明如下。
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"BOOLWINAPIReadDirectoryChangesW(__inHANDLEhDirectory,__out_bcount_part(nBufferLength,*lpBytesReturned)LPVOIDlpBuffer,__inDWORDnBufferLength,__inBOOLbWatchSubtree,__inDWORDdwNotifyFilter,__outLPDWORDlpBytesReturned,__inoutLPOVERLAPPEDlpOverlapped,__in_optLPOVERLAPPED_COMPLETION_ROUTINElpCompletionRoutine);DWORDWINAPIThreadProc(LPVOIDlpParam){BOOLbRet=FALSE;BYTEBuffer[1024]={0};FILE_NOTIFY_INFORMATION*pBuffer=(FILE_NOTIFY_INFORMATION*)Buffer;DWORDBytesReturned=0;HANDLEhFile=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){return1;}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){charszFileName[MAX_PATH]={0};//宽字符转换多字节WideCharToMultiByte(CP_ACP,0,pBuffer->FileName,pBuffer->FileNameLength/2,szFileName,MAX_PATH,NULL,NULL);switch(pBuffer->Action){//添加caseFILE_ACTION_ADDED:{printf("添加:%s\r\n",szFileName);break;}//删除caseFILE_ACTION_REMOVED:{printf("删除:%s\r\n",szFileName);break;}//修改caseFILE_ACTION_MODIFIED:{printf("修改:%s\r\n",szFileName);break;}//重命名caseFILE_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){caseFILE_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;}caseFILE_ACTION_RENAMED_NEW_NAME:{printf("重命名(new):%s\r\n",szFileName);}}}}CloseHandle(hFile);return0;}intmain(intargc,char*argv[]){HANDLEhThread=CreateThread(NULL,0,ThreadProc,NULL,0,NULL);if(hThread==NULL){return-1;}WaitForSingleObject(hThread,INFINITE);CloseHandle(hThread);return0;}连接和运行程序编译E盘下操作简单,查看程序E如图1所示。

图1
对于目录监控的例子,可以将其改为一个简单的文件防篡改程序。首先备份要监控的文件目录,然后监控文件目录。如果修改了文件,请使用备份目录下的指定文件恢复修改后的文件。