文件监控系统设计(2)-"Windows 文件监控API"

本文详细介绍了如何使用Windows文件监控API来监视C:/test及其子目录中的文件操作,如添加、删除、修改、重命名和移动,并将这些事件信息实时记录到C:/log.txt日志文件中。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天浅谈一下windows 下文件监控API 函数- ReadDirectoryChangesW() 的使用。

1 函数声明
BOOL WINAPI ReadDirectoryChangesW(
  __in         HANDLE hDirectory,
  __out        LPVOID lpBuffer,
  __in         DWORD nBufferLength,
  __in         BOOL bWatchSubtree,
  __in         DWORD dwNotifyFilter,
  __out_opt    LPDWORD lpBytesReturned,
  __inout_opt  LPOVERLAPPED lpOverlapped,
  __in_opt     LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);
2 参数说明
   
hDirectory:需要监控的文件夹的句柄 (文件夹用CreateFile打开是要添加 FILE_LIST_DIRECTORY 选项)
lpBuffer: 指向自定义的buffer的指针 ( 自定义的buffer最好大些,否则当瞬间大量文件信息产生时会buffer overflow 导致事件信息丢失)
nBufferLength: buffer大小 ( 这个值很重要, 后面再说)
bWatchSubtree: 是否需要递归监控 ( True 则以此文件为root文件夹下的所有子文件都会处于监控中)
dwNotifyFilter: 指定需要监控的事件类别
lpBytesReturned: 函数返回时存入自定义buffer的字节数
lpOverlapped:NULL (synchronous)
lpCompletionRoutine: 指向completion routine的pointer.

http://msdn.microsoft.com/en-us/library/aa365465%28VS.85%29.aspx


3 使用步骤


3.1 使用CreateFile 打开需要监控的文件夹句柄

Example:

// Get Directory Handle by CreateFile
    hDir=CreateFile( _T("C:/test"),
                    GENERIC_READ | FILE_LIST_DIRECTORY,
                    FILE_SHARE_READ | FILE_SHARE_WRITE,
                    NULL,
                    OPEN_EXISTING,
                    FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
                    NULL );

    if ( hDir==INVALID_HANDLE_VALUE)
    {
        MessageBox( NULL, _T("CreateFile Fail!"), _T("Fail"), MB_OK);
        return -1;
    }

3.2 调用 ReadDirectoryChangesW()

Example:

ReadDirectoryChangesW(
        hDir,
        szBuffer,
        SYS_ALLOC_BUF,
        TRUE,
        FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_FILE_NAME ,
        &BytesReturned,
        NULL,
        NULL)

(目前在我的project中使用的时synchronous的方式,相对asynchronous的方式要简单很多,具体asyn的方式参考MSDN。此文章中所有内容都是针对syn方式的。)

此函数调用后blocking,一直到有events发生,函数返回,events事件信息写入buffer。

3.3 读取events信息

Example:

TCHAR szBuffer[SYS_ALLOC_BUF/2]={0};
FILE_NOTIFY_INFORMATION *pInfo;
DWORD dwOffset=0;
int i=1;
       
       do{
            
            // Get a pointer to the first change record...
            pInfo=(FILE_NOTIFY_INFORMATION *)&szBuffer[dwOffset/2];
            dwOffset+=pInfo->NextEntryOffset;
            i++;

            // Event Process
            TCHAR FilePath[100]={0};
            memcpy((void *)FilePath, (void *)(pInfo->FileName), pInfo->FileNameLength);
            wstring File(FilePath);
            /*
           
             * Event  Procss    

             */
        }
        while(pInfo->NextEntryOffset!=0);

函数一次可能返回多个event信息, 每个event信息是以 FIEL_NOTIFY_INFORMATION 结构体存储的,如下

typedef struct _FILE_NOTIFY_INFORMATION {
  DWORD NextEntryOffset;
  DWORD Action;
  DWORD FileNameLength;
  WCHAR FileName[1];
} FILE_NOTIFY_INFORMATION, *PFILE_NOTIFY_INFORMATION;

