#include <efsw/FileWatcherWin32.hpp>
|
#include <efsw/FileSystem.hpp>
|
#include <efsw/System.hpp>
|
#include <efsw/String.hpp>
|
#include <efsw/Lock.hpp>
|
|
#if EFSW_PLATFORM == EFSW_PLATFORM_WIN32
|
|
namespace efsw
|
{
|
|
FileWatcherWin32::FileWatcherWin32( FileWatcher * parent ) :
|
FileWatcherImpl( parent ),
|
mLastWatchID(0),
|
mThread( NULL )
|
{
|
mIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1);
|
if (mIOCP && mIOCP != INVALID_HANDLE_VALUE)
|
mInitOK = true;
|
}
|
|
FileWatcherWin32::~FileWatcherWin32()
|
{
|
mInitOK = false;
|
|
if (mIOCP && mIOCP != INVALID_HANDLE_VALUE)
|
{
|
PostQueuedCompletionStatus(mIOCP, 0, reinterpret_cast<ULONG_PTR>(this), NULL);
|
}
|
|
efSAFE_DELETE( mThread );
|
|
removeAllWatches();
|
|
CloseHandle(mIOCP);
|
}
|
|
WatchID FileWatcherWin32::addWatch(const std::string& directory, FileWatchListener* watcher, bool recursive)
|
{
|
std::string dir( directory );
|
|
FileInfo fi( dir );
|
|
if ( !fi.isDirectory() )
|
{
|
return Errors::Log::createLastError( Errors::FileNotFound, dir );
|
}
|
else if ( !fi.isReadable() )
|
{
|
return Errors::Log::createLastError( Errors::FileNotReadable, dir );
|
}
|
|
FileSystem::dirAddSlashAtEnd( dir );
|
|
Lock lock( mWatchesLock );
|
|
if ( pathInWatches( dir ) )
|
{
|
return Errors::Log::createLastError( Errors::FileRepeated, dir );
|
}
|
|
WatchID watchid = ++mLastWatchID;
|
|
WatcherStructWin32 * watch = CreateWatch( String::fromUtf8( dir ).toWideString().c_str(), recursive,
|
FILE_NOTIFY_CHANGE_CREATION |
|
FILE_NOTIFY_CHANGE_LAST_WRITE |
|
FILE_NOTIFY_CHANGE_FILE_NAME |
|
FILE_NOTIFY_CHANGE_DIR_NAME |
|
FILE_NOTIFY_CHANGE_SIZE,
|
mIOCP
|
);
|
|
if( NULL == watch )
|
{
|
return Errors::Log::createLastError( Errors::FileNotFound, dir );
|
}
|
|
// Add the handle to the handles vector
|
watch->Watch->ID = watchid;
|
watch->Watch->Watch = this;
|
watch->Watch->Listener = watcher;
|
watch->Watch->DirName = new char[dir.length()+1];
|
strcpy(watch->Watch->DirName, dir.c_str());
|
|
mWatches.insert( watch );
|
|
return watchid;
|
}
|
|
void FileWatcherWin32::removeWatch(const std::string& directory)
|
{
|
Lock lock( mWatchesLock );
|
|
Watches::iterator iter = mWatches.begin();
|
|
for(; iter != mWatches.end(); ++iter)
|
{
|
if(directory == (*iter)->Watch->DirName)
|
{
|
removeWatch(*iter);
|
break;
|
}
|
}
|
}
|
|
void FileWatcherWin32::removeWatch(WatchID watchid)
|
{
|
Lock lock( mWatchesLock );
|
|
Watches::iterator iter = mWatches.begin();
|
|
for(; iter != mWatches.end(); ++iter)
|
{
|
// Find the watch ID
|
if ( (*iter)->Watch->ID == watchid )
|
{
|
removeWatch(*iter);
|
return;
|
}
|
}
|
}
|
|
void FileWatcherWin32::removeWatch(WatcherStructWin32* watch)
|
{
|
Lock lock( mWatchesLock );
|
|
DestroyWatch(watch);
|
mWatches.erase(watch);
|
}
|
|
void FileWatcherWin32::watch()
|
{
|
if ( NULL == mThread )
|
{
|
mThread = new Thread( &FileWatcherWin32::run, this );
|
mThread->launch();
|
}
|
}
|
|
void FileWatcherWin32::removeAllWatches()
|
{
|
Lock lock( mWatchesLock );
|
|
Watches::iterator iter = mWatches.begin();
|
|
for( ; iter != mWatches.end(); ++iter )
|
{
|
DestroyWatch((*iter));
|
}
|
|
mWatches.clear();
|
}
|
|
void FileWatcherWin32::run()
|
{
|
do
|
{
|
if ( mInitOK && !mWatches.empty() )
|
{
|
DWORD numOfBytes = 0;
|
OVERLAPPED* ov = NULL;
|
ULONG_PTR compKey = 0;
|
BOOL res = FALSE;
|
|
while ( ( res = GetQueuedCompletionStatus( mIOCP, &numOfBytes, &compKey, &ov, INFINITE ) ) != FALSE )
|
{
|
if ( compKey != 0 && compKey == reinterpret_cast<ULONG_PTR>( this ) )
|
{
|
break;
|
}
|
else
|
{
|
Lock lock( mWatchesLock );
|
WatchCallback( numOfBytes, ov );
|
}
|
}
|
}
|
else
|
{
|
System::sleep( 10 );
|
}
|
} while ( mInitOK );
|
|
removeAllWatches();
|
}
|
|
void FileWatcherWin32::handleAction(Watcher* watch, const std::string& filename, unsigned long action, std::string /*oldFilename*/)
|
{
|
Action fwAction;
|
|
switch(action)
|
{
|
case FILE_ACTION_RENAMED_OLD_NAME:
|
watch->OldFileName = filename;
|
return;
|
case FILE_ACTION_ADDED:
|
fwAction = Actions::Add;
|
break;
|
case FILE_ACTION_RENAMED_NEW_NAME:
|
{
|
fwAction = Actions::Moved;
|
|
std::string fpath( watch->Directory + filename );
|
|
// Update the directory path
|
if ( watch->Recursive && FileSystem::isDirectory( fpath ) )
|
{
|
// Update the new directory path
|
std::string opath( watch->Directory + watch->OldFileName );
|
FileSystem::dirAddSlashAtEnd( opath );
|
FileSystem::dirAddSlashAtEnd( fpath );
|
|
for ( Watches::iterator it = mWatches.begin(); it != mWatches.end(); ++it )
|
{
|
if ( (*it)->Watch->Directory == opath )
|
{
|
(*it)->Watch->Directory = fpath;
|
|
break;
|
}
|
}
|
}
|
|
std::string folderPath( static_cast<WatcherWin32*>( watch )->DirName );
|
std::string realFilename = filename;
|
std::size_t sepPos = filename.find_last_of("/\\");
|
std::string oldFolderPath = static_cast<WatcherWin32*>( watch )->DirName + watch->OldFileName.substr( 0, watch->OldFileName.find_last_of( "/\\" ) );
|
|
if ( sepPos != std::string::npos )
|
{
|
folderPath += filename.substr( 0, sepPos );
|
realFilename = filename.substr( sepPos + 1 );
|
}
|
|
if ( folderPath == oldFolderPath )
|
{
|
watch->Listener->handleFileAction(watch->ID, folderPath, realFilename, fwAction, FileSystem::fileNameFromPath(watch->OldFileName));
|
}
|
else
|
{
|
watch->Listener->handleFileAction(watch->ID, static_cast<WatcherWin32*>( watch )->DirName, filename, fwAction, watch->OldFileName);
|
}
|
return;
|
}
|
case FILE_ACTION_REMOVED:
|
fwAction = Actions::Delete;
|
break;
|
case FILE_ACTION_MODIFIED:
|
fwAction = Actions::Modified;
|
break;
|
default:
|
return;
|
};
|
|
std::string folderPath( static_cast<WatcherWin32*>( watch )->DirName );
|
std::string realFilename = filename;
|
std::size_t sepPos = filename.find_last_of("/\\");
|
|
if ( sepPos != std::string::npos )
|
{
|
folderPath += filename.substr( 0, sepPos );
|
realFilename = filename.substr( sepPos + 1 );
|
}
|
|
watch->Listener->handleFileAction(watch->ID, folderPath, realFilename, fwAction);
|
}
|
|
std::list<std::string> FileWatcherWin32::directories()
|
{
|
std::list<std::string> dirs;
|
|
Lock lock( mWatchesLock );
|
|
for ( Watches::iterator it = mWatches.begin(); it != mWatches.end(); ++it )
|
{
|
dirs.push_back( std::string( (*it)->Watch->DirName ) );
|
}
|
|
return dirs;
|
}
|
|
bool FileWatcherWin32::pathInWatches( const std::string& path )
|
{
|
Lock lock( mWatchesLock );
|
|
for ( Watches::iterator it = mWatches.begin(); it != mWatches.end(); ++it )
|
{
|
if ( (*it)->Watch->DirName == path )
|
{
|
return true;
|
}
|
}
|
|
return false;
|
}
|
|
}
|
|
#endif
|