小弟之前有篇关于监控文件(夹)的文章,利用的是API函数ReadDirectoryChangesW,当时图方便啊,使用ReadDirectoryChangesW进行同步监控文件(夹),现在突然发现自己居然没有手段让运行阻塞的ReadDirectoryChangesW函数的线程正常地退出,继而导致很多资源无法释放,恼火了.无奈之下只好又写了异步的版本。思考再三,由于监控过程不存在可收缩性的考虑,也就没有使用完成端口模型,使用重叠I/O模型就足够了。
小弟之前有篇关于监控文件(夹)的文章,利用的是API函数ReadDirectoryChangesW,当时图方便啊,使用ReadDirectoryChangesW进行同步监控文件(夹),现在突然发现自己居然没有手段让运行阻塞的ReadDirectoryChangesW函数的线程正常地退出,继而导致很多资源无法释放,恼火了.无奈之下只好又写了异步的版本。思考再三,由于监控过程不存在可收缩性的考虑,也就没有使用完成端口模型,使用重叠I/O模型就足够了。
// .h文件
/*

* 投送操作数据
*/
typedef
struct _SPerIOData
{
OVERLAPPED                           m_overlapped;
// 重叠结构
FILE_NOTIFY_INFORMATION * m_pNotify;
CHAR                                      m_szBuffer[
2 * ( sizeof ( FILE_NOTIFY_INFORMATION ) + MAX_PATH ) ];
DWORD                                   m_dwBytesReturned;

void Init( HANDLE hEvent )
{
// 使用AcceptEx函数需要事先创建连接套件字
m_overlapped.Internal = 0 ;
m_overlapped.InternalHigh
= 0 ;
m_overlapped.Offset
= 0 ;
m_overlapped.OffsetHigh
= 0 ;
m_overlapped.hEvent
= hEvent;

m_pNotify
= ( FILE_NOTIFY_INFORMATION * )m_szBuffer;
m_dwBytesReturned
= 0 ;

ZeroMemory( m_pNotify,  _countof( m_szBuffer ) );
}
}SPerIOData,
* PSPerIOData;
/////////////////////////////////////////////////////////////////////////////////

/*
*  监控特定文件目录的改变信息
*/
class CFileSystemMonitor
{
public :
/*
* 文件目录改变的类型
*/
enum tagACTION
{
Added
= 1 , // 添加了文件/目录
Removed = 2 , // 删除了文件/目录
Modified = 3 , // 更改了文件/目录
Renamed = 4 // 重命名了文件/目录
};

private :
HANDLE   m_hEvent;
// 事件句柄
HANDLE   m_hDir; // 文件(夹)句柄
HANDLE   m_hThread; // 监控线程句柄

volatile BOOL  m_bContinue; // 指示监控线程是否继续

SPerIOData  m_perIOData;
// 监控投送数据

public :
CFileSystemMonitor();
~ CFileSystemMonitor();

/*
* 开始监控文件(夹)
* lpszDir: 要监控的文件(夹)
* 返回成败与否
*/
BOOL  StartMonitor( LPCTSTR lpszDir );

/*
* 停止监控
*/
void EndMonitor();

private :
// 监控线程
static DWORD WINAPI  MonitorProc ( LPVOID lParam );
// 等待结束线程
static DWORD WINAPI  WaitExitProc ( LPVOID lParam );

private :
CFileSystemMonitor(
const CFileSystemMonitor & );
CFileSystemMonitor
operator = ( const CFileSystemMonitor );
};

////////////////////////////////////////////////////////////////////////////////////////////////////////
// .cpp文件
TCHAR * szFileAction[ 5 ] = { _T( " 空操作 " ), _T( " 添加文件 " ), _T( " 删除文件 " ), _T( " 修改文件 " ), _T( " 重命名文件 " ) };


CFileSystemMonitor::CFileSystemMonitor()
{
// 初始化数据
m_hThread = NULL;
m_hEvent
= ::CreateEvent( NULL, FALSE, FALSE, NULL );
m_perIOData.Init( m_hEvent );

m_bContinue
= FALSE;
}

CFileSystemMonitor::
~ CFileSystemMonitor()
{
ATLASSERT( m_hThread
== NULL );
::CloseHandle( m_hEvent );
}

