| WatchData.h: #ifndef WATCHDATA_H_ #define WATCHDATA_H_ #include #include #include #include using namespace std; typedef void(*ChangeCallback)(int watchID, int action, const WCHAR* rootPath, const WCHAR* filePath); class WatchData { private: static int _counter; WCHAR* _path; int _watchId; HANDLE _hDir; int _mask; bool _watchSubtree; DWORD _byteReturned; OVERLAPPED _overLapped; char _buffer[8196]; HANDLE _completionPort; public: WatchData(); WatchData(const WCHAR* path, int mask, bool watchSubtree, HANDLE completionPort); virtual ~WatchData(); const char* getBuffer() { return _buffer; } // FILE_NOTIFY_INFORMATION* getNotifyInfo(){return _notifyInfo;} const int getBufferSize() { return sizeof(_buffer); } // const DWORD getBytesReturned() {return _byteReturned;} const WCHAR* getPath() { return _path; } const int getId() { return _watchId; } // const HANDLE getDirHandle() {return _hDir;} // const int getMask() {return _mask;} int watchDirectory(); // cancel pending watch on the hDir, returns 0 if okay or errorCode otherwise. int unwatchDirectory(); }; #endif Win32FSHook.h: #ifndef WIN32FSHOOK_H_ #define WIN32FSHOOK_H_ #include #include #include #include #include #include "WatchData.h" using namespace std; class Event { public: int _watchID; int _action; WCHAR* _rootPath; WCHAR* _filePath; Event(int wd, int action, const WCHAR* rootPath, const WCHAR* filePath) { _watchID = wd; _action = action; size_t len1 = wcslen(rootPath); size_t len2 = wcslen(filePath); _rootPath = new WCHAR[len1 + 1]; _filePath = new WCHAR[len2 + 1]; wcsncpy(_rootPath, rootPath, len1); _rootPath[len1] = 0; wcsncpy(_filePath, filePath, len2); _filePath[len2] = 0; } ~Event() { delete[] _rootPath; delete[] _filePath; } }; class Win32FSHook { private: static const DWORD DELETE_WD_SIGNAL = 99999997; static const DWORD EXIT_SIGNAL = 99999998; static Win32FSHook* instance; // running flag bool _isRunning; // thread handle HANDLE _mainLoopThreadHandle; HANDLE _completionPort; // critical seaction CRITICAL_SECTION _cSection; // watch id 2 watch map map // Thread function static DWORD WINAPI mainLoop(LPVOID lpParam); ChangeCallback _callback; void watchDirectory(WatchData* wd); CRITICAL_SECTION _eventQueueLock; HANDLE _eventQueueEvent; void postEvent(Event* event); HANDLE _eventsThread; static DWORD WINAPI eventLoop(LPVOID lpParam); queue WatchData* find(int wd); public: static const int ERR_INIT_THREAD = 1; Win32FSHook(); virtual ~Win32FSHook(); void init(ChangeCallback callback); int add_watch(const WCHAR* path, long notifyFilter, bool watchSubdirs, DWORD& error); void remove_watch(int watchId); }; #endif |
| WatchData.cpp: #include "WatchData.h" //#include "Logger.h" int WatchData::_counter = 0; WatchData::WatchData() { } WatchData::~WatchData() { if (_path != NULL) free(_path); _hDir = 0; } WatchData::WatchData(const WCHAR* path, int mask, bool watchSubtree, HANDLE completionPort) : _watchId(++_counter), _mask(mask), _watchSubtree(watchSubtree), _byteReturned(0), _completionPort(completionPort) { _path = _wcsdup(path); _hDir = CreateFileW(_path, FILE_LIST_DIRECTORY | GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, //security attributes OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL); if (_hDir == INVALID_HANDLE_VALUE) { throw GetLastError(); } if (NULL == CreateIoCompletionPort(_hDir, _completionPort, (ULONG_PTR)_watchId, 0)) { throw GetLastError(); } } int WatchData::unwatchDirectory() { int c = CancelIo(_hDir); if (_hDir != INVALID_HANDLE_VALUE) CloseHandle(_hDir); if (c == 0) { return GetLastError(); } else { return 0; } } int WatchData::watchDirectory() { memset(_buffer, 0, sizeof(_buffer)); memset(&_overLapped, 0, sizeof(_overLapped)); if (!ReadDirectoryChangesW(_hDir, _buffer,//<--FILE_NOTIFY_INFORMATION records are put into this buffer sizeof(_buffer), _watchSubtree, _mask, &_byteReturned, &_overLapped, NULL)) { return GetLastError(); } else { return 0; } } Win32FSHook.cpp: #include "Win32FSHook.h" #include #include #include #include #include #include //#include "Lock.h" #include "WatchData.h" //#include "Logger.h" Win32FSHook* Win32FSHook::instance = 0; Win32FSHook::Win32FSHook() { _callback = 0; _mainLoopThreadHandle = INVALID_HANDLE_VALUE; _eventsThread = INVALID_HANDLE_VALUE; _isRunning = false; InitializeCriticalSection(&_cSection); InitializeCriticalSection(&_eventQueueLock); _eventQueueEvent = CreateEvent(NULL, FALSE, FALSE, NULL); } void Win32FSHook::init(ChangeCallback callback) { instance = this; _callback = callback; if (!_isRunning) { _completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1); if (_completionPort == NULL) { throw GetLastError(); } _isRunning = true; DWORD dwThreadId; LPVOID dwThrdParam = (LPVOID)this; _mainLoopThreadHandle = CreateThread( NULL, // default security attributes 0, // use default stack size Win32FSHook::mainLoop, // thread function dwThrdParam, // argument to thread function 0, // use default creation flags &dwThreadId); // returns the thread identifier if (_mainLoopThreadHandle == NULL) { throw ERR_INIT_THREAD; } SetThreadPriority(_mainLoopThreadHandle, THREAD_PRIORITY_TIME_CRITICAL); _eventsThread = CreateThread( NULL, // default security attributes 0, // use default stack size Win32FSHook::eventLoop, // thread function dwThrdParam, // argument to thread function 0, // use default creation flags &dwThreadId); // returns the thread identifier if (_eventsThread == NULL) { CloseHandle(_mainLoopThreadHandle); throw ERR_INIT_THREAD; } } } Win32FSHook::~Win32FSHook() { // debug("+Win32FSHook destructor"); // terminate thread. _isRunning = false; SetEvent(_eventQueueEvent); PostQueuedCompletionStatus(_completionPort, EXIT_SIGNAL, (ULONG_PTR)NULL, NULL); // cleanup if (INVALID_HANDLE_VALUE != _mainLoopThreadHandle) CloseHandle(_mainLoopThreadHandle); if (INVALID_HANDLE_VALUE != _eventsThread) CloseHandle(_eventsThread); CloseHandle(_completionPort); DeleteCriticalSection(&_cSection); // debug("-Win32FSHook destructor"); } void Win32FSHook::remove_watch(int wd) { // debug("+remove_watch(%d)", wd); EnterCriticalSection(&_cSection); map if (i == _wid2WatchData.end()) { // debug("remove_watch: watch id %d not found", wd); } else { WatchData* wd = i->second; int res = wd->unwatchDirectory(); if (res != 0) { // log("Error canceling watch on dir %ls : %d",wd->getPath(), res); } int res2 = _wid2WatchData.erase(wd->getId()); if (res2 != 1) { // log("Error deleting watch %d from map, res=%d",wd->getId(), res2); } PostQueuedCompletionStatus(_completionPort, DELETE_WD_SIGNAL, (ULONG_PTR)wd, NULL); } LeaveCriticalSection(&_cSection); // debug("-remove_watch(%d)", wd); } int Win32FSHook::add_watch(const WCHAR* path, long notifyFilter, bool watchSubdirs, DWORD& error) { // debug("+add_watch(%ls)", path); // synchronize access by multiple threads EnterCriticalSection(&_cSection); try { WatchData* watchData = new WatchData(path, notifyFilter, watchSubdirs, _completionPort); if (watchData == 0) throw (DWORD)8; //ERROR_NOT_ENOUGH_MEMORY int watchId = watchData->getId(); _wid2WatchData[watchId] = watchData; watchDirectory(watchData); LeaveCriticalSection(&_cSection); return watchId; } catch (DWORD err) { error = err; LeaveCriticalSection(&_cSection); return 0; } } WatchData* Win32FSHook::find(int wd) { WatchData* watchData = 0; EnterCriticalSection(&_cSection); map if (it != _wid2WatchData.end()) { watchData = it->second; } LeaveCriticalSection(&_cSection); return watchData; } DWORD WINAPI Win32FSHook::mainLoop(LPVOID lpParam) { // debug("mainLoop starts"); Win32FSHook* _this = (Win32FSHook*)lpParam; HANDLE hPort = _this->_completionPort; DWORD dwNoOfBytes = 0; ULONG_PTR ulKey = 0; OVERLAPPED* pov = NULL; WCHAR name[4096]; while (_this->_isRunning) { pov = NULL; BOOL fSuccess = GetQueuedCompletionStatus( hPort, // Completion port handle &dwNoOfBytes, // Bytes transferred &ulKey, &pov, // OVERLAPPED structure INFINITE // Notification time-out interval ); if (fSuccess) { if (dwNoOfBytes == 0) continue; // happens when removing a watch some times. if (dwNoOfBytes == EXIT_SIGNAL) continue; if (dwNoOfBytes == DELETE_WD_SIGNAL) { WatchData* wd = (WatchData*)ulKey; delete wd; continue; } int wd = (int)ulKey; // EnterCriticalSection(&_this->_cSection); WatchData* watchData = _this->find(wd); if (!watchData) { // log("mainLoop : ignoring event for watch id %d, no longer in wid2WatchData map", wd); continue; } const char* buffer = watchData->getBuffer(); // char buffer[watchData->getBufferSize()]; // memcpy(buffer, watchData->getBuffer(), dwNoOfBytes); // LeaveCriticalSection(&_this->_cSection); FILE_NOTIFY_INFORMATION* event; DWORD offset = 0; do { event = (FILE_NOTIFY_INFORMATION*)(buffer + offset); int action = event->Action; DWORD len = event->FileNameLength / sizeof(WCHAR); for (DWORD k = 0; k < len && k < (sizeof(name) - sizeof(WCHAR)) / sizeof(WCHAR); k++) { name[k] = event->FileName[k]; } name[len] = 0; _this->postEvent(new Event(watchData->getId(), action, watchData->getPath(), name)); offset += event->NextEntryOffset; } while (event->NextEntryOffset != 0); int res = watchData->watchDirectory(); if (res != 0) { // log("Error watching dir %s : %d",watchData->getPath(), res); } } else { // log("GetQueuedCompletionStatus returned an error"); } } // debug("mainLoop exits"); return 0; } void Win32FSHook::watchDirectory(WatchData* wd) { // debug("Watching %ls", wd->getPath()); int res = wd->watchDirectory(); if (res != 0) { // log("Error watching dir %ls : %d",wd->getPath(), res); } } void Win32FSHook::postEvent(Event* event) { EnterCriticalSection(&_eventQueueLock); _eventQueue.push(event); LeaveCriticalSection(&_eventQueueLock); SetEvent(_eventQueueEvent); } DWORD WINAPI Win32FSHook::eventLoop(LPVOID lpParam) { // debug("eventLoop starts"); Win32FSHook* _this = (Win32FSHook*)lpParam; queue while (1) { // quickly copy to local queue, minimizing the time holding the lock EnterCriticalSection(&_this->_eventQueueLock); while (_this->_eventQueue.size() > 0) { Event* e = _this->_eventQueue.front(); local.push(e); _this->_eventQueue.pop(); } LeaveCriticalSection(&_this->_eventQueueLock); while (local.size() > 0) { Event* e = local.front(); local.pop(); if (_this->_isRunning) { _this->_callback(e->_watchID, e->_action, e->_rootPath, e->_filePath); } delete e; } if (_this->_isRunning) { WaitForSingleObjectEx(_this->_eventQueueEvent, INFINITE, TRUE); } else break; } // debug("eventLoop exits"); return 0; } main.cpp: #include #include #include #include "Win32FSHook.h" //因为文件钩子回调的参数是wchar_t *类型,为了让你的程序正常获取到参数的意思,要转成char *类型 //说明:待转换的字符串是wchar,返回值保存在m_char开辟的空间里 char* wchar2char(const wchar_t* wchar, char* m_char) { int len = WideCharToMultiByte(CP_ACP, 0, wchar, wcslen(wchar), NULL, 0, NULL, NULL); WideCharToMultiByte(CP_ACP, 0, wchar, wcslen(wchar), m_char, len, NULL, NULL); m_char[len] = ' '; return m_char; } //因为添加文件钩子监听目标文件夹的时候,要用wchar_t *类型传参,为了方便,直接写个char *到wchar_t *的转换函数 //说明:待转换的字符串是cchar,返回值保存在m_wchar开辟的空间里 wchar_t* char2wchar(const char* cchar, wchar_t* m_wchar) { int len = MultiByteToWideChar(CP_ACP, 0, cchar, strlen(cchar), NULL, 0); MultiByteToWideChar(CP_ACP, 0, cchar, strlen(cchar), m_wchar, len); m_wchar[len] = ' '; return m_wchar; } void win32FSCallback(int watchID, int action, const WCHAR* rootPath, const WCHAR* filePath) { char tmp[256]; //windows系统文件名只需要256个字节就可以了 char filename[256]; strcpy(filename, wchar2char(rootPath, tmp)); strcat(filename, wchar2char(filePath, tmp)); //把根目录和相对根目录的文件名拼起来就是完整的文件名了 //std::cout<<"watchID: "< switch (action) { case 1: { std::cout << "创建:" << filename << std::endl; break; } case 2: { std::cout << "删除:" << filename << std::endl; break; } case 3: { std::cout << "复制:" << filename << std::endl; break; } case 4: //在4时别急着换行,因为windows在回调了4之后,就会回调5,拼起来意思才完整 { std::cout << "重命名原文件名:" << filename; break; } case 5: { std::cout << ",新文件名:" << filename << std::endl; break; } default: //其他的一些操作,因为没有文档,我也不知道还有没有其他的action { std::cout << "操作代码'" << action << "':" << filename << std::endl; } } } int main() { int watchID; Win32FSHook win32FSHook; win32FSHook.init(win32FSCallback); DWORD err; wchar_t path[256]; watchID = win32FSHook.add_watch(char2wchar("C:\", path), 1 | 2 | 4 | 8, true, err); if (err == 2) { std::cout << "该目录无法监听" << std::endl; } else { std::cout << "开始监听,按下回车结束监听" << std::endl; getchar(); win32FSHook.remove_watch(watchID); std::cout << "结束监听,按下回车退出程序" << std::endl; getchar(); } return 0; } 结果: |



