CmWin32FolderMonitor.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676
  1. #include "CmWin32FolderMonitor.h"
  2. #include "CmFileSystem.h"
  3. #include "CmException.h"
  4. #include "CmDebug.h"
  5. #include <windows.h>
  6. namespace BansheeEngine
  7. {
  8. enum class MonitorState
  9. {
  10. Inactive,
  11. Starting,
  12. Monitoring,
  13. Shutdown,
  14. Shutdown2
  15. };
  16. class WorkerFunc
  17. {
  18. public:
  19. WorkerFunc(FolderMonitor* owner);
  20. void operator()();
  21. private:
  22. FolderMonitor* mOwner;
  23. };
  24. struct FolderMonitor::FolderWatchInfo
  25. {
  26. FolderWatchInfo(const WString& folderToMonitor, HANDLE dirHandle, bool monitorSubdirectories, DWORD monitorFlags);
  27. ~FolderWatchInfo();
  28. void startMonitor(HANDLE compPortHandle);
  29. void stopMonitor(HANDLE compPortHandle);
  30. static const UINT32 READ_BUFFER_SIZE = 65536;
  31. WString mFolderToMonitor;
  32. HANDLE mDirHandle;
  33. OVERLAPPED mOverlapped;
  34. MonitorState mState;
  35. UINT8 mBuffer[READ_BUFFER_SIZE];
  36. DWORD mBufferSize;
  37. bool mMonitorSubdirectories;
  38. DWORD mMonitorFlags;
  39. DWORD mReadError;
  40. WString mCachedOldFileName; // Used during rename notifications as they are handled in two steps
  41. CM_MUTEX(mStatusMutex)
  42. CM_THREAD_SYNCHRONISER(mStartStopEvent)
  43. };
  44. FolderMonitor::FolderWatchInfo::FolderWatchInfo(const WString& folderToMonitor, HANDLE dirHandle, bool monitorSubdirectories, DWORD monitorFlags)
  45. :mFolderToMonitor(folderToMonitor), mDirHandle(dirHandle), mState(MonitorState::Inactive), mBufferSize(0),
  46. mMonitorSubdirectories(monitorSubdirectories), mMonitorFlags(monitorFlags), mReadError(0)
  47. {
  48. StringUtil::trim(mFolderToMonitor, L"\\", false, true);
  49. StringUtil::toLowerCase(mFolderToMonitor);
  50. memset(&mOverlapped, 0, sizeof(mOverlapped));
  51. }
  52. FolderMonitor::FolderWatchInfo::~FolderWatchInfo()
  53. {
  54. assert(mState == MonitorState::Inactive);
  55. stopMonitor(0);
  56. }
  57. void FolderMonitor::FolderWatchInfo::startMonitor(HANDLE compPortHandle)
  58. {
  59. if(mState != MonitorState::Inactive)
  60. return; // Already monitoring
  61. {
  62. CM_LOCK_MUTEX_NAMED(mStatusMutex, lock);
  63. mState = MonitorState::Starting;
  64. PostQueuedCompletionStatus(compPortHandle, sizeof(this), (DWORD)this, &mOverlapped);
  65. while(mState != MonitorState::Monitoring)
  66. CM_THREAD_WAIT(mStartStopEvent, mStatusMutex, lock);
  67. }
  68. if(mReadError != ERROR_SUCCESS)
  69. {
  70. {
  71. CM_LOCK_MUTEX(mStatusMutex);
  72. mState = MonitorState::Inactive;
  73. }
  74. CM_EXCEPT(InternalErrorException, "Failed to start folder monitor on folder \"" +
  75. toString(mFolderToMonitor) + "\" because ReadDirectoryChangesW failed.");
  76. }
  77. }
  78. void FolderMonitor::FolderWatchInfo::stopMonitor(HANDLE compPortHandle)
  79. {
  80. if(mState != MonitorState::Inactive)
  81. {
  82. CM_LOCK_MUTEX_NAMED(mStatusMutex, lock);
  83. mState = MonitorState::Shutdown;
  84. PostQueuedCompletionStatus(compPortHandle, sizeof(this), (DWORD)this, &mOverlapped);
  85. while(mState != MonitorState::Inactive)
  86. CM_THREAD_WAIT(mStartStopEvent, mStatusMutex, lock);
  87. }
  88. if(mDirHandle != INVALID_HANDLE_VALUE)
  89. {
  90. CloseHandle(mDirHandle);
  91. mDirHandle = INVALID_HANDLE_VALUE;
  92. }
  93. }
  94. class FolderMonitor::FileNotifyInfo
  95. {
  96. public:
  97. FileNotifyInfo(UINT8* notifyBuffer, DWORD bufferSize)
  98. :mBuffer(notifyBuffer), mBufferSize(bufferSize)
  99. {
  100. mCurrentRecord = (PFILE_NOTIFY_INFORMATION)mBuffer;
  101. }
  102. bool getNext();
  103. DWORD getAction() const;
  104. WString getFileName() const;
  105. WString getFileNameWithPath(const WString& rootPath) const;
  106. protected:
  107. UINT8* mBuffer;
  108. DWORD mBufferSize;
  109. PFILE_NOTIFY_INFORMATION mCurrentRecord;
  110. };
  111. bool FolderMonitor::FileNotifyInfo::getNext()
  112. {
  113. if(mCurrentRecord && mCurrentRecord->NextEntryOffset != 0)
  114. {
  115. PFILE_NOTIFY_INFORMATION oldRecord = mCurrentRecord;
  116. mCurrentRecord = (PFILE_NOTIFY_INFORMATION) ((UINT8*)mCurrentRecord + mCurrentRecord->NextEntryOffset);
  117. if((DWORD)((UINT8*)mCurrentRecord - mBuffer) > mBufferSize)
  118. {
  119. // Gone out of range, something bad happened
  120. assert(false);
  121. mCurrentRecord = oldRecord;
  122. }
  123. return (mCurrentRecord != oldRecord);
  124. }
  125. return false;
  126. }
  127. DWORD FolderMonitor::FileNotifyInfo::getAction() const
  128. {
  129. assert(mCurrentRecord != nullptr);
  130. if(mCurrentRecord)
  131. return mCurrentRecord->Action;
  132. return 0;
  133. }
  134. WString FolderMonitor::FileNotifyInfo::getFileName() const
  135. {
  136. if(mCurrentRecord)
  137. {
  138. wchar_t fileNameBuffer[32768 + 1] = {0};
  139. memcpy(fileNameBuffer, mCurrentRecord->FileName,
  140. std::min(DWORD(32768 * sizeof(wchar_t)), mCurrentRecord->FileNameLength));
  141. return WString(fileNameBuffer);
  142. }
  143. return WString();
  144. }
  145. WString FolderMonitor::FileNotifyInfo::getFileNameWithPath(const WString& rootPath) const
  146. {
  147. WStringStream wss;
  148. wss<<rootPath;
  149. wss<<L"\\";
  150. wss<<getFileName();
  151. return wss.str();
  152. }
  153. enum class FileActionType
  154. {
  155. Added,
  156. Removed,
  157. Modified,
  158. Renamed
  159. };
  160. struct FileAction
  161. {
  162. static FileAction* createAdded(const WString& fileName)
  163. {
  164. UINT8* bytes = (UINT8*)cm_alloc((UINT32)(sizeof(FileAction) + (fileName.size() + 1) * sizeof(WString::value_type)));
  165. FileAction* action = (FileAction*)bytes;
  166. bytes += sizeof(FileAction);
  167. action->oldName = nullptr;
  168. action->newName = (WString::value_type*)bytes;
  169. action->type = FileActionType::Added;
  170. memcpy(action->newName, fileName.data(), fileName.size() * sizeof(WString::value_type));
  171. action->newName[fileName.size()] = L'\0';
  172. return action;
  173. }
  174. static FileAction* createRemoved(const WString& fileName)
  175. {
  176. UINT8* bytes = (UINT8*)cm_alloc((UINT32)(sizeof(FileAction) + (fileName.size() + 1) * sizeof(WString::value_type)));
  177. FileAction* action = (FileAction*)bytes;
  178. bytes += sizeof(FileAction);
  179. action->oldName = nullptr;
  180. action->newName = (WString::value_type*)bytes;
  181. action->type = FileActionType::Removed;
  182. memcpy(action->newName, fileName.data(), fileName.size() * sizeof(WString::value_type));
  183. action->newName[fileName.size()] = L'\0';
  184. return action;
  185. }
  186. static FileAction* createModified(const WString& fileName)
  187. {
  188. UINT8* bytes = (UINT8*)cm_alloc((UINT32)(sizeof(FileAction) + (fileName.size() + 1) * sizeof(WString::value_type)));
  189. FileAction* action = (FileAction*)bytes;
  190. bytes += sizeof(FileAction);
  191. action->oldName = nullptr;
  192. action->newName = (WString::value_type*)bytes;
  193. action->type = FileActionType::Modified;
  194. memcpy(action->newName, fileName.data(), fileName.size() * sizeof(WString::value_type));
  195. action->newName[fileName.size()] = L'\0';
  196. return action;
  197. }
  198. static FileAction* createRenamed(const WString& oldFilename, const WString& newfileName)
  199. {
  200. UINT8* bytes = (UINT8*)cm_alloc((UINT32)(sizeof(FileAction) +
  201. (oldFilename.size() + newfileName.size() + 2) * sizeof(WString::value_type)));
  202. FileAction* action = (FileAction*)bytes;
  203. bytes += sizeof(FileAction);
  204. action->oldName = (WString::value_type*)bytes;
  205. bytes += (oldFilename.size() + 1) * sizeof(WString::value_type);
  206. action->newName = (WString::value_type*)bytes;
  207. action->type = FileActionType::Modified;
  208. memcpy(action->oldName, oldFilename.data(), oldFilename.size() * sizeof(WString::value_type));
  209. action->oldName[oldFilename.size()] = L'\0';
  210. memcpy(action->newName, newfileName.data(), newfileName.size() * sizeof(WString::value_type));
  211. action->newName[newfileName.size()] = L'\0';
  212. return action;
  213. }
  214. static void destroy(FileAction* action)
  215. {
  216. cm_free(action);
  217. }
  218. WString::value_type* oldName;
  219. WString::value_type* newName;
  220. FileActionType type;
  221. };
  222. struct FolderMonitor::Pimpl
  223. {
  224. Vector<FolderWatchInfo*>::type mFoldersToWatch;
  225. HANDLE mCompPortHandle;
  226. Queue<FileAction*>::type mFileActions;
  227. Queue<FileAction*>::type mActiveFileActions;
  228. CM_MUTEX(mMainMutex);
  229. CM_THREAD_TYPE* mWorkerThread;
  230. };
  231. FolderMonitor::FolderMonitor()
  232. {
  233. mPimpl = cm_new<Pimpl>();
  234. mPimpl->mWorkerThread = nullptr;
  235. mPimpl->mCompPortHandle = nullptr;
  236. }
  237. FolderMonitor::~FolderMonitor()
  238. {
  239. stopMonitorAll();
  240. // No need for mutex since we know worker thread is shut down by now
  241. while(!mPimpl->mFileActions.empty())
  242. {
  243. FileAction* action = mPimpl->mFileActions.front();
  244. mPimpl->mFileActions.pop();
  245. FileAction::destroy(action);
  246. }
  247. cm_delete(mPimpl);
  248. }
  249. void FolderMonitor::startMonitor(const WString& folderPath, bool subdirectories, FolderChange changeFilter)
  250. {
  251. if(!FileSystem::isDirectory(folderPath))
  252. {
  253. CM_EXCEPT(InvalidParametersException, "Provided path \"" + toString(folderPath) + "\" is not a directory");
  254. }
  255. WString extendedFolderPath = L"\\\\?\\" + folderPath;
  256. HANDLE dirHandle = CreateFileW(folderPath.c_str(), FILE_LIST_DIRECTORY,
  257. FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING,
  258. FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, nullptr);
  259. if(dirHandle == INVALID_HANDLE_VALUE)
  260. {
  261. CM_EXCEPT(InternalErrorException, "Failed to open folder \"" + toString(folderPath) + "\" for monitoring. Error code: " + toString((UINT64)GetLastError()));
  262. }
  263. DWORD filterFlags = 0;
  264. if((((UINT32)changeFilter) & (UINT32)BansheeEngine::FolderChange::FileName) != 0)
  265. filterFlags |= FILE_NOTIFY_CHANGE_FILE_NAME;
  266. if((((UINT32)changeFilter) & (UINT32)BansheeEngine::FolderChange::DirName) != 0)
  267. filterFlags |= FILE_NOTIFY_CHANGE_DIR_NAME;
  268. if((((UINT32)changeFilter) & (UINT32)BansheeEngine::FolderChange::Attributes) != 0)
  269. filterFlags |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
  270. if((((UINT32)changeFilter) & (UINT32)BansheeEngine::FolderChange::Size) != 0)
  271. filterFlags |= FILE_NOTIFY_CHANGE_SIZE;
  272. if((((UINT32)changeFilter) & (UINT32)BansheeEngine::FolderChange::LastWrite) != 0)
  273. filterFlags |= FILE_NOTIFY_CHANGE_LAST_WRITE;
  274. if((((UINT32)changeFilter) & (UINT32)BansheeEngine::FolderChange::LastAccess) != 0)
  275. filterFlags |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
  276. if((((UINT32)changeFilter) & (UINT32)BansheeEngine::FolderChange::Creation) != 0)
  277. filterFlags |= FILE_NOTIFY_CHANGE_CREATION;
  278. if((((UINT32)changeFilter) & (UINT32)BansheeEngine::FolderChange::Security) != 0)
  279. filterFlags |= FILE_NOTIFY_CHANGE_SECURITY;
  280. mPimpl->mFoldersToWatch.push_back(cm_new<FolderWatchInfo>(folderPath, dirHandle, subdirectories, filterFlags));
  281. FolderWatchInfo* watchInfo = mPimpl->mFoldersToWatch.back();
  282. mPimpl->mCompPortHandle = CreateIoCompletionPort(dirHandle, mPimpl->mCompPortHandle, (DWORD)watchInfo, 0);
  283. if(mPimpl->mCompPortHandle == nullptr)
  284. {
  285. mPimpl->mFoldersToWatch.erase(mPimpl->mFoldersToWatch.end() - 1);
  286. cm_delete(watchInfo);
  287. CM_EXCEPT(InternalErrorException, "Failed to open completition port for folder monitoring. Error code: " + toString((UINT64)GetLastError()));
  288. }
  289. if(mPimpl->mWorkerThread == nullptr)
  290. {
  291. CM_THREAD_CREATE(t, (std::bind(&FolderMonitor::workerThreadMain, this)));
  292. mPimpl->mWorkerThread = t;
  293. if(mPimpl->mWorkerThread == nullptr)
  294. {
  295. mPimpl->mFoldersToWatch.erase(mPimpl->mFoldersToWatch.end() - 1);
  296. cm_delete(watchInfo);
  297. CM_EXCEPT(InternalErrorException, "Failed to create a new worker thread for folder monitoring");
  298. }
  299. }
  300. if(mPimpl->mWorkerThread != nullptr)
  301. {
  302. try
  303. {
  304. watchInfo->startMonitor(mPimpl->mCompPortHandle);
  305. }
  306. catch (Exception* e)
  307. {
  308. mPimpl->mFoldersToWatch.erase(mPimpl->mFoldersToWatch.end() - 1);
  309. cm_delete(watchInfo);
  310. throw(e);
  311. }
  312. }
  313. else
  314. {
  315. mPimpl->mFoldersToWatch.erase(mPimpl->mFoldersToWatch.end() - 1);
  316. cm_delete(watchInfo);
  317. CM_EXCEPT(InternalErrorException, "Failed to create a new worker thread for folder monitoring");
  318. }
  319. }
  320. void FolderMonitor::stopMonitor(const WString& folderPath)
  321. {
  322. WString folderPathForComparison = folderPath;
  323. StringUtil::trim(folderPathForComparison, L"\\", false, true);
  324. StringUtil::toLowerCase(folderPathForComparison);
  325. auto findIter = std::find_if(mPimpl->mFoldersToWatch.begin(), mPimpl->mFoldersToWatch.end(),
  326. [&] (const FolderWatchInfo* x) { return x->mFolderToMonitor == folderPathForComparison; });
  327. if(findIter != mPimpl->mFoldersToWatch.end())
  328. {
  329. FolderWatchInfo* watchInfo = *findIter;
  330. watchInfo->stopMonitor(mPimpl->mCompPortHandle);
  331. cm_delete(watchInfo);
  332. mPimpl->mFoldersToWatch.erase(findIter);
  333. }
  334. if(mPimpl->mFoldersToWatch.size() == 0)
  335. stopMonitorAll();
  336. }
  337. void FolderMonitor::stopMonitorAll()
  338. {
  339. for(auto& watchInfo : mPimpl->mFoldersToWatch)
  340. {
  341. watchInfo->stopMonitor(mPimpl->mCompPortHandle);
  342. {
  343. // Note: Need this mutex to ensure worker thread is done with watchInfo.
  344. // Even though we wait for a condition variable from the worker thread in stopMonitor,
  345. // that doesn't mean the worker thread is done with the condition variable
  346. // (which is stored inside watchInfo)
  347. CM_LOCK_MUTEX(mPimpl->mMainMutex);
  348. cm_delete(watchInfo);
  349. }
  350. }
  351. mPimpl->mFoldersToWatch.clear();
  352. if(mPimpl->mWorkerThread != nullptr)
  353. {
  354. PostQueuedCompletionStatus(mPimpl->mCompPortHandle, 0, 0, nullptr);
  355. mPimpl->mWorkerThread->join();
  356. CM_THREAD_DESTROY(mPimpl->mWorkerThread);
  357. mPimpl->mWorkerThread = nullptr;
  358. }
  359. if(mPimpl->mCompPortHandle != nullptr)
  360. {
  361. CloseHandle(mPimpl->mCompPortHandle);
  362. mPimpl->mCompPortHandle = nullptr;
  363. }
  364. }
  365. void FolderMonitor::workerThreadMain()
  366. {
  367. FolderWatchInfo* watchInfo = nullptr;
  368. do
  369. {
  370. DWORD numBytes;
  371. LPOVERLAPPED overlapped;
  372. if(!GetQueuedCompletionStatus(mPimpl->mCompPortHandle, &numBytes, (PULONG_PTR) &watchInfo, &overlapped, INFINITE))
  373. {
  374. assert(false);
  375. // TODO: Folder handle was lost most likely. Not sure how to deal with that. Shutdown watch on this folder and cleanup?
  376. }
  377. if(watchInfo != nullptr)
  378. {
  379. MonitorState state;
  380. {
  381. CM_LOCK_MUTEX(watchInfo->mStatusMutex);
  382. state = watchInfo->mState;
  383. }
  384. switch(state)
  385. {
  386. case MonitorState::Starting:
  387. if(!ReadDirectoryChangesW(watchInfo->mDirHandle, watchInfo->mBuffer, FolderWatchInfo::READ_BUFFER_SIZE,
  388. watchInfo->mMonitorSubdirectories, watchInfo->mMonitorFlags, &watchInfo->mBufferSize, &watchInfo->mOverlapped, nullptr))
  389. {
  390. assert(false); // TODO - Possibly the buffer was too small?
  391. watchInfo->mReadError = GetLastError();
  392. }
  393. else
  394. {
  395. watchInfo->mReadError = ERROR_SUCCESS;
  396. {
  397. CM_LOCK_MUTEX(watchInfo->mStatusMutex);
  398. watchInfo->mState = MonitorState::Monitoring;
  399. }
  400. }
  401. CM_THREAD_NOTIFY_ONE(watchInfo->mStartStopEvent);
  402. break;
  403. case MonitorState::Monitoring:
  404. {
  405. FileNotifyInfo info(watchInfo->mBuffer, FolderWatchInfo::READ_BUFFER_SIZE);
  406. handleNotifications(info, *watchInfo);
  407. if(!ReadDirectoryChangesW(watchInfo->mDirHandle, watchInfo->mBuffer, FolderWatchInfo::READ_BUFFER_SIZE,
  408. watchInfo->mMonitorSubdirectories, watchInfo->mMonitorFlags, &watchInfo->mBufferSize, &watchInfo->mOverlapped, nullptr))
  409. {
  410. assert(false); // TODO: Failed during normal operation, possibly the buffer was too small. Shutdown watch on this folder and cleanup?
  411. watchInfo->mReadError = GetLastError();
  412. }
  413. else
  414. {
  415. watchInfo->mReadError = ERROR_SUCCESS;
  416. }
  417. }
  418. break;
  419. case MonitorState::Shutdown:
  420. if(watchInfo->mDirHandle != INVALID_HANDLE_VALUE)
  421. {
  422. CloseHandle(watchInfo->mDirHandle);
  423. watchInfo->mDirHandle = INVALID_HANDLE_VALUE;
  424. {
  425. CM_LOCK_MUTEX(watchInfo->mStatusMutex);
  426. watchInfo->mState = MonitorState::Shutdown2;
  427. }
  428. }
  429. else
  430. {
  431. {
  432. CM_LOCK_MUTEX(watchInfo->mStatusMutex);
  433. watchInfo->mState = MonitorState::Inactive;
  434. }
  435. {
  436. CM_LOCK_MUTEX(mPimpl->mMainMutex); // Ensures that we don't delete "watchInfo" before this thread is done with mStartStopEvent
  437. CM_THREAD_NOTIFY_ONE(watchInfo->mStartStopEvent);
  438. }
  439. }
  440. break;
  441. case MonitorState::Shutdown2:
  442. if(watchInfo->mDirHandle != INVALID_HANDLE_VALUE)
  443. {
  444. // Handle is still open? Try again.
  445. CloseHandle(watchInfo->mDirHandle);
  446. watchInfo->mDirHandle = INVALID_HANDLE_VALUE;
  447. }
  448. else
  449. {
  450. {
  451. CM_LOCK_MUTEX(watchInfo->mStatusMutex);
  452. watchInfo->mState = MonitorState::Inactive;
  453. }
  454. {
  455. CM_LOCK_MUTEX(mPimpl->mMainMutex); // Ensures that we don't delete "watchInfo" before this thread is done with mStartStopEvent
  456. CM_THREAD_NOTIFY_ONE(watchInfo->mStartStopEvent);
  457. }
  458. }
  459. break;
  460. }
  461. }
  462. } while (watchInfo != nullptr);
  463. }
  464. void FolderMonitor::handleNotifications(FileNotifyInfo& notifyInfo, FolderWatchInfo& watchInfo)
  465. {
  466. Vector<FileAction*>::type mActions;
  467. do
  468. {
  469. switch(notifyInfo.getAction())
  470. {
  471. case FILE_ACTION_ADDED:
  472. {
  473. WString fileName = notifyInfo.getFileNameWithPath(watchInfo.mFolderToMonitor);
  474. mActions.push_back(FileAction::createAdded(fileName));
  475. }
  476. break;
  477. case FILE_ACTION_REMOVED:
  478. {
  479. WString fileName = notifyInfo.getFileNameWithPath(watchInfo.mFolderToMonitor);
  480. mActions.push_back(FileAction::createRemoved(fileName));
  481. }
  482. break;
  483. case FILE_ACTION_MODIFIED:
  484. {
  485. WString fileName = notifyInfo.getFileNameWithPath(watchInfo.mFolderToMonitor);
  486. mActions.push_back(FileAction::createModified(fileName));
  487. }
  488. break;
  489. case FILE_ACTION_RENAMED_OLD_NAME:
  490. {
  491. WString fileName = notifyInfo.getFileNameWithPath(watchInfo.mFolderToMonitor);
  492. watchInfo.mCachedOldFileName = fileName;
  493. }
  494. break;
  495. case FILE_ACTION_RENAMED_NEW_NAME:
  496. {
  497. WString fileName = notifyInfo.getFileNameWithPath(watchInfo.mFolderToMonitor);
  498. mActions.push_back(FileAction::createRenamed(watchInfo.mCachedOldFileName, fileName));
  499. }
  500. break;
  501. }
  502. } while(notifyInfo.getNext());
  503. {
  504. CM_LOCK_MUTEX(mPimpl->mMainMutex);
  505. for(auto& action : mActions)
  506. mPimpl->mFileActions.push(action);
  507. }
  508. }
  509. void FolderMonitor::_update()
  510. {
  511. {
  512. CM_LOCK_MUTEX(mPimpl->mMainMutex);
  513. mPimpl->mActiveFileActions.swap(mPimpl->mFileActions);
  514. }
  515. while(!mPimpl->mActiveFileActions.empty())
  516. {
  517. FileAction* action = mPimpl->mActiveFileActions.front();
  518. mPimpl->mActiveFileActions.pop();
  519. switch(action->type)
  520. {
  521. case FileActionType::Added:
  522. if(!onAdded.empty())
  523. onAdded(WString(action->newName));
  524. break;
  525. case FileActionType::Removed:
  526. if(!onRemoved.empty())
  527. onRemoved(WString(action->newName));
  528. break;
  529. case FileActionType::Modified:
  530. if(!onModified.empty())
  531. onModified(WString(action->newName));
  532. break;
  533. case FileActionType::Renamed:
  534. if(!onRenamed.empty())
  535. onRenamed(WString(action->oldName), WString(action->newName));
  536. break;
  537. }
  538. FileAction::destroy(action);
  539. }
  540. }
  541. }