今天浅谈一下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。
找到这个规律后,凡是这两种事件发生时,一定是某个文件
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。
找到这个规律后,凡是这两种事件发生时,一定是某个文件