应用重叠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, this0, 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, this0, 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';

        
if0 !=  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;
}
原文地址:https://www.cnblogs.com/fangkm/p/1537835.html