OSDN Git Service

Try to enable 64bit compile
[tortoisegit/TortoiseGitJp.git] / src / TGitCache / DirectoryWatcher.cpp
1 // TortoiseSVN - a Windows shell extension for easy version control\r
2 \r
3 // External Cache Copyright (C) 2005 - 2007 - TortoiseSVN\r
4 \r
5 // This program is free software; you can redistribute it and/or\r
6 // modify it under the terms of the GNU General Public License\r
7 // as published by the Free Software Foundation; either version 2\r
8 // of the License, or (at your option) any later version.\r
9 \r
10 // This program is distributed in the hope that it will be useful,\r
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13 // GNU General Public License for more details.\r
14 \r
15 // You should have received a copy of the GNU General Public License\r
16 // along with this program; if not, write to the Free Software Foundation,\r
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
18 //\r
19 #include "StdAfx.h"\r
20 #include "Dbt.h"\r
21 #include "GitStatusCache.h"\r
22 #include ".\directorywatcher.h"\r
23 \r
24 extern HWND hWnd;\r
25 \r
26 CDirectoryWatcher::CDirectoryWatcher(void) : m_hCompPort(NULL)\r
27         , m_bRunning(TRUE)\r
28         , m_FolderCrawler(NULL)\r
29         , blockTickCount(0)\r
30 {\r
31         // enable the required privileges for this process\r
32         \r
33         LPCTSTR arPrivelegeNames[] = {  SE_BACKUP_NAME,\r
34                                                                         SE_RESTORE_NAME,\r
35                                                                         SE_CHANGE_NOTIFY_NAME\r
36                                                                  };\r
37 \r
38         for (int i=0; i<(sizeof(arPrivelegeNames)/sizeof(LPCTSTR)); ++i)\r
39         {\r
40                 HANDLE hToken;    \r
41                 if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))    \r
42                 {        \r
43                         TOKEN_PRIVILEGES tp = { 1 };        \r
44 \r
45                         if (LookupPrivilegeValue(NULL, arPrivelegeNames[i],  &tp.Privileges[0].Luid))\r
46                         {\r
47                                 tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;\r
48 \r
49                                 AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);\r
50                         }\r
51                         CloseHandle(hToken);    \r
52                 }       \r
53         }\r
54 \r
55         unsigned int threadId;\r
56         m_hThread = (HANDLE)_beginthreadex(NULL,0,ThreadEntry,this,0,&threadId);\r
57 }\r
58 \r
59 CDirectoryWatcher::~CDirectoryWatcher(void)\r
60 {\r
61         InterlockedExchange(&m_bRunning, FALSE);\r
62         if (m_hThread != INVALID_HANDLE_VALUE)\r
63         {\r
64                 CloseHandle(m_hThread);\r
65                 m_hThread = INVALID_HANDLE_VALUE;\r
66         }\r
67         AutoLocker lock(m_critSec);\r
68         ClearInfoMap();\r
69 }\r
70 \r
71 void CDirectoryWatcher::Stop()\r
72 {\r
73         InterlockedExchange(&m_bRunning, FALSE);\r
74         if (m_hThread != INVALID_HANDLE_VALUE)\r
75                 CloseHandle(m_hThread);\r
76         m_hThread = INVALID_HANDLE_VALUE;\r
77         if (m_hCompPort != INVALID_HANDLE_VALUE)\r
78                 CloseHandle(m_hCompPort);\r
79         m_hCompPort = INVALID_HANDLE_VALUE;\r
80 }\r
81 \r
82 void CDirectoryWatcher::SetFolderCrawler(CFolderCrawler * crawler)\r
83 {\r
84         m_FolderCrawler = crawler;\r
85 }\r
86 \r
87 bool CDirectoryWatcher::RemovePathAndChildren(const CTGitPath& path)\r
88 {\r
89         bool bRemoved = false;\r
90         AutoLocker lock(m_critSec);\r
91 repeat:\r
92         for (int i=0; i<watchedPaths.GetCount(); ++i)\r
93         {\r
94                 if (path.IsAncestorOf(watchedPaths[i]))\r
95                 {\r
96                         watchedPaths.RemovePath(watchedPaths[i]);\r
97                         bRemoved = true;\r
98                         goto repeat;\r
99                 }\r
100         }\r
101         return bRemoved;\r
102 }\r
103 \r
104 void CDirectoryWatcher::BlockPath(const CTGitPath& path)\r
105 {\r
106         blockedPath = path;\r
107         // block the path from being watched for 4 seconds\r
108         blockTickCount = GetTickCount()+4000;\r
109         ATLTRACE(_T("Blocking path: %s\n"), path.GetWinPath());\r
110 }\r
111 \r
112 bool CDirectoryWatcher::AddPath(const CTGitPath& path)\r
113 {\r
114         if (!CGitStatusCache::Instance().IsPathAllowed(path))\r
115                 return false;\r
116         if ((!blockedPath.IsEmpty())&&(blockedPath.IsAncestorOf(path)))\r
117         {\r
118                 if (GetTickCount() < blockTickCount)\r
119                 {\r
120                         ATLTRACE(_T("Path %s prevented from being watched\n"), path.GetWinPath());\r
121                         return false;\r
122                 }\r
123         }\r
124         AutoLocker lock(m_critSec);\r
125         for (int i=0; i<watchedPaths.GetCount(); ++i)\r
126         {\r
127                 if (watchedPaths[i].IsAncestorOf(path))\r
128                         return false;           // already watched (recursively)\r
129         }\r
130         \r
131         // now check if with the new path we might have a new root\r
132         CTGitPath newroot;\r
133         for (int i=0; i<watchedPaths.GetCount(); ++i)\r
134         {\r
135                 const CString& watched = watchedPaths[i].GetWinPathString();\r
136                 const CString& sPath = path.GetWinPathString();\r
137                 int minlen = min(sPath.GetLength(), watched.GetLength());\r
138                 int len = 0;\r
139                 for (len = 0; len < minlen; ++len)\r
140                 {\r
141                         if (watched.GetAt(len) != sPath.GetAt(len))\r
142                         {\r
143                                 if ((len > 1)&&(len < minlen))\r
144                                 {\r
145                                         if (sPath.GetAt(len)=='\\')\r
146                                         {\r
147                                                 newroot = CTGitPath(sPath.Left(len));\r
148                                         }\r
149                                         else if (watched.GetAt(len)=='\\')\r
150                                         {\r
151                                                 newroot = CTGitPath(watched.Left(len));\r
152                                         }\r
153                                 }\r
154                                 break;\r
155                         }\r
156                 }\r
157                 if (len == minlen)\r
158                 {\r
159                         if (sPath.GetLength() == minlen)\r
160                         {\r
161                                 if (watched.GetLength() > minlen)\r
162                                 {\r
163                                         if (watched.GetAt(len)=='\\')\r
164                                         {\r
165                                                 newroot = path;\r
166                                         }\r
167                                         else if (sPath.GetLength() == 3 && sPath[1] == ':')\r
168                                         {\r
169                                                 newroot = path;\r
170                                         }\r
171                                 }\r
172                         }\r
173                         else\r
174                         {\r
175                                 if (sPath.GetLength() > minlen)\r
176                                 {\r
177                                         if (sPath.GetAt(len)=='\\')\r
178                                         {\r
179                                                 newroot = CTGitPath(watched);\r
180                                         }\r
181                                         else if (watched.GetLength() == 3 && watched[1] == ':')\r
182                                         {\r
183                                                 newroot = CTGitPath(watched);\r
184                                         }\r
185                                 }\r
186                         }\r
187                 }\r
188         }\r
189         if (!newroot.IsEmpty())\r
190         {\r
191                 ATLTRACE(_T("add path to watch %s\n"), newroot.GetWinPath());\r
192                 watchedPaths.AddPath(newroot);\r
193                 watchedPaths.RemoveChildren();\r
194                 CloseInfoMap();\r
195                 m_hCompPort = INVALID_HANDLE_VALUE;\r
196                 return true;\r
197         }\r
198         ATLTRACE(_T("add path to watch %s\n"), path.GetWinPath());\r
199         watchedPaths.AddPath(path);\r
200         CloseInfoMap();\r
201         m_hCompPort = INVALID_HANDLE_VALUE;\r
202         return true;\r
203 }\r
204 \r
205 bool CDirectoryWatcher::IsPathWatched(const CTGitPath& path)\r
206 {\r
207         for (int i=0; i<watchedPaths.GetCount(); ++i)\r
208         {\r
209                 if (watchedPaths[i].IsAncestorOf(path))\r
210                         return true;\r
211         }\r
212         return false;\r
213 }\r
214 \r
215 unsigned int CDirectoryWatcher::ThreadEntry(void* pContext)\r
216 {\r
217         ((CDirectoryWatcher*)pContext)->WorkerThread();\r
218         return 0;\r
219 }\r
220 \r
221 void CDirectoryWatcher::WorkerThread()\r
222 {\r
223         DWORD numBytes;\r
224         CDirWatchInfo * pdi = NULL;\r
225         LPOVERLAPPED lpOverlapped;\r
226         WCHAR buf[READ_DIR_CHANGE_BUFFER_SIZE] = {0};\r
227         WCHAR * pFound = NULL;\r
228         while (m_bRunning)\r
229         {\r
230                 if (watchedPaths.GetCount())\r
231                 {\r
232                         if (!GetQueuedCompletionStatus(m_hCompPort,\r
233                                                                                         &numBytes,\r
234                                                                                         (PULONG_PTR) &pdi,\r
235                                                                                         &lpOverlapped,\r
236                                                                                         INFINITE))\r
237                         {\r
238                                 // Error retrieving changes\r
239                                 // Clear the list of watched objects and recreate that list\r
240                                 if (!m_bRunning)\r
241                                         return;\r
242                                 {\r
243                                         AutoLocker lock(m_critSec);\r
244                                         ClearInfoMap();\r
245                                 }\r
246                                 DWORD lasterr = GetLastError();\r
247                                 if ((m_hCompPort != INVALID_HANDLE_VALUE)&&(lasterr!=ERROR_SUCCESS)&&(lasterr!=ERROR_INVALID_HANDLE))\r
248                                 {\r
249                                         CloseHandle(m_hCompPort);\r
250                                         m_hCompPort = INVALID_HANDLE_VALUE;\r
251                                 }\r
252                                 // Since we pass m_hCompPort to CreateIoCompletionPort, we\r
253                                 // have to set this to NULL to have that API create a new\r
254                                 // handle.\r
255                                 m_hCompPort = NULL;\r
256                                 for (int i=0; i<watchedPaths.GetCount(); ++i)\r
257                                 {\r
258                                         CTGitPath watchedPath = watchedPaths[i];\r
259 \r
260                                         HANDLE hDir = CreateFile(watchedPath.GetWinPath(), \r
261                                                                                         FILE_LIST_DIRECTORY, \r
262                                                                                         FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,\r
263                                                                                         NULL, //security attributes\r
264                                                                                         OPEN_EXISTING,\r
265                                                                                         FILE_FLAG_BACKUP_SEMANTICS | //required privileges: SE_BACKUP_NAME and SE_RESTORE_NAME.\r
266                                                                                         FILE_FLAG_OVERLAPPED,\r
267                                                                                         NULL);\r
268                                         if (hDir == INVALID_HANDLE_VALUE)\r
269                                         {\r
270                                                 // this could happen if a watched folder has been removed/renamed\r
271                                                 ATLTRACE(_T("CDirectoryWatcher: CreateFile failed. Can't watch directory %s\n"), watchedPaths[i].GetWinPath());\r
272                                                 CloseHandle(m_hCompPort);\r
273                                                 m_hCompPort = INVALID_HANDLE_VALUE;\r
274                                                 AutoLocker lock(m_critSec);\r
275                                                 watchedPaths.RemovePath(watchedPath);\r
276                                                 i--; if (i<0) i=0;\r
277                                                 break;\r
278                                         }\r
279                                         \r
280                                         DEV_BROADCAST_HANDLE NotificationFilter;\r
281                                         SecureZeroMemory(&NotificationFilter, sizeof(NotificationFilter));\r
282                                         NotificationFilter.dbch_size = sizeof(DEV_BROADCAST_HANDLE);\r
283                                         NotificationFilter.dbch_devicetype = DBT_DEVTYP_HANDLE;\r
284                                         NotificationFilter.dbch_handle = hDir;\r
285                                         NotificationFilter.dbch_hdevnotify = RegisterDeviceNotification(hWnd, &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE);\r
286 \r
287                                         CDirWatchInfo * pDirInfo = new CDirWatchInfo(hDir, watchedPath);\r
288                                         pDirInfo->m_hDevNotify = NotificationFilter.dbch_hdevnotify;\r
289                                         m_hCompPort = CreateIoCompletionPort(hDir, m_hCompPort, (ULONG_PTR)pDirInfo, 0);\r
290                                         if (m_hCompPort == NULL)\r
291                                         {\r
292                                                 ATLTRACE(_T("CDirectoryWatcher: CreateIoCompletionPort failed. Can't watch directory %s\n"), watchedPath.GetWinPath());\r
293                                                 AutoLocker lock(m_critSec);\r
294                                                 ClearInfoMap();\r
295                                                 delete pDirInfo;\r
296                                                 pDirInfo = NULL;\r
297                                                 watchedPaths.RemovePath(watchedPath);\r
298                                                 i--; if (i<0) i=0;\r
299                                                 break;\r
300                                         }\r
301                                         if (!ReadDirectoryChangesW(pDirInfo->m_hDir,\r
302                                                                                                 pDirInfo->m_Buffer,\r
303                                                                                                 READ_DIR_CHANGE_BUFFER_SIZE,\r
304                                                                                                 TRUE,\r
305                                                                                                 FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE,\r
306                                                                                                 &numBytes,// not used\r
307                                                                                                 &pDirInfo->m_Overlapped,\r
308                                                                                                 NULL))  //no completion routine!\r
309                                         {\r
310                                                 ATLTRACE(_T("CDirectoryWatcher: ReadDirectoryChangesW failed. Can't watch directory %s\n"), watchedPath.GetWinPath());\r
311                                                 AutoLocker lock(m_critSec);\r
312                                                 ClearInfoMap();\r
313                                                 delete pDirInfo;\r
314                                                 pDirInfo = NULL;\r
315                                                 watchedPaths.RemovePath(watchedPath);\r
316                                                 i--; if (i<0) i=0;\r
317                                                 break;\r
318                                         }\r
319                                         AutoLocker lock(m_critSec);\r
320                                         watchInfoMap[pDirInfo->m_hDir] = pDirInfo;\r
321                                         ATLTRACE(_T("watching path %s\n"), pDirInfo->m_DirName.GetWinPath());\r
322                                 }\r
323                         }\r
324                         else\r
325                         {\r
326                                 if (!m_bRunning)\r
327                                         return;\r
328                                 // NOTE: the longer this code takes to execute until ReadDirectoryChangesW\r
329                                 // is called again, the higher the chance that we miss some\r
330                                 // changes in the file system! \r
331                                 if (pdi)\r
332                                 {\r
333                                         if (numBytes == 0)\r
334                                         {\r
335                                                 goto continuewatching;\r
336                                         }\r
337                                         PFILE_NOTIFY_INFORMATION pnotify = (PFILE_NOTIFY_INFORMATION)pdi->m_Buffer;\r
338                                         if ((ULONG_PTR)pnotify - (ULONG_PTR)pdi->m_Buffer > READ_DIR_CHANGE_BUFFER_SIZE)\r
339                                                 goto continuewatching;\r
340                                         DWORD nOffset = pnotify->NextEntryOffset;\r
341                                         do \r
342                                         {\r
343                                                 nOffset = pnotify->NextEntryOffset;\r
344                                                 if (pnotify->FileNameLength >= (READ_DIR_CHANGE_BUFFER_SIZE*sizeof(TCHAR)))\r
345                                                         continue;\r
346                                                 SecureZeroMemory(buf, READ_DIR_CHANGE_BUFFER_SIZE*sizeof(TCHAR));\r
347                                                 _tcsncpy_s(buf, READ_DIR_CHANGE_BUFFER_SIZE, pdi->m_DirPath, READ_DIR_CHANGE_BUFFER_SIZE);\r
348                                                 errno_t err = _tcsncat_s(buf+pdi->m_DirPath.GetLength(), READ_DIR_CHANGE_BUFFER_SIZE-pdi->m_DirPath.GetLength(), pnotify->FileName, _TRUNCATE);\r
349                                                 if (err == STRUNCATE)\r
350                                                 {\r
351                                                         pnotify = (PFILE_NOTIFY_INFORMATION)((LPBYTE)pnotify + nOffset);\r
352                                                         continue;\r
353                                                 }\r
354                                                 buf[(pnotify->FileNameLength/sizeof(TCHAR))+pdi->m_DirPath.GetLength()] = 0;\r
355                                                 pnotify = (PFILE_NOTIFY_INFORMATION)((LPBYTE)pnotify + nOffset);\r
356                                                 if (m_FolderCrawler)\r
357                                                 {\r
358                                                         if ((pFound = wcsstr(buf, L"\\tmp"))!=NULL)\r
359                                                         {\r
360                                                                 pFound += 4;\r
361                                                                 if (((*pFound)=='\\')||((*pFound)=='\0'))\r
362                                                                 {\r
363                                                                         if ((ULONG_PTR)pnotify - (ULONG_PTR)pdi->m_Buffer > READ_DIR_CHANGE_BUFFER_SIZE)\r
364                                                                                 break;\r
365                                                                         continue;\r
366                                                                 }\r
367                                                         }\r
368                                                         if ((pFound = wcsstr(buf, L":\\RECYCLER\\"))!=NULL)\r
369                                                         {\r
370                                                                 if ((pFound-buf) < 5)\r
371                                                                 {\r
372                                                                         // a notification for the recycle bin - ignore it\r
373                                                                         if ((ULONG_PTR)pnotify - (ULONG_PTR)pdi->m_Buffer > READ_DIR_CHANGE_BUFFER_SIZE)\r
374                                                                                 break;\r
375                                                                         continue;\r
376                                                                 }\r
377                                                         }\r
378                                                         if ((pFound = wcsstr(buf, L":\\$Recycle.Bin\\"))!=NULL)\r
379                                                         {\r
380                                                                 if ((pFound-buf) < 5)\r
381                                                                 {\r
382                                                                         // a notification for the recycle bin - ignore it\r
383                                                                         if ((ULONG_PTR)pnotify - (ULONG_PTR)pdi->m_Buffer > READ_DIR_CHANGE_BUFFER_SIZE)\r
384                                                                                 break;\r
385                                                                         continue;\r
386                                                                 }\r
387                                                         }\r
388                                                         if ((pFound = wcsstr(buf, L".tmp"))!=NULL)\r
389                                                         {\r
390                                                                 // assume files with a .tmp extension are not versioned and interesting,\r
391                                                                 // so ignore them.\r
392                                                                 if ((ULONG_PTR)pnotify - (ULONG_PTR)pdi->m_Buffer > READ_DIR_CHANGE_BUFFER_SIZE)\r
393                                                                         break;\r
394                                                                 continue;\r
395                                                         }\r
396                                                         ATLTRACE(_T("change notification: %s\n"), buf);\r
397                                                         m_FolderCrawler->AddPathForUpdate(CTGitPath(buf));\r
398                                                 }\r
399                                                 if ((ULONG_PTR)pnotify - (ULONG_PTR)pdi->m_Buffer > READ_DIR_CHANGE_BUFFER_SIZE)\r
400                                                         break;\r
401                                         } while (nOffset);\r
402 continuewatching:\r
403                                         SecureZeroMemory(pdi->m_Buffer, sizeof(pdi->m_Buffer));\r
404                                         SecureZeroMemory(&pdi->m_Overlapped, sizeof(OVERLAPPED));\r
405                                         if (!ReadDirectoryChangesW(pdi->m_hDir,\r
406                                                                                                 pdi->m_Buffer,\r
407                                                                                                 READ_DIR_CHANGE_BUFFER_SIZE,\r
408                                                                                                 TRUE,\r
409                                                                                                 FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE,\r
410                                                                                                 &numBytes,// not used\r
411                                                                                                 &pdi->m_Overlapped,\r
412                                                                                                 NULL))  //no completion routine!\r
413                                         {\r
414                                                 // Since the call to ReadDirectoryChangesW failed, just\r
415                                                 // wait a while. We don't want to have this thread\r
416                                                 // running using 100% CPU if something goes completely\r
417                                                 // wrong.\r
418                                                 Sleep(200);\r
419                                         }\r
420                                 }\r
421                         }\r
422                 }// if (watchedPaths.GetCount())\r
423                 else\r
424                         Sleep(200);\r
425         }// while (m_bRunning)\r
426 }\r
427 \r
428 void CDirectoryWatcher::ClearInfoMap()\r
429 {\r
430         if (watchInfoMap.size()!=0)\r
431         {\r
432                 AutoLocker lock(m_critSec);\r
433                 for (std::map<HANDLE, CDirWatchInfo *>::iterator I = watchInfoMap.begin(); I != watchInfoMap.end(); ++I)\r
434                 {\r
435                         CDirectoryWatcher::CDirWatchInfo * info = I->second;\r
436                         delete info;\r
437                         info = NULL;\r
438                 }\r
439         }\r
440         watchInfoMap.clear();\r
441         if (m_hCompPort != INVALID_HANDLE_VALUE)\r
442                 CloseHandle(m_hCompPort);\r
443         m_hCompPort = INVALID_HANDLE_VALUE;\r
444 }\r
445 \r
446 CTGitPath CDirectoryWatcher::CloseInfoMap(HDEVNOTIFY hdev)\r
447 {\r
448         CTGitPath path;\r
449         if (watchInfoMap.size() == 0)\r
450                 return path;\r
451         AutoLocker lock(m_critSec);\r
452         for (std::map<HANDLE, CDirWatchInfo *>::iterator I = watchInfoMap.begin(); I != watchInfoMap.end(); ++I)\r
453         {\r
454                 CDirectoryWatcher::CDirWatchInfo * info = I->second;\r
455                 if (info->m_hDevNotify == hdev)\r
456                 {\r
457                         path = info->m_DirName;\r
458                         RemovePathAndChildren(path);\r
459                         BlockPath(path);\r
460                 }\r
461                 info->CloseDirectoryHandle();\r
462         }\r
463         watchInfoMap.clear();\r
464         if (m_hCompPort != INVALID_HANDLE_VALUE)\r
465                 CloseHandle(m_hCompPort);\r
466         m_hCompPort = INVALID_HANDLE_VALUE;\r
467         return path;\r
468 }\r
469 \r
470 bool CDirectoryWatcher::CloseHandlesForPath(const CTGitPath& path)\r
471 {\r
472         if (watchInfoMap.size() == 0)\r
473                 return false;\r
474         AutoLocker lock(m_critSec);\r
475         for (std::map<HANDLE, CDirWatchInfo *>::iterator I = watchInfoMap.begin(); I != watchInfoMap.end(); ++I)\r
476         {\r
477                 CDirectoryWatcher::CDirWatchInfo * info = I->second;\r
478                 CTGitPath p = CTGitPath(info->m_DirPath);\r
479                 if (path.IsAncestorOf(p))\r
480                 {\r
481                         RemovePathAndChildren(p);\r
482                         BlockPath(p);\r
483                 }\r
484                 info->CloseDirectoryHandle();\r
485         }\r
486         watchInfoMap.clear();\r
487         if (m_hCompPort != INVALID_HANDLE_VALUE)\r
488                 CloseHandle(m_hCompPort);\r
489         m_hCompPort = INVALID_HANDLE_VALUE;\r
490         return true;\r
491 }\r
492 \r
493 CDirectoryWatcher::CDirWatchInfo::CDirWatchInfo(HANDLE hDir, const CTGitPath& DirectoryName) :\r
494         m_hDir(hDir),\r
495         m_DirName(DirectoryName)\r
496 {\r
497         ATLASSERT( hDir != INVALID_HANDLE_VALUE \r
498                 && !DirectoryName.IsEmpty());\r
499         memset(&m_Overlapped, 0, sizeof(m_Overlapped));\r
500         m_DirPath = m_DirName.GetWinPathString();\r
501         if (m_DirPath.GetAt(m_DirPath.GetLength()-1) != '\\')\r
502                 m_DirPath += _T("\\");\r
503         m_hDevNotify = INVALID_HANDLE_VALUE;\r
504 }\r
505 \r
506 CDirectoryWatcher::CDirWatchInfo::~CDirWatchInfo()\r
507 {\r
508         CloseDirectoryHandle();\r
509 }\r
510 \r
511 bool CDirectoryWatcher::CDirWatchInfo::CloseDirectoryHandle()\r
512 {\r
513         bool b = TRUE;\r
514         if( m_hDir != INVALID_HANDLE_VALUE )\r
515         {\r
516                 b = !!CloseHandle(m_hDir);\r
517                 m_hDir = INVALID_HANDLE_VALUE;\r
518         }\r
519         if (m_hDevNotify != INVALID_HANDLE_VALUE)\r
520         {\r
521                 UnregisterDeviceNotification(m_hDevNotify);\r
522         }\r
523         return b;\r
524 }\r
525 \r
526 \r
527 \r
528 \r
529 \r
530 \r
531 \r
532 \r