监控文件(夹)是开发中比较常用的功能.
Windows API函数FindFirstChangeNotification、FindCloseChangeNotification、
FindNextChangeNotification可以实现监控文件夹的改变,
但是不能具体指出改变的是哪个文件,自己写程序比较文件?有点舍本逐末了。个人觉得这些函数有些鸡肋。
还好ReadDirectoryChangesW能满足这种需求。其声明如下:
BOOL ReadDirectoryChangesW(
HANDLE hDirectory,
LPVOID lpBuffer,
DWORD nBufferLength,
BOOL bWatchSubtree,
DWORD dwNotifyFilter,
LPDWORD lpBytesReturned,
LPOVERLAPPED lpOverlapped,
LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);
其执行方式有同步和异步之分,异步性能较好,但涉及到重叠I/O,稍显复杂,因此下面的封装代码中采用的同步方式,在一个独立的线程中专门检测文件的改变.lpBuffer参数则存放发生事件的文件的列表.代码如下:
/*
* 监控特定文件目录的改变信息
*/
class
CFileSystemMonitor
{
public
:
/*
* 文件目录改变的类型
*/
enum
tagACTION
{
Added
=
1
,
//
添加了文件/目录
Removed
=
2
,
//
删除了文件/目录
Modified
=
3
,
//
更改了文件/目录
Renamed
=
4
//
重命名了文件/目录
};
//
定义文件目录改变后的回调函数指针
typedef
void
(
*
lpFunDealFile )( tagACTION action, LPCTSTR lpszFileSrc, LPCTSTR lpszFileDst );
public
:
CFileSystemMonitor()
{
m_hDir
=
NULL;
m_bContinue
=
FALSE;
m_hThread
=
NULL;
}
~
CFileSystemMonitor()
{}
/*
* 设置回调函数
*/
void
SetDealFilePtr( lpFunDealFile pFunDeal )
{
ASSERT( pFunDeal
!=
NULL );
m_pFunDeal
=
pFunDeal;
}
BOOL StartMonitor( LPCTSTR lpszDir )
{
ASSERT( m_hThread
==
NULL );
HANDLE hDir
=
::CreateFile( lpszDir, GENERIC_READ
|
FILE_LIST_DIRECTORY,
FILE_SHARE_READ
|
FILE_SHARE_WRITE
|
FILE_SHARE_DELETE,NULL,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS,NULL);
if
( INVALID_HANDLE_VALUE
==
hDir )
return
FALSE;
this
->
m_hDir
=
hDir;
m_bContinue
=
TRUE;
m_hThread
=
::CreateThread( NULL,
0
, MonitorProc,
this
,
0
, NULL );
return
( NULL
==
m_hThread )
?
FALSE:TRUE;
}
void
EndMonitor()
{
ASSERT( m_hThread
!=
NULL );
m_bContinue
=
FALSE;
DWORD dwRet
=
::WaitForSingleObject( m_hThread,
1000
);
if
( WAIT_TIMEOUT
==
dwRet )
{
ASSERT( m_hThread
!=
NULL );
::TerminateThread( m_hThread,
-
1
);
}
::CloseHandle( m_hDir );
m_hDir
=
NULL;
m_hThread
=
NULL;
}
BOOL IsMoniting()
{
return
m_bContinue;
}
private
:
HANDLE m_hDir;
volatile
BOOL m_bContinue;
HANDLE m_hThread;
lpFunDealFile m_pFunDeal;
static
DWORD WINAPI MonitorProc ( LPVOID lParam )
{
CFileSystemMonitor
*
pThis
=
( CFileSystemMonitor
*
)lParam;
ASSERT( pThis
!=
NULL );
char
szBuf[
2
*
(
sizeof
( FILE_NOTIFY_INFORMATION )
+
MAX_PATH ) ];
FILE_NOTIFY_INFORMATION
*
pNotify
=
( FILE_NOTIFY_INFORMATION
*
)szBuf;
DWORD dwBytesReturned(
0
);
while
( pThis
->
m_bContinue )
{
if
(
!
::ReadDirectoryChangesW( pThis
->
m_hDir, pNotify,
sizeof
( szBuf ), TRUE, FILE_NOTIFY_CHANGE_FILE_NAME
|
FILE_NOTIFY_CHANGE_DIR_NAME
|
FILE_NOTIFY_CHANGE_ATTRIBUTES
|
FILE_NOTIFY_CHANGE_SIZE
|
FILE_NOTIFY_CHANGE_LAST_WRITE
|
FILE_NOTIFY_CHANGE_LAST_ACCESS
|
FILE_NOTIFY_CHANGE_CREATION
|
FILE_NOTIFY_CHANGE_SECURITY,
&
dwBytesReturned, NULL, NULL ) )
{
break
;
}
else
{
WCHAR
*
pszFileDst
=
NULL;
WCHAR
*
pszFileSrc
=
pNotify
->
FileName;
pszFileSrc[ pNotify
->
FileNameLength
/
2
]
=
L
'
\0
'
;
if
(
0
!=
pNotify
->
NextEntryOffset )
{
PFILE_NOTIFY_INFORMATION pNext
=
(PFILE_NOTIFY_INFORMATION)( (
char
*
)pNotify
+
pNotify
->
NextEntryOffset);
pszFileDst
=
pNext
->
FileName;
pszFileDst[ pNext
->
FileNameLength
/
2
]
=
L
'
\0
'
;
}
if
( NULL
!=
pThis
->
m_pFunDeal )
pThis
->
m_pFunDeal( (tagACTION)pNotify
->
Action, CW2T(pszFileSrc) , CW2T(pszFileDst) );
}
}
return
0
;
}
private
:
CFileSystemMonitor(
const
CFileSystemMonitor
&
);
CFileSystemMonitor
operator
=
(
const
CFileSystemMonitor );
};