小弟之前有篇关于监控文件(夹)的文章,利用的是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
;
}