今天在改进文件监视程序时写的框架代码,监视文件修改(采用完成端口和ReadDirectoryChangesW同时在一个线程中监视多个目录,并且能够判断文件是否完全复制完毕)
#define STRICT
#define WINVER 0x0500
#define _WIN32_WINNT 0x0500
#define _WIN32_IE 0x0501
#define _RICHEDIT_VER 0x0200
#define _WIN32_DCOM
#include <CTL/CTL_BASE.HPP>
class P2PFileShare
{
typedef struct
{
OVERLAPPED ov;
BYTE buff[1024];
LPTSTR path;
DWORD flag;
HANDLE handle;
}PATH_OV, *LPPATH_OV;
typedef struct
{
LPTSTR name; // 文件名称
DWORD time; // 通知时间
}FILE_NOTIFY;
public:
P2PFileShare()
: mh_IOCP(NULL)
, mn_OVPtr(0)
, mp_OVPtr(NULL)
, mn_Notify(0)
, mp_Notify(NULL)
{
}
virtual~P2PFileShare()
{
Close(TRUE);
}
private:
// 创建工作线程
HRESULT _CreateWorkerThread();
// 工作线程
#ifndef _WIN32_WCE
static UINT WINAPI _WorkerThreadProc(IN LPVOID pData);
#else
static DWORD WINAPI _WorkerThreadProc(IN LPVOID pData);
#endif // #ifndef _WIN32_WCE
HRESULT _WorkerThreadProc();
public:
HRESULT Start();
VRESULT Close(IN CONST BOOL bWait = FALSE);
public:
// 监视指定目录
HRESULT MonitorPath(IN LPCTSTR sFileName);
// 文件变化通知
LPTSTR GetNotify();
private:
HANDLE mh_IOCP;
MLONG mn_OVPtr;
LPPATH_OV* mp_OVPtr;
MLONG mn_Notify;
FILE_NOTIFY* mp_Notify;
public:
INLINE VRESULT EnterLock() {mo_cs.EnterLock();}
INLINE VRESULT LeaveLock() {mo_cs.LeaveLock();}
private:
MTCSObject mo_cs;
};
// 创建工作线程(根据 CPU 的数量,创建相应数量的工作线程)
HRESULT P2PFileShare::_CreateWorkerThread()
{
HRESULT hr = E_FAIL;
HANDLE hThread;
#ifndef _WIN32_WCE
if((hThread = (HANDLE)_beginthreadex(NULL, 0
, _WorkerThreadProc
, (LPVOID)this, 0, NULL)) == 0)
{
return _doserrno;
}
#else
if((hThread = (HANDLE)::CreateThread(NULL, 0
, _WorkerThreadProc
, (LPVOID)this, 0, &NULL)) == 0)
{
return ::GetLastError();
}
#endif
::CloseHandle(hThread); // 关闭句柄避免资源泄漏
hr = S_OK;
return hr;
}
// 工作线程
#ifndef _WIN32_WCE
UINT P2PFileShare::_WorkerThreadProc(IN LPVOID pData)
#else
DWORD P2PFileShare::_WorkerThreadProc(IN LPVOID pData)
#endif // #ifndef _WIN32_WCE
{
((P2PFileShare*)pData)->_WorkerThreadProc();
#ifndef _WIN32_WCE
_endthreadex(0);
#else
ExitThread(0);
#endif
return 0;
}
// 数据处理线程函数
HRESULT P2PFileShare::_WorkerThreadProc()
{
// 注意: 调用 GetQueuedCompletionStatus 的线程都将被放到完成端口的等待线程队列中
// 完成操作循环
BOOL bSucceed;
DWORD dwBytes;
LPDWORD pCT;
PATH_OV* pOV;
for(;;)
{
bSucceed = ::GetQueuedCompletionStatus(mh_IOCP
, &dwBytes
, (LPDWORD)&pCT
, (LPOVERLAPPED*)&pOV
, INFINITE
);
if(bSucceed)
{
if(NULL == pOV) break; // 退出工作线程
FILE_NOTIFY_INFORMATION * pfiNotifyInfo = (FILE_NOTIFY_INFORMATION*)pOV->buff;
DWORD dwNextEntryOffset;
TCHAR sFileName[1024];
do
{
dwNextEntryOffset = pfiNotifyInfo->NextEntryOffset;
DWORD dwAction = pfiNotifyInfo->Action;
DWORD dwFileNameLength = pfiNotifyInfo->FileNameLength;
CPY_W2T(sFileName, pfiNotifyInfo->FileName, dwFileNameLength/sizeof(WCHAR));
switch(dwAction)
{
case FILE_ACTION_REMOVED: // 文件删除
{
LPTSTR sFullName = new TCHAR[LPTSTRLen(pOV->path) + LPTSTRLen(sFileName) + 1];
if(NULL != sFullName)
{
LPTSTRCpy(sFullName, pOV->path);
LPTSTRCat(sFullName, sFileName);
LPTSTRPrintf(__T("Del %s"n"), sFullName);
delete[] sFullName;
}
}
break;
case FILE_ACTION_ADDED: // 文件替换
{
// 替换文件时只会触发 FILE_ACTION_ADDED, 因此需要手工触发 FILE_ACTION_MODIFIED
LPTSTRPrintf(__T("Add %s"n"), sFileName);
}
case FILE_ACTION_MODIFIED: // 文件修改
{
// 测试文件是否关闭
LPTSTR sFullName = new TCHAR[LPTSTRLen(pOV->path) + LPTSTRLen(sFileName) + 1];
if(NULL != sFullName)
{
LPTSTRCpy(sFullName, pOV->path);
LPTSTRCat(sFullName, sFileName);
HANDLE hFile = ::CreateFile(sFullName
, GENERIC_WRITE
, FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
if(INVALID_HANDLE_VALUE == hFile)
{
HRESULT hr = ::GetLastError();
LPTSTRPrintf(__T("Locked %s %d"n"), sFileName, hr);
}
else
{
::CloseHandle(hFile);
LPTSTRPrintf(__T("Modify %s"n"), sFileName);
LONG i;
EnterLock();
for(i=0;i<mn_Notify;i++)
{
if(LPTSTRCompare(mp_Notify[i].name, sFullName) == 0)
{
mp_Notify[i].time = ::GetTickCount();
break;
}
}
if(i >= mn_Notify)
{
FILE_NOTIFY* pNotify = new FILE_NOTIFY[mn_Notify + 1];
if(NULL != pNotify)
{
if(mn_Notify > 0)
{
::CopyMemory(pNotify, mp_Notify, sizeof(FILE_NOTIFY)*mn_Notify);
delete[] mp_Notify;
}
pNotify[mn_Notify].name = sFullName; sFullName = NULL;
pNotify[mn_Notify].time = ::GetTickCount();