DectoryWatch.cpp
#pragma once
#pragma execution_character_set("utf-8")
// DectoryWatch.cpp
#include "stdafx.h"
#include "DirectoryWatch.h"
#include <process.h>
#include <strsafe.h>
#include <Shlwapi.h>
#pragma comment(lib, "User32.lib")
#pragma comment(lib, "Shlwapi.lib")
#define MAX_BUFFER_SIZE (1024)
typedef enum _MyMSG
{
MSG_SUCCESS = (WM_USER)+0x100,
MSG_STARTWATCH,
MSG_STOPWATCH,
MSG_EXIT
};
//目录监控的构造函数
CDirectoryWatch::CDirectoryWatch(): m_dwMSGTID(0), m_hMSG(NULL)
{
m_hMutex = CreateMutexW(NULL, FALSE, NULL);
m_hMSG = (HANDLE)_beginthreadex(NULL, NULL, MSGThred, this, NULL, (unsigned int *)&m_dwMSGTID);
//创建消息线程 接收消息 此线程必须先于发送消息函数之前运行 否则消息将发送失败
if (NULL == m_hMSG)
TRACE("<ERR> CDirectoryWatch()->Create MSGThred fail! ErrorCode:0x%X\r\n", GetLastError());
TRACE("<DBG> CDirectoryWatch() success! Open the MSGThred...\r\n");
}
CDirectoryWatch::~CDirectoryWatch()
{
// 释放资源
if (!m_vWatchList.empty())
{
std::list<WATCH_PARAM*>::iterator iBegin = m_vWatchList.begin();
std::list<WATCH_PARAM*>::iterator iEnd = m_vWatchList.end();
while (iBegin != iEnd)
{
StopWatch((*iBegin++)->wzPath); // 停止监控
}
}
PostThreadMessageW(m_dwMSGTID, MSG_EXIT, NULL, NULL); // 退出 MSGThred 线程
WaitForSingleObject(m_hMSG, INFINITE);
CloseHandle(m_hMSG);
CloseHandle(m_hMutex);
TRACE("<DBG> ~CDirectoryWatch() success! Close the MSGThred...\r\n");
}
void CDirectoryWatch::Lock()
{
WaitForSingleObject(m_hMutex, INFINITE);
}
void CDirectoryWatch::UnLock()
{
ReleaseMutex(m_hMutex);
}
BOOL CDirectoryWatch::StartWatch(LPWSTR wzPath, funNotifyAction lpfunNotifyAction)
{
if (wzPath == NULL && wzPath[0] == 0 && lpfunNotifyAction == NULL && !PathFileExistsW(wzPath))
return FALSE;
// 排除重复
Lock();
std::list<WATCH_PARAM*>::iterator iBegin = m_vWatchList.begin();
std::list<WATCH_PARAM*>::iterator iEnd = m_vWatchList.end();
while (iBegin != iEnd)
{
if (wcscmp((*iBegin)->wzPath, wzPath) == 0)
{
TRACE("<ERR> StartWatch() fail! %S 已经被监控!\r\n", wzPath);
UnLock();
return FALSE;
}
++iBegin;
}
UnLock();
if (!PostThreadMessageW(m_dwMSGTID, MSG_STARTWATCH, (WPARAM)wzPath, (LPARAM)lpfunNotifyAction))
{
TRACE("<ERR> StartWatch()->PostThreadMessageW() fail! ErrorCode:0x%X\r\n", GetLastError());
return FALSE;
}
return TRUE;
}
BOOL CDirectoryWatch::StopWatch(LPWSTR wzPath)
{
if (wzPath == NULL && wzPath[0] == 0 && !PathFileExistsW(wzPath))
return FALSE;
BOOL bIsExist = FALSE;
std::list<WATCH_PARAM*>::iterator iBegin = m_vWatchList.begin();
std::list<WATCH_PARAM*>::iterator iEnd = m_vWatchList.end();
while (iBegin != iEnd)
{
if (wcscmp((*iBegin)->wzPath, wzPath) == 0)
{
bIsExist = TRUE;
break;
}
++iBegin;
}
if (!bIsExist)
{
TRACE("<ERR> StopWatch() fail! %S 没有监控...\r\n", wzPath);
return FALSE;
}
if (!PostThreadMessageW(m_dwMSGTID, MSG_STOPWATCH, (WPARAM)wzPath, NULL))
{
TRACE("<ERR> StopWatch()->PostThreadMessageW() fail! ErrorCode:0x%X\r\n", GetLastError());
return FALSE;
}
return TRUE;
}
unsigned __stdcall CDirectoryWatch::MSGThred( LPVOID lpParam )
{
CDirectoryWatch* This = (CDirectoryWatch*)lpParam;
MSG msg;
while( GetMessageW(&msg, NULL, 0, 0) ){
switch ( msg.message ){
default:
TranslateMessage(&msg);
DispatchMessage(&msg);
break;
//开启监控
case MSG_STARTWATCH:
{
WATCH_PARAM* SwatchParam = new(std::nothrow) WATCH_PARAM;
if( SwatchParam == 0 ){
TRACE("<ERR> MSGThred()->New buffer fail!\r\n");
break;
}
ZeroMemory(SwatchParam, sizeof(WATCH_PARAM));
StringCchCopyW(SwatchParam->wzPath, MAX_PATH, (LPCWSTR)msg.wParam);
SwatchParam->hFile = CreateFileW(
SwatchParam->wzPath,
FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, // 注意这里的 异步标志
NULL
);
if( SwatchParam->hFile == INVALID_HANDLE_VALUE ){
TRACE("<ERR> MSGThred()->CreateFileW fail! ErrorCode:0x%X\r\n", GetLastError());
delete[] SwatchParam;
break;
}
SwatchParam->hEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
if( SwatchParam->hEvent == NULL ){
TRACE("<ERR> MSGThred()->CreateEventW fail! ErrorCode:0x%X\r\n", GetLastError());
CloseHandle(SwatchParam->hFile);
delete[] SwatchParam;
break;
}
SwatchParam->ol.hEvent = SwatchParam->hEvent;
// 接收通知信息的缓存区
SwatchParam->pBuffer = new(std::nothrow) BYTE[MAX_BUFFER_SIZE];
if( SwatchParam->pBuffer == 0 ){
SwatchParam->pBuffer = new(std::nothrow) BYTE[MAX_BUFFER_SIZE];
if( SwatchParam->pBuffer == 0 ){
TRACE("<ERR> MSGThred()->New buffer fail!\r\n");
CloseHandle(SwatchParam->hEvent);
CloseHandle(SwatchParam->hFile);
delete[] SwatchParam;
break;
}
}
ZeroMemory(SwatchParam->pBuffer, MAX_BUFFER_SIZE);
SwatchParam->dwBufferSize = MAX_BUFFER_SIZE;
SwatchParam->NotifyAction = (funNotifyAction)msg.lParam;
// 开启监控线程 每个文件都有一个
SwatchParam->hWatch = (HANDLE)_beginthreadex(NULL, NULL, WatchThred, (LPVOID)SwatchParam, NULL, NULL);
This->Lock();
This->m_vWatchList.push_back(SwatchParam); // 添加到监控列表 便于控制
This->UnLock();
TRACE("<DBG> StartWatch success!-> %S\r\n", SwatchParam->wzPath);
break;
}
// 停止监控
case MSG_STOPWATCH:
{
WCHAR *wzPath = (WCHAR *)msg.wParam;
This->Lock();
std::list<WATCH_PARAM*>::iterator iBegin = This->m_vWatchList.begin();
std::list<WATCH_PARAM*>::iterator iEnd = This->m_vWatchList.end();
while (iBegin != iEnd){
if( wcscmp((*iBegin)->wzPath, wzPath) == 0 ){
break;
}
++iBegin;
}
(*iBegin)->bIsExit = TRUE;
SetEvent((*iBegin)->hEvent); //设置状态 不让 监控函数堵塞住
WaitForSingleObject((*iBegin)->hWatch, INFINITE);
CloseHandle((*iBegin)->hFile);
CloseHandle((*iBegin)->hEvent);
delete[](*iBegin)->pBuffer;
TRACE("<DBG> StopWatch success!-> %S\r\n", (*iBegin)->wzPath);
delete[](*iBegin);
This->m_vWatchList.erase(iBegin);
This->UnLock();
break;
}
case MSG_EXIT:
{
_endthreadex(0);
}
}
}
return 0;
}
unsigned __stdcall CDirectoryWatch::WatchThred( LPVOID lpParam )
{
WATCH_PARAM* pSwatchParam = (WATCH_PARAM*)lpParam;
DWORD dwByteRet = 0;
FILE_NOTIFY_INFORMATION* pNextFileNotify;
while( true ){
if( pSwatchParam->bIsExit ){
break;
//退出标记
}
if( !ReadDirectoryChangesW(pSwatchParam->hFile,
pSwatchParam->pBuffer,
pSwatchParam->dwBufferSize,
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,
&dwByteRet,
&pSwatchParam->ol, //异步结构
NULL) ){
TRACE("<ERR> WatchThred()->ReadDirectoryChangesW() fail! ErrorCode:0x%X path:%S\r\n", GetLastError(), pSwatchParam->wzPath);
continue;
}
//TRACE("WatchThred.0\n");
if( !GetOverlappedResult(pSwatchParam->hFile, &pSwatchParam->ol, &dwByteRet, TRUE)){
TRACE("<ERR> WatchThred()->GetOverlappedResult() fail! ErrorCode:0x%X path:%S\r\n", GetLastError(), pSwatchParam->wzPath);
continue;
}
Sleep(100);
//TRACE("WatchThred.1\n");
FILE_NOTIFY_INFORMATION* pFileNotify = (FILE_NOTIFY_INFORMATION*)pSwatchParam->pBuffer;
next_frame:
if( !pFileNotify->Action ){
continue;
}
//TRACE("WatchThred.2\n");
//这里要注意的就是 FILE_NOTIFY_INFORMATION.FileName 不是以'\0'结尾
WCHAR wzSrcFile[MAX_PATH] = { 0 };
WCHAR wzDestFile[MAX_PATH] = { 0 };
memcpy(wzSrcFile, pFileNotify->FileName, pFileNotify->FileNameLength);
//用wmemcpy 会有乱码 多拷贝数据了 应该用memcpy
//如果是重命名会将新的信息存储到下一个结构
if( pFileNotify->Action == FILE_ACTION_RENAMED_OLD_NAME ){
pNextFileNotify = (FILE_NOTIFY_INFORMATION*)((BYTE*)pFileNotify + pFileNotify->NextEntryOffset);
memcpy(wzDestFile, pNextFileNotify->FileName, pNextFileNotify->FileNameLength);
}
//通用处理函数
pSwatchParam->NotifyAction(pFileNotify->Action, wzSrcFile, wzDestFile);
if( pFileNotify->Action == FILE_ACTION_RENAMED_OLD_NAME ){
pFileNotify = pNextFileNotify;
}
if( pFileNotify->NextEntryOffset ){
pFileNotify = PFILE_NOTIFY_INFORMATION((BYTE*)pFileNotify + pFileNotify->NextEntryOffset);
goto next_frame;
}else{
ZeroMemory(pSwatchParam->pBuffer, MAX_BUFFER_SIZE);
}
}
_endthreadex(0);
return 0;
}
DirectoryWatch.h
#pragma once
#pragma execution_character_set("utf-8")
// DirectoryWatch.h
#pragma once
#include <windows.h>
#include <list>
// 通用的通知处理函数
typedef void(*funNotifyAction)(DWORD dwAction, LPWSTR wzSrcFile, LPWSTR wzDestFile);
// 要监控的目录的信息
typedef struct _WATCH_PARAM
{
_WATCH_PARAM()
{
hFile = INVALID_HANDLE_VALUE;
hEvent = NULL;
hWatch = NULL;
pBuffer = NULL;
bIsExit = FALSE;
dwBufferSize = 0;
NotifyAction = NULL;
ZeroMemory(&wzPath, sizeof(WCHAR)*MAX_PATH);
ZeroMemory(&ol, sizeof(OVERLAPPED));
}
WCHAR wzPath[MAX_PATH]; // 路径
HANDLE hFile; // 文件句柄
HANDLE hEvent; // 事件句柄
BYTE *pBuffer; // 缓存区
DWORD dwBufferSize; // 缓存区大小
OVERLAPPED ol; // 异步结构
HANDLE hWatch; // 监控线程句柄
BOOL bIsExit; // 监控线程是否退出
funNotifyAction NotifyAction;
}WATCH_PARAM, *PWATCH_PARAM;
// 监控类
class CDirectoryWatch
{
public:
CDirectoryWatch();
virtual ~CDirectoryWatch();
public:
BOOL StartWatch(LPWSTR wzPath, funNotifyAction lpfunNotifyAction);
BOOL StopWatch(LPWSTR wzPath);
private:
static unsigned __stdcall MSGThred(LPVOID lpParam); // 消息线程
static unsigned __stdcall WatchThred(LPVOID lpParam); // 监控线程
void Lock();
void UnLock();
private:
std::list<WATCH_PARAM*> m_vWatchList; // 监控的列表
HANDLE m_hMSG; // 消息线程句柄
DWORD m_dwMSGTID; // 消息线程ID
HANDLE m_hMutex; // 互斥锁(list保护)
};
测试函数 main.cpp
// 测试函数 main.cpp
#include "stdafx.h"
#include "DirectoryWatch.h"
#include <locale.h>
void NotifyAction(DWORD dwAction, LPWSTR wzSrcFile, LPWSTR wzDestFile)
{
switch (dwAction)
{
case FILE_ACTION_ADDED:
TRACE("FILE_ACTION_ADDED:%S \r\n", wzSrcFile);
break;
case FILE_ACTION_REMOVED:
TRACE("FILE_ACTION_REMOVED:%S \r\n", wzSrcFile);
break;
case FILE_ACTION_MODIFIED:
TRACE("FILE_ACTION_MODIFIED:%S \r\n", wzSrcFile);
break;
case FILE_ACTION_RENAMED_OLD_NAME:
TRACE("FILE_ACTION_RENAMED:%S to %S \r\n", wzSrcFile, wzDestFile);
break;
case FILE_ACTION_RENAMED_NEW_NAME:
TRACE("FILE_ACTION_RENAMED new:%S to %S \r\n", wzSrcFile, wzDestFile);
break;
default:
TRACE("FILE_ACTION_RENAMED default:%S to %S \r\n", wzSrcFile, wzDestFile);
break;
}
}
int test_main(void)
{
//setlocale(LC_ALL, "chs");
CDirectoryWatch watch;
Sleep(100);
TRACE("Start Directory Watch ...\r\n");
watch.StartWatch(L"F:\\A50\\java\\doip\\uart\\libs\\armeabi\\", NotifyAction);
//watch.StartWatch(L"F:\\22", NotifyAction);
//watch.StartWatch(L"F:\\33", NotifyAction);
system("pause");
watch.StopWatch(L"F:\\A50\\java\\doip\\uart\\libs\\armeabi\\");
//watch.StopWatch(L"F:\\22");
//watch.StopWatch(L"F:\\33");
system("pause");
TRACE("Stop Directory Watch ...\r\n");
return 0;
}