From 9750786a5c03b5ce3ea22b240d1b3cd34990856b Mon Sep 17 00:00:00 2001 From: Li Jin Date: Wed, 9 Apr 2025 17:40:13 +0800 Subject: Updated efsw. Fixed issue #204. --- src/3rdParty/efsw/WatcherWin32.cpp | 214 +++++++++++++++++++++++++++++++------ 1 file changed, 184 insertions(+), 30 deletions(-) mode change 100755 => 100644 src/3rdParty/efsw/WatcherWin32.cpp (limited to 'src/3rdParty/efsw/WatcherWin32.cpp') diff --git a/src/3rdParty/efsw/WatcherWin32.cpp b/src/3rdParty/efsw/WatcherWin32.cpp old mode 100755 new mode 100644 index 3e8bcc7..712419e --- a/src/3rdParty/efsw/WatcherWin32.cpp +++ b/src/3rdParty/efsw/WatcherWin32.cpp @@ -1,34 +1,114 @@ +#include #include #include #if EFSW_PLATFORM == EFSW_PLATFORM_WIN32 +#include + namespace efsw { -/// Unpacks events and passes them to a user defined callback. -void CALLBACK WatchCallback( DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped ) { - if ( dwNumberOfBytesTransfered == 0 || NULL == lpOverlapped ) { - return; +struct EFSW_FILE_NOTIFY_EXTENDED_INFORMATION_EX { + DWORD NextEntryOffset; + DWORD Action; + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastModificationTime; + LARGE_INTEGER LastChangeTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER AllocatedLength; + LARGE_INTEGER FileSize; + DWORD FileAttributes; + DWORD ReparsePointTag; + LARGE_INTEGER FileId; + LARGE_INTEGER ParentFileId; + DWORD FileNameLength; + WCHAR FileName[1]; +}; + +typedef EFSW_FILE_NOTIFY_EXTENDED_INFORMATION_EX* EFSW_PFILE_NOTIFY_EXTENDED_INFORMATION_EX; + +typedef BOOL( WINAPI* EFSW_LPREADDIRECTORYCHANGESEXW )( HANDLE hDirectory, LPVOID lpBuffer, + DWORD nBufferLength, BOOL bWatchSubtree, + DWORD dwNotifyFilter, LPDWORD lpBytesReturned, + LPOVERLAPPED lpOverlapped, LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine, + DWORD ReadDirectoryNotifyInformationClass ); + +static EFSW_LPREADDIRECTORYCHANGESEXW pReadDirectoryChangesExW = NULL; + +#define EFSW_ReadDirectoryNotifyExtendedInformation 2 + +static void initReadDirectoryChangesEx() { + static bool hasInit = false; + if ( !hasInit ) { + hasInit = true; + + HMODULE hModule = GetModuleHandleW( L"Kernel32.dll" ); + if ( !hModule ) + return; + + pReadDirectoryChangesExW = + (EFSW_LPREADDIRECTORYCHANGESEXW)GetProcAddress( hModule, "ReadDirectoryChangesExW" ); } +} - char szFile[MAX_PATH]; +void WatchCallbackOld( WatcherWin32* pWatch ) { PFILE_NOTIFY_INFORMATION pNotify; - WatcherStructWin32* tWatch = (WatcherStructWin32*)lpOverlapped; - WatcherWin32* pWatch = tWatch->Watch; size_t offset = 0; - do { bool skip = false; pNotify = (PFILE_NOTIFY_INFORMATION)&pWatch->Buffer[offset]; offset += pNotify->NextEntryOffset; + int count = + WideCharToMultiByte( CP_UTF8, 0, pNotify->FileName, + pNotify->FileNameLength / sizeof( WCHAR ), NULL, 0, NULL, NULL ); + if ( count == 0 ) + continue; + + std::string nfile( count, '\0' ); + + count = WideCharToMultiByte( CP_UTF8, 0, pNotify->FileName, + pNotify->FileNameLength / sizeof( WCHAR ), &nfile[0], count, + NULL, NULL ); + + if ( FILE_ACTION_MODIFIED == pNotify->Action ) { + FileInfo fifile( std::string( pWatch->DirName ) + nfile ); + + if ( pWatch->LastModifiedEvent.file.ModificationTime == fifile.ModificationTime && + pWatch->LastModifiedEvent.file.Size == fifile.Size && + pWatch->LastModifiedEvent.fileName == nfile ) { + skip = true; + } + + pWatch->LastModifiedEvent.fileName = nfile; + pWatch->LastModifiedEvent.file = fifile; + } + + if ( !skip ) { + pWatch->Watch->handleAction( pWatch, nfile, pNotify->Action ); + } + } while ( pNotify->NextEntryOffset != 0 ); +} + +void WatchCallbackEx( WatcherWin32* pWatch ) { + EFSW_PFILE_NOTIFY_EXTENDED_INFORMATION_EX pNotify; + size_t offset = 0; + do { + bool skip = false; + + pNotify = (EFSW_PFILE_NOTIFY_EXTENDED_INFORMATION_EX)&pWatch->Buffer[offset]; + offset += pNotify->NextEntryOffset; + int count = + WideCharToMultiByte( CP_UTF8, 0, pNotify->FileName, + pNotify->FileNameLength / sizeof( WCHAR ), NULL, 0, NULL, NULL ); + if ( count == 0 ) + continue; - int count = WideCharToMultiByte( CP_UTF8, 0, pNotify->FileName, - pNotify->FileNameLength / sizeof( WCHAR ), szFile, - MAX_PATH - 1, NULL, NULL ); - szFile[count] = TEXT( '\0' ); + std::string nfile( count, '\0' ); - std::string nfile( szFile ); + count = WideCharToMultiByte( CP_UTF8, 0, pNotify->FileName, + pNotify->FileNameLength / sizeof( WCHAR ), &nfile[0], count, + NULL, NULL ); if ( FILE_ACTION_MODIFIED == pNotify->Action ) { FileInfo fifile( std::string( pWatch->DirName ) + nfile ); @@ -41,12 +121,59 @@ void CALLBACK WatchCallback( DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOve pWatch->LastModifiedEvent.fileName = nfile; pWatch->LastModifiedEvent.file = fifile; + } else if ( FILE_ACTION_RENAMED_OLD_NAME == pNotify->Action ) { + pWatch->OldFiles.emplace_back( nfile, pNotify->FileId ); + skip = true; + } else if ( FILE_ACTION_RENAMED_NEW_NAME == pNotify->Action ) { + std::string oldFile; + LARGE_INTEGER oldFileId{}; + + for ( auto it = pWatch->OldFiles.begin(); it != pWatch->OldFiles.end(); ++it ) { + if ( it->second.QuadPart == pNotify->FileId.QuadPart ) { + oldFile = it->first; + oldFileId = it->second; + it = pWatch->OldFiles.erase( it ); + break; + } + } + + if ( oldFile.empty() ) { + pWatch->Watch->handleAction( pWatch, nfile, FILE_ACTION_ADDED ); + skip = true; + } else { + pWatch->Watch->handleAction( pWatch, oldFile, FILE_ACTION_RENAMED_OLD_NAME ); + } } if ( !skip ) { pWatch->Watch->handleAction( pWatch, nfile, pNotify->Action ); } } while ( pNotify->NextEntryOffset != 0 ); +} + +/// Unpacks events and passes them to a user defined callback. +void CALLBACK WatchCallback( DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped ) { + if ( NULL == lpOverlapped ) { + return; + } + + WatcherStructWin32* tWatch = (WatcherStructWin32*)lpOverlapped; + WatcherWin32* pWatch = tWatch->Watch; + + if ( dwNumberOfBytesTransfered == 0 ) { + if ( nullptr != pWatch && !pWatch->StopNow ) { + RefreshWatch( tWatch ); + } else { + return; + } + } + + // Fork watch depending on the Windows API supported + if ( pWatch->Extended ) { + WatchCallbackEx( pWatch ); + } else { + WatchCallbackOld( pWatch ); + } if ( !pWatch->StopNow ) { RefreshWatch( tWatch ); @@ -54,11 +181,40 @@ void CALLBACK WatchCallback( DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOve } /// Refreshes the directory monitoring. -bool RefreshWatch( WatcherStructWin32* pWatch ) { - return ReadDirectoryChangesW( pWatch->Watch->DirHandle, pWatch->Watch->Buffer, - sizeof( pWatch->Watch->Buffer ), pWatch->Watch->Recursive, - pWatch->Watch->NotifyFilter, NULL, &pWatch->Overlapped, - NULL ) != 0; +RefreshResult RefreshWatch( WatcherStructWin32* pWatch ) { + initReadDirectoryChangesEx(); + + bool bRet = false; + RefreshResult ret = RefreshResult::Failed; + pWatch->Watch->Extended = false; + + if ( pReadDirectoryChangesExW ) { + bRet = pReadDirectoryChangesExW( pWatch->Watch->DirHandle, pWatch->Watch->Buffer.data(), + (DWORD)pWatch->Watch->Buffer.size(), pWatch->Watch->Recursive, + pWatch->Watch->NotifyFilter, NULL, &pWatch->Overlapped, + NULL, EFSW_ReadDirectoryNotifyExtendedInformation ) != 0; + if ( bRet ) { + ret = RefreshResult::SucessEx; + pWatch->Watch->Extended = true; + } + } + + if ( !bRet ) { + bRet = ReadDirectoryChangesW( pWatch->Watch->DirHandle, pWatch->Watch->Buffer.data(), + (DWORD)pWatch->Watch->Buffer.size(), pWatch->Watch->Recursive, + pWatch->Watch->NotifyFilter, NULL, &pWatch->Overlapped, + NULL ) != 0; + + if ( bRet ) + ret = RefreshResult::Success; + } + + if ( !bRet ) { + std::string error = std::to_string( GetLastError() ); + Errors::Log::createLastError( Errors::WatcherFailed, error ); + } + + return ret; } /// Stops monitoring a directory. @@ -70,19 +226,17 @@ void DestroyWatch( WatcherStructWin32* pWatch ) { CloseHandle( pWatch->Watch->DirHandle ); efSAFE_DELETE_ARRAY( pWatch->Watch->DirName ); efSAFE_DELETE( pWatch->Watch ); + efSAFE_DELETE( pWatch ); } } /// Starts monitoring a directory. -WatcherStructWin32* CreateWatch( LPCWSTR szDirectory, bool recursive, DWORD NotifyFilter, - HANDLE iocp ) { - WatcherStructWin32* tWatch; - size_t ptrsize = sizeof( *tWatch ); - tWatch = static_cast( - HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, ptrsize ) ); - - WatcherWin32* pWatch = new WatcherWin32(); - tWatch->Watch = pWatch; +WatcherStructWin32* CreateWatch( LPCWSTR szDirectory, bool recursive, + DWORD bufferSize, DWORD notifyFilter, HANDLE iocp ) { + WatcherStructWin32* tWatch = new WatcherStructWin32(); + WatcherWin32* pWatch = new WatcherWin32(bufferSize); + if (tWatch) + tWatch->Watch = pWatch; pWatch->DirHandle = CreateFileW( szDirectory, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, @@ -90,17 +244,17 @@ WatcherStructWin32* CreateWatch( LPCWSTR szDirectory, bool recursive, DWORD Noti if ( pWatch->DirHandle != INVALID_HANDLE_VALUE && CreateIoCompletionPort( pWatch->DirHandle, iocp, 0, 1 ) ) { - pWatch->NotifyFilter = NotifyFilter; + pWatch->NotifyFilter = notifyFilter; pWatch->Recursive = recursive; - if ( RefreshWatch( tWatch ) ) { + if ( RefreshResult::Failed != RefreshWatch( tWatch ) ) { return tWatch; } } CloseHandle( pWatch->DirHandle ); efSAFE_DELETE( pWatch->Watch ); - HeapFree( GetProcessHeap(), 0, tWatch ); + efSAFE_DELETE( tWatch ); return NULL; } -- cgit v1.2.3-55-g6feb