监控文件(夹)是开发中比较常用的功能.
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 );
};
* 监控特定文件目录的改变信息
*/
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 );
};