Watcher.cpp 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. // Watcher.cpp: implementation of the CWatcher class.
  2. //
  3. //////////////////////////////////////////////////////////////////////
  4. #include "stdafx.h"
  5. #include "FileWatcher.h"
  6. #include "Watcher.h"
  7. #include "FileWatcherDlg.h"
  8. #ifdef _DEBUG
  9. #undef THIS_FILE
  10. static char THIS_FILE[]=__FILE__;
  11. #define new DEBUG_NEW
  12. #endif
  13. //////////////////////////////////////////////////////////////////////
  14. // Construction/Destruction
  15. //////////////////////////////////////////////////////////////////////
  16. CWatcher::CWatcher()
  17. {
  18. }
  19. CWatcher::~CWatcher()
  20. {
  21. this->Destroy();
  22. }
  23. //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
  24. //【名称】创建目录监控。
  25. //【参数】csStrPath 监控目录的路径
  26. //【描述】打开目录,保存句柄。创建空完成端口,保存句柄。
  27. // 把目录句柄与完成端口关联,保存句柄。创建线程,并挂起,保存句柄。
  28. // 投递第一次IO请求,调用Watch。
  29. BOOL CWatcher::Create(CString csStrPath)
  30. {
  31. //===========> 打开目录,保存句柄
  32. if ((this->m_hPath = CreateFile(
  33. csStrPath,
  34. GENERIC_READ | GENERIC_WRITE,
  35. FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
  36. NULL,
  37. OPEN_EXISTING,
  38. FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
  39. NULL
  40. )) == INVALID_HANDLE_VALUE)
  41. {
  42. return FALSE;
  43. }
  44. //============> 创建新的完成端口
  45. if ((this->m_hIOCPApp = ::CreateIoCompletionPort(
  46. INVALID_HANDLE_VALUE,
  47. NULL,
  48. 0,
  49. 0
  50. )) == NULL)
  51. {
  52. this->CloseAllHandle();
  53. return FALSE;
  54. }
  55. //============> 关联完成端口,启用单线程
  56. if ((this->m_hIOCPNew = ::CreateIoCompletionPort(
  57. this->m_hPath,
  58. this->m_hIOCPApp,
  59. 0,
  60. 2
  61. )) == NULL)
  62. {
  63. this->CloseAllHandle();
  64. return FALSE;
  65. }
  66. //=========> 创建监控线程,挂起,保存句柄
  67. if ((this->m_hThread = ::CreateThread(
  68. NULL,
  69. 0,
  70. CWatcher::WatchThreadProc,
  71. this,
  72. CREATE_SUSPENDED,
  73. NULL
  74. )) == NULL)
  75. {
  76. this->CloseAllHandle();
  77. return FALSE;
  78. }
  79. //=============> 投递IO请求
  80. if (this->Watch() == FALSE)
  81. {
  82. //=======> 强行退出线程,因为线程没有工作,所以是安全操作
  83. ::TerminateThread(this->m_hThread, 0);
  84. this->CloseAllHandle();
  85. return FALSE;
  86. }
  87. //============> 恢复线程,让线程工作
  88. ::ResumeThread(this->m_hThread);
  89. return TRUE;
  90. }
  91. //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
  92. //【名称】关闭所有打开的句柄
  93. //【参数】无
  94. //【描述】关闭新完成端口、关联完成端口、目录、线程句柄
  95. VOID CWatcher::CloseAllHandle()
  96. {
  97. ::CloseHandle(this->m_hIOCPApp);
  98. ::CloseHandle(this->m_hIOCPNew);
  99. ::CloseHandle(this->m_hPath);
  100. ::CloseHandle(this->m_hThread);
  101. }
  102. //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
  103. //【名称】监控
  104. //【参数】无
  105. //【描述】调用ReadDirectoryChangesW,相当于投递第一次IO请求
  106. BOOL CWatcher::Watch()
  107. {
  108. DWORD dwByteRet = 0;
  109. //==========> 清空OVERLAPPED结构
  110. ::memset(&this->m_stOverlapped, 0, sizeof(OVERLAPPED));
  111. //=========> 监控目录
  112. if (::ReadDirectoryChangesW(
  113. this->m_hPath,
  114. this->m_szBuffer,
  115. WTH_BUF_LEN,
  116. TRUE,
  117. FILE_NOTIFY_CHANGE_FILE_NAME |
  118. FILE_NOTIFY_CHANGE_DIR_NAME |
  119. FILE_NOTIFY_CHANGE_ATTRIBUTES |
  120. FILE_NOTIFY_CHANGE_SIZE |
  121. FILE_NOTIFY_CHANGE_LAST_WRITE |
  122. FILE_NOTIFY_CHANGE_LAST_ACCESS |
  123. FILE_NOTIFY_CHANGE_CREATION |
  124. FILE_NOTIFY_CHANGE_SECURITY,
  125. &dwByteRet,
  126. &this->m_stOverlapped,
  127. NULL
  128. ) == FALSE)
  129. {
  130. return FALSE;
  131. }
  132. return TRUE;
  133. }
  134. //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
  135. //【名称】监控线程过程
  136. //【参数】lpPara 创建线程时候传递的指针
  137. //【描述】循环,查询完成端口状态,如果查询失败、触发OnError,中断。
  138. // 如果dwByteRet为-1,那么说明收到,退出通知,中断
  139. // 如果没有意外,处理缓冲区以后,触发OnWatch
  140. DWORD WINAPI CWatcher::WatchThreadProc(LPVOID lpvPara)
  141. {
  142. CWatcher *lpcsWatcher = NULL;
  143. DWORD dwByteRet = 0, dwIOKey = 0;
  144. LPOVERLAPPED lpstOverlapped = NULL;
  145. PFILE_NOTIFY_INFORMATION lpstNotify = NULL;
  146. char *lpszBuffer = NULL;
  147. CString csStrName1, csStrName2;
  148. lpcsWatcher = (CWatcher *)lpvPara;
  149. while (TRUE)
  150. {
  151. //========> 查询完成端口状态
  152. if (::GetQueuedCompletionStatus(
  153. lpcsWatcher->m_hIOCPApp,
  154. &dwByteRet,
  155. &dwIOKey,
  156. &lpstOverlapped,
  157. INFINITE
  158. ) == TRUE)
  159. {
  160. //=========> 判断是否失败
  161. if (dwByteRet == 0)
  162. {
  163. lpcsWatcher->CloseAllHandle(); // 这里会导致程序崩溃;
  164. break;
  165. }
  166. else
  167. {
  168. //========> 分配内存,复制内存
  169. lpszBuffer = new char[dwByteRet];
  170. memcpy(lpszBuffer, lpcsWatcher->m_szBuffer, dwByteRet);
  171. //======> 缓冲区并且转换成FILE_NOTIFY_INFORMATION结构
  172. lpstNotify = (PFILE_NOTIFY_INFORMATION)lpszBuffer;
  173. //========= 转换成CString,得到名称1
  174. csStrName1 = lpcsWatcher->WideCharToCString(
  175. lpstNotify->FileName,
  176. lpstNotify->FileNameLength);
  177. //========> 判断是否存在偏移
  178. if (lpstNotify->NextEntryOffset != 0)
  179. {
  180. //==========> 计算第二个字符偏移
  181. //======> 一定要记得是
  182. // (PFILE_NOTIFY_INFORMATION)(lpszBuffer + lpstNotify->NextEntryOffset)
  183. lpstNotify =
  184. (PFILE_NOTIFY_INFORMATION)(lpszBuffer +
  185. lpstNotify->NextEntryOffset);
  186. //========= 转换成CString,得到名称2
  187. csStrName2 = lpcsWatcher->WideCharToCString(
  188. lpstNotify->FileName,
  189. lpstNotify->FileNameLength);
  190. }
  191. //=======> 调用OnWatch,剩下的交给此过程处理
  192. lpcsWatcher->OnWatch(
  193. lpstNotify->Action,
  194. csStrName1,
  195. csStrName2);
  196. // 释放内存
  197. delete[] lpszBuffer;
  198. //======> 再次投递IO请求
  199. if (lpcsWatcher->Watch() == FALSE)
  200. {
  201. lpcsWatcher->CloseAllHandle();
  202. }
  203. }
  204. }
  205. else
  206. {
  207. lpcsWatcher->CloseAllHandle();
  208. }
  209. }
  210. return 0;
  211. }
  212. //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
  213. //【名称】宽字符转换成多字节CString
  214. //【参数】lpszWideChar 宽字符缓冲区。nLength 缓冲区长度
  215. //【描述】宽字符转换成多字节以后,再转换成CString对象,然后返回
  216. CString CWatcher::WideCharToCString(LPCWSTR lpszWideChar, UINT nLength)
  217. {
  218. char szString[MAX_PATH] = {0};
  219. //======> 转换
  220. WideCharToMultiByte(
  221. CP_ACP,
  222. 0,
  223. lpszWideChar,
  224. nLength / 2,
  225. szString,
  226. MAX_PATH,
  227. NULL,
  228. NULL
  229. );
  230. return szString;
  231. }
  232. //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
  233. //【名称】销毁目录监控
  234. //【参数】无
  235. //【描述】关闭句柄,让它在线程检测到到失败,安全退出
  236. VOID CWatcher::Destroy()
  237. {
  238. this->CloseAllHandle();
  239. }
  240. //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
  241. //【名称】监控过程
  242. //【参数】dwAction 动作类型。csStrName 相关名称
  243. //【描述】处理动作,不要调用Destroy
  244. VOID CWatcher::OnWatch(DWORD dwAction, CString csStrName1, CString csStrName2)
  245. {
  246. //========> 交托给窗口的过程处理
  247. ((CFileWatcherDlg *)AfxGetApp()->GetMainWnd())->OnWatch(
  248. dwAction,
  249. csStrName1,
  250. csStrName2);
  251. }