本文介绍使用ReadDirectoryChangesW()编写一个监控目录变化的程序。实时监控目录和目录中的文件,可以有效发现文件被更改的情况。就好比在本地安装一个IIS服务器,搭建一个网站平台,有时候会被黑客篡改,而程序员又不能及时恢复被篡改的页面,造成非常恶劣的影响。如果能及时发现网页被篡改,及时恢复原页面就好了,那么怎么办呢?下面以一个简单的例子来介绍如何监控某个目录和目录下文件的变化。首先需要了解的函数是ReadDirectoryChangesW(),其定义如下:BOOLReadDirectoryChangesW(HANDLEhDirectory,LPVOIDlpBuffer,DWORDnBufferLength,BOOLbWatchSubtree,DWORDdwNotifyFilter,LPDWORDlpBytesReturned,LPOVERLAPPEDlpOverlapped,LPOVERLAPPED_COMPLETION_ROUTINElpComption)h参数指向的参数如下:要监视的目录的句柄。此目录需要使用FILE_LIST_DIRECTORY访问权限打开。lpBuffer:该参数指向一个内存缓冲区,用于存放返回结果。结果是一个FILE_NOTIFY_INFORMATION数据结构。nBufferLength:表示缓冲区的大小。bWatchSubtree:当该参数为TRUE时,表示监视指定目录下的文件和子目录下的文件操作。如果该参数为FALSE,则只监控指定目录下的文件,不监控子目录下的文件。dwNotifyFilter:此参数指定要返回的文件的更改类型。有关此参数的常量值,请参阅MSDN。lpBytesReturned:此参数返回传递给lpBuffer结果的字节数。lpOverlapped:该参数实现了一个OVERLAPPED结构,用于异步操作,否则数据为NULL。ReadDirectoryChangesW()函数的使用非常简单,下面通过一个例子来介绍它的使用。本例是监控E盘的目录,在程序写完后对E盘进行简单的文件操作,观察程序的输出结构。完整的代码如下:#include#includeextern"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_DELETE|FILE_RLENULL,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("Add:%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;}caseFILE_ACTION_RENAMED_NEW_NAME:{printf("Rename(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目录监控输出记录对于这个目录监控的例子,可以改成简单的文件防御篡改程序。先备份要监控的文件目录,再监控文件目录。如果修改了任何文件,则使用备份目录中的指定文件恢复修改后的文件。