所以要通过while循环的方式把buffer中的每个event信息读到,当 NextEntryOffset = 0时,说明这是buffer中的最后一个event。
注意,一定要通过这种循环的方式把buffer中每个event都读完。

3.4 Event 信息处理

使用此API有个特别让人头疼的地方: 单一事件返回重复events

比如:当对单一文件进行ADD,DELETE,MODIFY时,系统会返回多个事件信息。特别是修改某些文件时(Word,Excel)会产生很多临时文件,这些events系统都会返回到buffer中,
所以使用此API时需要根据自己的实际需要自定义一套逻辑来处理多个返回事件。我的方法简述如下:

       1 , 找规律: 尝试着Add, Delete, Modify 单一文件时,系统会返回那些events,找到其中的模式FILE_LIST_DIRECTORY 选项             2 ,   用程序语言定义每种模式
             3,    当通过读取events找到某种模式时,确定何种事件发生

 比如: 当对文件进行重命名时,系统一定会返回两个连续的events:FILE_ACTION_RENAMED_OLD_NAME 和 FILE_ACTION_RENAMED_NEW_NAME。
 找到这个规律后,凡是这两种事件发生时,一定是某个文件被RENAME了。
我自定义了一套简单的模式识别逻辑,可以识别简单文件(eg, txt, cfg..., 不会产生大量临时文件)的ADD,DELETE,MODIFY,MOVE,RENAME .参见附件代码。


4 注意事项

 4.1 文件夹使用CreateFile打开时要选择 FILE_LIST_DIRECTORY 选项
 
 4.2  每次ReadDirectoryChangesW 函数返回时,可能包含多个events,要循环读取完。建议使用两个while循环,大循环调用此函数,小循环负责读取大循环每轮返回的events。

 4.3  要尽量使用大些的 nBufferLength .当调用此API时,操作系统会分配一个buffer,events会自动装入此系统分配的buffer中,指定系统buffer的大小就是通过这个
nBufferLength 参数。当此参数值过小,同时文件夹中瞬间发生大量events时(如瞬间删除成百上千的文件),则此系统buffer会发生buffer overflow, buffer被清空,events信息
丢失, 也就是说,程序还没有来得及把系统buffer中的events读取到自定义的buffer中,系统buffer由于太小产生了overflow,则尚未读取到的系统buffer中的events会被清除。当
系统buffer overflow时, lpBytesReturned 值返回0 。可以通过检查这个参数来检验是否有overflow发生。另外,显然自定义的buffer不能小于系统buffer的size。


附件是一个简单的例子,写得有点儿乱,有兴趣的话讲究着看吧。
下次讨论Linux中文件的监控。

  尴尬好像不能加附件,直接贴之:


说明:监控C:/test 文件夹及其子文件夹中文件ADD,DELETE,MODIFY,RENAME,MOVE 五种事件的发生,并存储在C:/log.txt 中。

有几个小bug,自己发现吧。




#include "stdafx.h"
#include <windows.h>
#include <iostream>
#include <string>
#include <vector>
#define SYS_ALLOC_BUF 1024*128
#define RECORD 2048





using namespace std;

typedef struct _FileEvent
{
    DWORD dwAction;
    wstring lpFile;
    string sTime;
    TCHAR ComputerName[MAX_COMPUTERNAME_LENGTH+1];
} FileEvent,*PFileEvent;


BOOL IsFile( wstring );
wstring FileName( wstring );
VOID CaseProcess( vector<FileEvent> &_vEvent, FileEvent &_feRecord);
VOID EventLog(wstring File1, wstring File2, wstring Action, string sTime);
string TimeStamp( SYSTEMTIME s);
VOID EventLog(wstring File1, wstring File2, string Action, string Time, TCHAR * Computer);
VOID Welcome();

int main()
{

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值