BOOL CFileSystemMonitor::StartMonitor( LPCTSTR lpszDir )
{
ATLASSERT( m_hThread
== NULL );

// 打开文件(夹),
HANDLE  hDir = ::CreateFile( lpszDir, GENERIC_READ | FILE_LIST_DIRECTORY, // 需指定FILE_LIST_DIRECTORY属性
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,NULL,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS
| FILE_FLAG_OVERLAPPED, NULL ); // 指定FILE_FLAG_OVERLAPPED在于使用重叠I/O
if ( INVALID_HANDLE_VALUE == hDir )
return FALSE;
this -> m_hDir = hDir;

// 创建监控线程
this -> m_bContinue = TRUE;
m_hThread
= ::CreateThread( NULL, 0 , MonitorProc, this , 0 , NULL );
if ( m_hThread == NULL )
return FALSE;


// 投送监测操作
ZeroMemory( m_perIOData.m_pNotify,  _countof( m_perIOData.m_szBuffer ) );
if ( ! ::ReadDirectoryChangesW( this -> m_hDir, m_perIOData.m_pNotify, sizeof ( m_perIOData.m_szBuffer ), 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,
& m_perIOData.m_dwBytesReturned,  (LPOVERLAPPED) & m_perIOData.m_overlapped,    NULL ) )
{
return FALSE;
}

return TRUE;
}


void CFileSystemMonitor::EndMonitor()
{
this -> m_bContinue = FALSE;
::SetEvent( m_hEvent );
// 人工使事件受信,从而使监控线程退出

// 等待结束
HANDLE hThread = ::CreateThread( NULL, 0 , WaitExitProc, this , 0 , NULL );
::CloseHandle( hThread );
}

DWORD WINAPI  CFileSystemMonitor::MonitorProc ( LPVOID lParam )
{
CFileSystemMonitor
* pThis = ( CFileSystemMonitor * )lParam;
ATLASSERT( pThis
!= NULL );

while ( pThis -> m_bContinue )
{
// 等待事件受信
::WaitForSingleObject( pThis -> m_hEvent, INFINITE );

// 调用GetOverlappedResult获取投递操作结果(由于此环境下是单投递操作,故此过程可以省略)
DWORD  dwTransferred( 0 );
if ( ! ::GetOverlappedResult( pThis -> m_hDir, (LPOVERLAPPED) & pThis -> m_perIOData.m_overlapped, & dwTransferred, FALSE )  )
{
break ;
}

WCHAR
* pszFileDst = NULL;
WCHAR
* pszFileSrc = pThis -> m_perIOData.m_pNotify -> FileName;
pszFileSrc[ pThis
-> m_perIOData.m_pNotify -> FileNameLength / 2 ] = L ' \0 ' ;

if ( 0 != pThis -> m_perIOData.m_pNotify -> NextEntryOffset )
{
PFILE_NOTIFY_INFORMATION pNext
= (PFILE_NOTIFY_INFORMATION)( ( char * ) pThis -> m_perIOData.m_pNotify + pThis -> m_perIOData.m_pNotify -> NextEntryOffset);
pszFileDst
= pNext -> FileName;
pszFileDst[ pNext
-> FileNameLength / 2 ] = L ' \0 ' ;
}

{
// 在输出窗口输出,用户在此可定制自己的处理
// pThis->m_perIOData.m_pNotify->Action 为操作类型
// pszFileSrc为源文件
// pszFileDst为改动后的文件
TCHAR szOutput[ 1024 ];
wsprintf( szOutput,  _T(
" %s: 源文件:%s  \n " ),
szFileAction[(tagACTION)pThis
-> m_perIOData.m_pNotify -> Action],
CW2T(pszFileSrc) );
::OutputDebugString( szOutput );
}

// 继续投送监测操作
ZeroMemory( pThis -> m_perIOData.m_pNotify,  _countof( pThis -> m_perIOData.m_szBuffer ) );
if ( ! ::ReadDirectoryChangesW( pThis -> m_hDir, pThis -> m_perIOData.m_pNotify, sizeof ( pThis -> m_perIOData.m_szBuffer ), 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,
& pThis -> m_perIOData.m_dwBytesReturned,  (LPOVERLAPPED) & pThis -> m_perIOData.m_overlapped,    NULL ) )
{
break ;
}
}
return 0 ;
}

DWORD WINAPI  CFileSystemMonitor::WaitExitProc ( LPVOID lParam )
{
CFileSystemMonitor
* pThis = ( CFileSystemMonitor * )lParam;
ATLASSERT( pThis
!= NULL );

// 等待监听线程结束
::WaitForSingleObject( pThis -> m_hThread, INFINITE );
pThis
-> m_hThread = NULL;
// 关闭文件句柄
::CloseHandle( pThis -> m_hDir );

return 0 ;
}