应用重叠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;
}
/*
* 投送操作数据
*/
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;
}