#include #include #include #include #include #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( 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( 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( watch )->DirName ); std::string realFilename = filename; std::size_t sepPos = filename.find_last_of( "/\\" ); std::string oldFolderPath = static_cast( 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( 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( 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 FileWatcherWin32::directories() { std::list 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; } } // namespace efsw #endif