362 lines
8.9 KiB
C++
362 lines
8.9 KiB
C++
/**
|
|
Copyright (c) 2009 James Wynn (james@jameswynn.com)
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
THE SOFTWARE.
|
|
*/
|
|
|
|
#include "FileWatcherWin32.h"
|
|
|
|
#if FILEWATCHER_PLATFORM == FILEWATCHER_PLATFORM_WIN32
|
|
|
|
#include <windows.h>
|
|
|
|
#if defined(_MSC_VER)
|
|
#pragma comment(lib, "comctl32.lib")
|
|
#pragma comment(lib, "user32.lib")
|
|
#pragma comment(lib, "ole32.lib")
|
|
|
|
// disable secure warnings
|
|
#pragma warning (disable: 4996)
|
|
#endif
|
|
|
|
namespace FW
|
|
{
|
|
/// Internal watch data
|
|
struct WatchStruct
|
|
{
|
|
OVERLAPPED mOverlapped;
|
|
HANDLE mDirHandle;
|
|
BYTE mBuffer[32 * 1024];
|
|
LPARAM lParam;
|
|
DWORD mNotifyFilter;
|
|
bool mStopNow;
|
|
FileWatcherImpl* mFileWatcher;
|
|
FileWatchListener* mFileWatchListener;
|
|
char* mDirName;
|
|
WatchID mWatchid;
|
|
bool mIsRecursive;
|
|
};
|
|
|
|
#pragma region Internal Functions
|
|
|
|
// forward decl
|
|
bool RefreshWatch(WatchStruct* pWatch, bool _clear = false);
|
|
|
|
/// Unpacks events and passes them to a user defined callback.
|
|
void CALLBACK WatchCallback(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped)
|
|
{
|
|
#ifndef WIN32_FW_USE_FINDFIRST_API
|
|
char szFile[MAX_PATH];
|
|
PFILE_NOTIFY_INFORMATION pNotify;
|
|
WatchStruct* pWatch = (WatchStruct*) lpOverlapped;
|
|
size_t offset = 0;
|
|
|
|
if(dwNumberOfBytesTransfered == 0)
|
|
return;
|
|
|
|
if (dwErrorCode == ERROR_SUCCESS)
|
|
{
|
|
do
|
|
{
|
|
pNotify = (PFILE_NOTIFY_INFORMATION) &pWatch->mBuffer[offset];
|
|
offset += pNotify->NextEntryOffset;
|
|
|
|
|
|
int count = WideCharToMultiByte(CP_ACP, 0, pNotify->FileName,
|
|
pNotify->FileNameLength / sizeof(WCHAR),
|
|
szFile, MAX_PATH - 1, NULL, NULL);
|
|
szFile[count] = TEXT('\0');
|
|
|
|
|
|
pWatch->mFileWatcher->handleAction(pWatch, szFile, pNotify->Action);
|
|
|
|
} while (pNotify->NextEntryOffset != 0);
|
|
}
|
|
|
|
if (!pWatch->mStopNow)
|
|
{
|
|
RefreshWatch(pWatch);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/// Refreshes the directory monitoring.
|
|
bool RefreshWatch(WatchStruct* pWatch, bool _clear)
|
|
{
|
|
#ifndef WIN32_FW_USE_FINDFIRST_API
|
|
return ReadDirectoryChangesW(
|
|
pWatch->mDirHandle, pWatch->mBuffer, sizeof(pWatch->mBuffer), pWatch->mIsRecursive,
|
|
pWatch->mNotifyFilter, NULL, &pWatch->mOverlapped, _clear ? 0 : WatchCallback) != 0;
|
|
#else
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
/// Stops monitoring a directory.
|
|
void DestroyWatch(WatchStruct* pWatch)
|
|
{
|
|
if (pWatch)
|
|
{
|
|
pWatch->mStopNow = TRUE;
|
|
|
|
#ifndef WIN32_FW_USE_FINDFIRST_API
|
|
CancelIo(pWatch->mDirHandle);
|
|
|
|
RefreshWatch(pWatch, true);
|
|
|
|
if (!HasOverlappedIoCompleted(&pWatch->mOverlapped))
|
|
{
|
|
SleepEx(5, TRUE);
|
|
}
|
|
|
|
CloseHandle(pWatch->mOverlapped.hEvent);
|
|
CloseHandle(pWatch->mDirHandle);
|
|
#endif
|
|
|
|
delete[] pWatch->mDirName;
|
|
HeapFree(GetProcessHeap(), 0, pWatch);
|
|
}
|
|
}
|
|
|
|
/// Starts monitoring a directory.
|
|
WatchStruct* CreateWatch(const char* szDirectory, bool recursive, DWORD mNotifyFilter)
|
|
{
|
|
WatchStruct* pWatch;
|
|
size_t ptrsize = sizeof(*pWatch);
|
|
pWatch = static_cast<WatchStruct*>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ptrsize));
|
|
|
|
#ifndef WIN32_FW_USE_FINDFIRST_API
|
|
pWatch->mDirHandle = CreateFileA(szDirectory, FILE_LIST_DIRECTORY,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
|
|
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);
|
|
|
|
if (pWatch->mDirHandle != INVALID_HANDLE_VALUE)
|
|
{
|
|
pWatch->mOverlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
pWatch->mNotifyFilter = mNotifyFilter;
|
|
pWatch->mIsRecursive = recursive;
|
|
|
|
if (RefreshWatch(pWatch))
|
|
{
|
|
return pWatch;
|
|
}
|
|
else
|
|
{
|
|
CloseHandle(pWatch->mOverlapped.hEvent);
|
|
CloseHandle(pWatch->mDirHandle);
|
|
}
|
|
}
|
|
|
|
HeapFree(GetProcessHeap(), 0, pWatch);
|
|
return NULL;
|
|
#else
|
|
|
|
return pWatch;
|
|
|
|
#endif
|
|
}
|
|
|
|
#pragma endregion
|
|
|
|
//--------
|
|
FileWatcherWin32::FileWatcherWin32()
|
|
: mLastWatchID(0)
|
|
{
|
|
}
|
|
|
|
//--------
|
|
FileWatcherWin32::~FileWatcherWin32()
|
|
{
|
|
WatchMap::iterator iter = mWatches.begin();
|
|
WatchMap::iterator end = mWatches.end();
|
|
for(; iter != end; ++iter)
|
|
{
|
|
DestroyWatch(iter->second);
|
|
}
|
|
mWatches.clear();
|
|
}
|
|
|
|
//--------
|
|
WatchID FileWatcherWin32::addWatch(const String& directory, FileWatchListener* watcher, bool recursive)
|
|
{
|
|
WatchStruct* watch = CreateWatch(directory.c_str(), recursive,
|
|
FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_FILE_NAME);
|
|
|
|
#ifdef WIN32_FW_USE_FINDFIRST_API
|
|
WatchID watchid = (unsigned long)fw.add( directory.m_string );
|
|
#else
|
|
WatchID watchid = ++mLastWatchID;
|
|
#endif
|
|
|
|
if(watch)
|
|
{
|
|
watch->mWatchid = watchid;
|
|
watch->mFileWatcher = this;
|
|
watch->mFileWatchListener = watcher;
|
|
watch->mDirName = new char[directory.m_string.length()+1];
|
|
strcpy(watch->mDirName, directory.c_str());
|
|
}
|
|
|
|
|
|
mWatches.insert(std::make_pair(watchid, watch));
|
|
|
|
|
|
|
|
return watchid;
|
|
}
|
|
|
|
//--------
|
|
void FileWatcherWin32::removeWatch(const String& directory)
|
|
{
|
|
WatchMap::iterator iter = mWatches.begin();
|
|
WatchMap::iterator end = mWatches.end();
|
|
for(; iter != end; ++iter)
|
|
{
|
|
if(directory == iter->second->mDirName)
|
|
{
|
|
removeWatch(iter->first);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------
|
|
void FileWatcherWin32::removeWatch(WatchID watchid)
|
|
{
|
|
WatchMap::iterator iter = mWatches.find(watchid);
|
|
|
|
if(iter == mWatches.end())
|
|
return;
|
|
|
|
WatchStruct* watch = iter->second;
|
|
|
|
#ifdef WIN32_FW_USE_FINDFIRST_API
|
|
fw.remove(watch->mWatchid);
|
|
#endif
|
|
|
|
mWatches.erase(iter);
|
|
|
|
DestroyWatch(watch);
|
|
}
|
|
|
|
//--------
|
|
void FileWatcherWin32::update()
|
|
{
|
|
#ifndef WIN32_FW_USE_FINDFIRST_API
|
|
MsgWaitForMultipleObjectsEx(0, NULL, 0, QS_ALLINPUT, MWMO_ALERTABLE);
|
|
#endif
|
|
|
|
#ifdef WIN32_FW_USE_FINDFIRST_API
|
|
static std::vector<FileWatcherWin32_AltImpl::fw_event> events;
|
|
events.clear();
|
|
|
|
fw.watch(events);
|
|
|
|
for( size_t c = 0; c != events.size(); ++c )
|
|
{
|
|
WatchMap::iterator iter = mWatches.find(events[c].id);
|
|
if(iter == mWatches.end())
|
|
continue;
|
|
|
|
|
|
switch(events[c].ty)
|
|
{
|
|
case FileWatcherWin32_AltImpl::CHANGE_SIZE:
|
|
{
|
|
std::string old_name;
|
|
DWORD fni;
|
|
std::string new_name = fw.get_event_filename( iter->second->mDirName, events[c].id, events[c].ty, fni, old_name );
|
|
|
|
if( !new_name.empty() )
|
|
{
|
|
handleAction(iter->second, new_name.c_str(), fni );
|
|
}
|
|
|
|
break;
|
|
}
|
|
case FileWatcherWin32_AltImpl::CHANGE_FILE_NAME:
|
|
{
|
|
std::string old_name;
|
|
DWORD fni;
|
|
std::string new_name = fw.get_event_filename( iter->second->mDirName, events[c].id, events[c].ty, fni, old_name );
|
|
|
|
if( !new_name.empty() )
|
|
{
|
|
//changed from-to
|
|
if( !old_name.empty() )
|
|
{
|
|
handleAction(iter->second, old_name.c_str(), FILE_ACTION_RENAMED_OLD_NAME );
|
|
handleAction(iter->second, new_name.c_str(), FILE_ACTION_RENAMED_NEW_NAME );
|
|
}
|
|
else
|
|
{
|
|
handleAction(iter->second, new_name.c_str(), fni );
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case FileWatcherWin32_AltImpl::CHANGE_LAST_WRITE:
|
|
case FileWatcherWin32_AltImpl::CHANGE_CREATION:
|
|
{
|
|
std::string old_name;
|
|
DWORD fni;
|
|
std::string new_name = fw.get_event_filename( iter->second->mDirName, events[c].id, events[c].ty, fni, old_name );
|
|
|
|
if( !new_name.empty() )
|
|
{
|
|
handleAction(iter->second, new_name.c_str(), fni );
|
|
}
|
|
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//--------
|
|
void FileWatcherWin32::handleAction(WatchStruct* watch, const String& filename, unsigned long action)
|
|
{
|
|
Action fwAction;
|
|
|
|
switch(action)
|
|
{
|
|
case FILE_ACTION_RENAMED_NEW_NAME:
|
|
case FILE_ACTION_ADDED:
|
|
fwAction = Actions::Add;
|
|
break;
|
|
case FILE_ACTION_RENAMED_OLD_NAME:
|
|
case FILE_ACTION_REMOVED:
|
|
fwAction = Actions::Delete;
|
|
break;
|
|
case FILE_ACTION_MODIFIED:
|
|
fwAction = Actions::Modified;
|
|
break;
|
|
};
|
|
|
|
watch->mFileWatchListener->handleFileAction(watch->mWatchid, watch->mDirName, filename, fwAction);
|
|
}
|
|
|
|
};//namespace FW
|
|
|
|
#endif//_WIN32
|