diff options
Diffstat (limited to 'src/3rdParty/efsw/WatcherWin32.cpp')
-rw-r--r--[-rwxr-xr-x] | src/3rdParty/efsw/WatcherWin32.cpp | 214 |
1 files changed, 184 insertions, 30 deletions
diff --git a/src/3rdParty/efsw/WatcherWin32.cpp b/src/3rdParty/efsw/WatcherWin32.cpp index 3e8bcc7..712419e 100755..100644 --- a/src/3rdParty/efsw/WatcherWin32.cpp +++ b/src/3rdParty/efsw/WatcherWin32.cpp | |||
@@ -1,34 +1,114 @@ | |||
1 | #include <efsw/Debug.hpp> | ||
1 | #include <efsw/String.hpp> | 2 | #include <efsw/String.hpp> |
2 | #include <efsw/WatcherWin32.hpp> | 3 | #include <efsw/WatcherWin32.hpp> |
3 | 4 | ||
4 | #if EFSW_PLATFORM == EFSW_PLATFORM_WIN32 | 5 | #if EFSW_PLATFORM == EFSW_PLATFORM_WIN32 |
5 | 6 | ||
7 | #include <algorithm> | ||
8 | |||
6 | namespace efsw { | 9 | namespace efsw { |
7 | 10 | ||
8 | /// Unpacks events and passes them to a user defined callback. | 11 | struct EFSW_FILE_NOTIFY_EXTENDED_INFORMATION_EX { |
9 | void CALLBACK WatchCallback( DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped ) { | 12 | DWORD NextEntryOffset; |
10 | if ( dwNumberOfBytesTransfered == 0 || NULL == lpOverlapped ) { | 13 | DWORD Action; |
11 | return; | 14 | LARGE_INTEGER CreationTime; |
15 | LARGE_INTEGER LastModificationTime; | ||
16 | LARGE_INTEGER LastChangeTime; | ||
17 | LARGE_INTEGER LastAccessTime; | ||
18 | LARGE_INTEGER AllocatedLength; | ||
19 | LARGE_INTEGER FileSize; | ||
20 | DWORD FileAttributes; | ||
21 | DWORD ReparsePointTag; | ||
22 | LARGE_INTEGER FileId; | ||
23 | LARGE_INTEGER ParentFileId; | ||
24 | DWORD FileNameLength; | ||
25 | WCHAR FileName[1]; | ||
26 | }; | ||
27 | |||
28 | typedef EFSW_FILE_NOTIFY_EXTENDED_INFORMATION_EX* EFSW_PFILE_NOTIFY_EXTENDED_INFORMATION_EX; | ||
29 | |||
30 | typedef BOOL( WINAPI* EFSW_LPREADDIRECTORYCHANGESEXW )( HANDLE hDirectory, LPVOID lpBuffer, | ||
31 | DWORD nBufferLength, BOOL bWatchSubtree, | ||
32 | DWORD dwNotifyFilter, LPDWORD lpBytesReturned, | ||
33 | LPOVERLAPPED lpOverlapped, LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine, | ||
34 | DWORD ReadDirectoryNotifyInformationClass ); | ||
35 | |||
36 | static EFSW_LPREADDIRECTORYCHANGESEXW pReadDirectoryChangesExW = NULL; | ||
37 | |||
38 | #define EFSW_ReadDirectoryNotifyExtendedInformation 2 | ||
39 | |||
40 | static void initReadDirectoryChangesEx() { | ||
41 | static bool hasInit = false; | ||
42 | if ( !hasInit ) { | ||
43 | hasInit = true; | ||
44 | |||
45 | HMODULE hModule = GetModuleHandleW( L"Kernel32.dll" ); | ||
46 | if ( !hModule ) | ||
47 | return; | ||
48 | |||
49 | pReadDirectoryChangesExW = | ||
50 | (EFSW_LPREADDIRECTORYCHANGESEXW)GetProcAddress( hModule, "ReadDirectoryChangesExW" ); | ||
12 | } | 51 | } |
52 | } | ||
13 | 53 | ||
14 | char szFile[MAX_PATH]; | 54 | void WatchCallbackOld( WatcherWin32* pWatch ) { |
15 | PFILE_NOTIFY_INFORMATION pNotify; | 55 | PFILE_NOTIFY_INFORMATION pNotify; |
16 | WatcherStructWin32* tWatch = (WatcherStructWin32*)lpOverlapped; | ||
17 | WatcherWin32* pWatch = tWatch->Watch; | ||
18 | size_t offset = 0; | 56 | size_t offset = 0; |
19 | |||
20 | do { | 57 | do { |
21 | bool skip = false; | 58 | bool skip = false; |
22 | 59 | ||
23 | pNotify = (PFILE_NOTIFY_INFORMATION)&pWatch->Buffer[offset]; | 60 | pNotify = (PFILE_NOTIFY_INFORMATION)&pWatch->Buffer[offset]; |
24 | offset += pNotify->NextEntryOffset; | 61 | offset += pNotify->NextEntryOffset; |
62 | int count = | ||
63 | WideCharToMultiByte( CP_UTF8, 0, pNotify->FileName, | ||
64 | pNotify->FileNameLength / sizeof( WCHAR ), NULL, 0, NULL, NULL ); | ||
65 | if ( count == 0 ) | ||
66 | continue; | ||
67 | |||
68 | std::string nfile( count, '\0' ); | ||
69 | |||
70 | count = WideCharToMultiByte( CP_UTF8, 0, pNotify->FileName, | ||
71 | pNotify->FileNameLength / sizeof( WCHAR ), &nfile[0], count, | ||
72 | NULL, NULL ); | ||
73 | |||
74 | if ( FILE_ACTION_MODIFIED == pNotify->Action ) { | ||
75 | FileInfo fifile( std::string( pWatch->DirName ) + nfile ); | ||
76 | |||
77 | if ( pWatch->LastModifiedEvent.file.ModificationTime == fifile.ModificationTime && | ||
78 | pWatch->LastModifiedEvent.file.Size == fifile.Size && | ||
79 | pWatch->LastModifiedEvent.fileName == nfile ) { | ||
80 | skip = true; | ||
81 | } | ||
82 | |||
83 | pWatch->LastModifiedEvent.fileName = nfile; | ||
84 | pWatch->LastModifiedEvent.file = fifile; | ||
85 | } | ||
86 | |||
87 | if ( !skip ) { | ||
88 | pWatch->Watch->handleAction( pWatch, nfile, pNotify->Action ); | ||
89 | } | ||
90 | } while ( pNotify->NextEntryOffset != 0 ); | ||
91 | } | ||
92 | |||
93 | void WatchCallbackEx( WatcherWin32* pWatch ) { | ||
94 | EFSW_PFILE_NOTIFY_EXTENDED_INFORMATION_EX pNotify; | ||
95 | size_t offset = 0; | ||
96 | do { | ||
97 | bool skip = false; | ||
98 | |||
99 | pNotify = (EFSW_PFILE_NOTIFY_EXTENDED_INFORMATION_EX)&pWatch->Buffer[offset]; | ||
100 | offset += pNotify->NextEntryOffset; | ||
101 | int count = | ||
102 | WideCharToMultiByte( CP_UTF8, 0, pNotify->FileName, | ||
103 | pNotify->FileNameLength / sizeof( WCHAR ), NULL, 0, NULL, NULL ); | ||
104 | if ( count == 0 ) | ||
105 | continue; | ||
25 | 106 | ||
26 | int count = WideCharToMultiByte( CP_UTF8, 0, pNotify->FileName, | 107 | std::string nfile( count, '\0' ); |
27 | pNotify->FileNameLength / sizeof( WCHAR ), szFile, | ||
28 | MAX_PATH - 1, NULL, NULL ); | ||
29 | szFile[count] = TEXT( '\0' ); | ||
30 | 108 | ||
31 | std::string nfile( szFile ); | 109 | count = WideCharToMultiByte( CP_UTF8, 0, pNotify->FileName, |
110 | pNotify->FileNameLength / sizeof( WCHAR ), &nfile[0], count, | ||
111 | NULL, NULL ); | ||
32 | 112 | ||
33 | if ( FILE_ACTION_MODIFIED == pNotify->Action ) { | 113 | if ( FILE_ACTION_MODIFIED == pNotify->Action ) { |
34 | FileInfo fifile( std::string( pWatch->DirName ) + nfile ); | 114 | FileInfo fifile( std::string( pWatch->DirName ) + nfile ); |
@@ -41,12 +121,59 @@ void CALLBACK WatchCallback( DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOve | |||
41 | 121 | ||
42 | pWatch->LastModifiedEvent.fileName = nfile; | 122 | pWatch->LastModifiedEvent.fileName = nfile; |
43 | pWatch->LastModifiedEvent.file = fifile; | 123 | pWatch->LastModifiedEvent.file = fifile; |
124 | } else if ( FILE_ACTION_RENAMED_OLD_NAME == pNotify->Action ) { | ||
125 | pWatch->OldFiles.emplace_back( nfile, pNotify->FileId ); | ||
126 | skip = true; | ||
127 | } else if ( FILE_ACTION_RENAMED_NEW_NAME == pNotify->Action ) { | ||
128 | std::string oldFile; | ||
129 | LARGE_INTEGER oldFileId{}; | ||
130 | |||
131 | for ( auto it = pWatch->OldFiles.begin(); it != pWatch->OldFiles.end(); ++it ) { | ||
132 | if ( it->second.QuadPart == pNotify->FileId.QuadPart ) { | ||
133 | oldFile = it->first; | ||
134 | oldFileId = it->second; | ||
135 | it = pWatch->OldFiles.erase( it ); | ||
136 | break; | ||
137 | } | ||
138 | } | ||
139 | |||
140 | if ( oldFile.empty() ) { | ||
141 | pWatch->Watch->handleAction( pWatch, nfile, FILE_ACTION_ADDED ); | ||
142 | skip = true; | ||
143 | } else { | ||
144 | pWatch->Watch->handleAction( pWatch, oldFile, FILE_ACTION_RENAMED_OLD_NAME ); | ||
145 | } | ||
44 | } | 146 | } |
45 | 147 | ||
46 | if ( !skip ) { | 148 | if ( !skip ) { |
47 | pWatch->Watch->handleAction( pWatch, nfile, pNotify->Action ); | 149 | pWatch->Watch->handleAction( pWatch, nfile, pNotify->Action ); |
48 | } | 150 | } |
49 | } while ( pNotify->NextEntryOffset != 0 ); | 151 | } while ( pNotify->NextEntryOffset != 0 ); |
152 | } | ||
153 | |||
154 | /// Unpacks events and passes them to a user defined callback. | ||
155 | void CALLBACK WatchCallback( DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped ) { | ||
156 | if ( NULL == lpOverlapped ) { | ||
157 | return; | ||
158 | } | ||
159 | |||
160 | WatcherStructWin32* tWatch = (WatcherStructWin32*)lpOverlapped; | ||
161 | WatcherWin32* pWatch = tWatch->Watch; | ||
162 | |||
163 | if ( dwNumberOfBytesTransfered == 0 ) { | ||
164 | if ( nullptr != pWatch && !pWatch->StopNow ) { | ||
165 | RefreshWatch( tWatch ); | ||
166 | } else { | ||
167 | return; | ||
168 | } | ||
169 | } | ||
170 | |||
171 | // Fork watch depending on the Windows API supported | ||
172 | if ( pWatch->Extended ) { | ||
173 | WatchCallbackEx( pWatch ); | ||
174 | } else { | ||
175 | WatchCallbackOld( pWatch ); | ||
176 | } | ||
50 | 177 | ||
51 | if ( !pWatch->StopNow ) { | 178 | if ( !pWatch->StopNow ) { |
52 | RefreshWatch( tWatch ); | 179 | RefreshWatch( tWatch ); |
@@ -54,11 +181,40 @@ void CALLBACK WatchCallback( DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOve | |||
54 | } | 181 | } |
55 | 182 | ||
56 | /// Refreshes the directory monitoring. | 183 | /// Refreshes the directory monitoring. |
57 | bool RefreshWatch( WatcherStructWin32* pWatch ) { | 184 | RefreshResult RefreshWatch( WatcherStructWin32* pWatch ) { |
58 | return ReadDirectoryChangesW( pWatch->Watch->DirHandle, pWatch->Watch->Buffer, | 185 | initReadDirectoryChangesEx(); |
59 | sizeof( pWatch->Watch->Buffer ), pWatch->Watch->Recursive, | 186 | |
60 | pWatch->Watch->NotifyFilter, NULL, &pWatch->Overlapped, | 187 | bool bRet = false; |
61 | NULL ) != 0; | 188 | RefreshResult ret = RefreshResult::Failed; |
189 | pWatch->Watch->Extended = false; | ||
190 | |||
191 | if ( pReadDirectoryChangesExW ) { | ||
192 | bRet = pReadDirectoryChangesExW( pWatch->Watch->DirHandle, pWatch->Watch->Buffer.data(), | ||
193 | (DWORD)pWatch->Watch->Buffer.size(), pWatch->Watch->Recursive, | ||
194 | pWatch->Watch->NotifyFilter, NULL, &pWatch->Overlapped, | ||
195 | NULL, EFSW_ReadDirectoryNotifyExtendedInformation ) != 0; | ||
196 | if ( bRet ) { | ||
197 | ret = RefreshResult::SucessEx; | ||
198 | pWatch->Watch->Extended = true; | ||
199 | } | ||
200 | } | ||
201 | |||
202 | if ( !bRet ) { | ||
203 | bRet = ReadDirectoryChangesW( pWatch->Watch->DirHandle, pWatch->Watch->Buffer.data(), | ||
204 | (DWORD)pWatch->Watch->Buffer.size(), pWatch->Watch->Recursive, | ||
205 | pWatch->Watch->NotifyFilter, NULL, &pWatch->Overlapped, | ||
206 | NULL ) != 0; | ||
207 | |||
208 | if ( bRet ) | ||
209 | ret = RefreshResult::Success; | ||
210 | } | ||
211 | |||
212 | if ( !bRet ) { | ||
213 | std::string error = std::to_string( GetLastError() ); | ||
214 | Errors::Log::createLastError( Errors::WatcherFailed, error ); | ||
215 | } | ||
216 | |||
217 | return ret; | ||
62 | } | 218 | } |
63 | 219 | ||
64 | /// Stops monitoring a directory. | 220 | /// Stops monitoring a directory. |
@@ -70,19 +226,17 @@ void DestroyWatch( WatcherStructWin32* pWatch ) { | |||
70 | CloseHandle( pWatch->Watch->DirHandle ); | 226 | CloseHandle( pWatch->Watch->DirHandle ); |
71 | efSAFE_DELETE_ARRAY( pWatch->Watch->DirName ); | 227 | efSAFE_DELETE_ARRAY( pWatch->Watch->DirName ); |
72 | efSAFE_DELETE( pWatch->Watch ); | 228 | efSAFE_DELETE( pWatch->Watch ); |
229 | efSAFE_DELETE( pWatch ); | ||
73 | } | 230 | } |
74 | } | 231 | } |
75 | 232 | ||
76 | /// Starts monitoring a directory. | 233 | /// Starts monitoring a directory. |
77 | WatcherStructWin32* CreateWatch( LPCWSTR szDirectory, bool recursive, DWORD NotifyFilter, | 234 | WatcherStructWin32* CreateWatch( LPCWSTR szDirectory, bool recursive, |
78 | HANDLE iocp ) { | 235 | DWORD bufferSize, DWORD notifyFilter, HANDLE iocp ) { |
79 | WatcherStructWin32* tWatch; | 236 | WatcherStructWin32* tWatch = new WatcherStructWin32(); |
80 | size_t ptrsize = sizeof( *tWatch ); | 237 | WatcherWin32* pWatch = new WatcherWin32(bufferSize); |
81 | tWatch = static_cast<WatcherStructWin32*>( | 238 | if (tWatch) |
82 | HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, ptrsize ) ); | 239 | tWatch->Watch = pWatch; |
83 | |||
84 | WatcherWin32* pWatch = new WatcherWin32(); | ||
85 | tWatch->Watch = pWatch; | ||
86 | 240 | ||
87 | pWatch->DirHandle = CreateFileW( | 241 | pWatch->DirHandle = CreateFileW( |
88 | szDirectory, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, | 242 | 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 | |||
90 | 244 | ||
91 | if ( pWatch->DirHandle != INVALID_HANDLE_VALUE && | 245 | if ( pWatch->DirHandle != INVALID_HANDLE_VALUE && |
92 | CreateIoCompletionPort( pWatch->DirHandle, iocp, 0, 1 ) ) { | 246 | CreateIoCompletionPort( pWatch->DirHandle, iocp, 0, 1 ) ) { |
93 | pWatch->NotifyFilter = NotifyFilter; | 247 | pWatch->NotifyFilter = notifyFilter; |
94 | pWatch->Recursive = recursive; | 248 | pWatch->Recursive = recursive; |
95 | 249 | ||
96 | if ( RefreshWatch( tWatch ) ) { | 250 | if ( RefreshResult::Failed != RefreshWatch( tWatch ) ) { |
97 | return tWatch; | 251 | return tWatch; |
98 | } | 252 | } |
99 | } | 253 | } |
100 | 254 | ||
101 | CloseHandle( pWatch->DirHandle ); | 255 | CloseHandle( pWatch->DirHandle ); |
102 | efSAFE_DELETE( pWatch->Watch ); | 256 | efSAFE_DELETE( pWatch->Watch ); |
103 | HeapFree( GetProcessHeap(), 0, tWatch ); | 257 | efSAFE_DELETE( tWatch ); |
104 | return NULL; | 258 | return NULL; |
105 | } | 259 | } |
106 | 260 | ||