root/honeybow/trunk/mwwatcher/src/DirectoryChanges.cpp
| Revision 670, 80.0 kB (checked in by chengyu, 2 years ago) |
|---|
| Line | |
|---|---|
| 1 | // DirectoryChanges.cpp |
| 2 | // |
| 3 | // Implementation of the CDirectoryChangeWatcher and CDirectoryChangeHandler classes. |
| 4 | // Copyright (C) 2001. Wes Jones (wesj@hotmail.com) |
| 5 | // |
| 6 | // This code is free for use under the following conditions: |
| 7 | // |
| 8 | // 1. You may not claim authorship of this code. |
| 9 | // 2. You may not sell or distrubute this code without author's permission. |
| 10 | // 3. You are not permitted to sell this code in it's compiled, non-compiled, executable, |
| 11 | // or any other form. |
| 12 | // 4. Executable code excepted in the case that it is not sold separately from an application |
| 13 | // which uses this it. This means you can use this code for your applications as you |
| 14 | // see fit, but this code may not be sold in any form to others for use in their applications. |
| 15 | // 5. This copyright notice may not be removed. |
| 16 | // 6. Sell this code as part of a 'Programmer's Library' is stricly prohibited. |
| 17 | // |
| 18 | // If you'd like to pay me to turn this into an ActiveX/COM object so you can use it in |
| 19 | // a Visual Basic application, feel free to contact me with an offer, and I will create |
| 20 | // it for you. Otherwise, here is the source code, and you may make your own ActiveX/COM |
| 21 | // object, providing that it is not sold separately. |
| 22 | // |
| 23 | // No guarantees or warranties are expressed or implied. |
| 24 | // This code may contain bugs. |
| 25 | // |
| 26 | // Warning: May contain matter. If this should come into contact with anti-matter, |
| 27 | // a violent explosion may occur. |
| 28 | // |
| 29 | // Please let me know of any bugs, bug fixes, or enhancements made to this code. |
| 30 | // If you have ideas for this, and/or tips or admonitions, I would be glad to hear them. |
| 31 | // |
| 32 | // See notes at top of DirectoryChanges.cpp modification history and more info. |
| 33 | // |
| 34 | //////////////////////////////////////////////////////////////////////////////////////// |
| 35 | |
| 36 | |
| 37 | /************** Change Logs ********************************** |
| 38 | |
| 39 | Author: Song Chengyu (songchengyu@icst.pku.edu.cn) |
| 40 | |
| 41 | Latest Changes: |
| 42 | |
| 43 | 2006 -- Removed two function in class CDirectoryChangeHandler |
| 44 | FtpInit, FtpPut |
| 45 | |
| 46 | Modified FtpAddFile in class CDirectoryChangeHandler |
| 47 | |
| 48 | Added three function in class CDirectoryChangeHandler |
| 49 | isInBlackList, isInWhiteList, On_FileUploaded |
| 50 | |
| 51 | |
| 52 | |
| 53 | 12/01/2005 -- Added three function in class CDirecotryChangeHandler which handles |
| 54 | the file upload fears |
| 55 | |
| 56 | FtpInit -- Initialize the command header |
| 57 | FtpAddFile -- Put files needed to be uploaded in a CString |
| 58 | FtpPut -- Finish the command string, apply a temp file, |
| 59 | write the source file that will be used by the "ftp -s:" |
| 60 | command |
| 61 | |
| 62 | Added some global vars in seek of auto-monitor when system starts |
| 63 | |
| 64 | *********************************************************** |
| 65 | |
| 66 | Author: Wes Jones wesj@hotmail.com |
| 67 | |
| 68 | File: DirectoryChanges.cpp |
| 69 | |
| 70 | Latest Changes: |
| 71 | |
| 72 | 11/22/2001 -- Fixed bug causing file name's to be truncated if |
| 73 | longer than 130 characters. Fixed CFileNotifyInformation::GetFileName() |
| 74 | Thanks to Edric(uo_edric@hotmail.com) for pointing this out. |
| 75 | |
| 76 | Added code to enable process privileges when CDirectoryChangeWatcher::WatchDirectory() |
| 77 | is first called. See docuementation API for ReadDirectoryChangesW() for more information of required privileges. |
| 78 | |
| 79 | Currently enables these privileges: (11/22/2001) |
| 80 | SE_BACKUP_NAME |
| 81 | SE_CHANGE_NOTIFY_NAME |
| 82 | SE_RESTORE_NAME(02/09/2002) |
| 83 | Implemented w/ helper class CPrivilegeEnabler. |
| 84 | |
| 85 | 11/23/2001 Added classes so that all notifications are handled by the |
| 86 | same thread that called CDirectoryChangeWatcher::WatchDirectory(), |
| 87 | ie: the main thread. |
| 88 | CDirectoryChangeHandler::On_Filexxxx() functions are now called in the |
| 89 | context of the main thread instead of the worker thread. |
| 90 | |
| 91 | This is good for two reasons: |
| 92 | 1: The worker thread spends less time handling each notification. |
| 93 | The worker thread simply passes the notification to the main thread, |
| 94 | which does the processing. |
| 95 | This means that each file change notification is handled as fast as possible |
| 96 | and ReadDirectoryChangesW() can be called again to receive more notifications |
| 97 | faster. |
| 98 | |
| 99 | 2: This makes it easier to make an ActiveX or ATL object with this class |
| 100 | because the events that are fired, fire in the context of the main thread. |
| 101 | The fact that I'm using a worker thread w/ this class is totally |
| 102 | transparent to a client user. |
| 103 | If I decide to turn this app into an ActiveX or ATL object |
| 104 | I don't have to worry about wierd COM rules and multithreading issues, |
| 105 | and neither does the client, be the client a VB app, C++ app, Delphi app, or whatever. |
| 106 | |
| 107 | Implemented with CDelayedDirectoryChangeHandler in DirectoryChangeHandler.h/.cpp |
| 108 | |
| 109 | 02/06/2002 Fixed a bug that would cause an application to hang. |
| 110 | If ReadDirectoryChangesW was to fail during normal operation, |
| 111 | the short story is that the application would hang |
| 112 | when it called CDirectoryChangeWatcher::UnwatchDirectory(const CString & ) |
| 113 | |
| 114 | One way to reproduce this behavior on the old code |
| 115 | is to watch a directory using a UNC path, and then change the IP |
| 116 | address of that machine while the watch was running. Exitting |
| 117 | the app after this would cause it to hang. |
| 118 | |
| 119 | Steps to reproduce it: |
| 120 | |
| 121 | 1) Assume that the computer running the code is |
| 122 | named 'ThisComputer' and there is a shared folder named 'FolderName' |
| 123 | |
| 124 | |
| 125 | 2) Start a watch on a folder using a UNC path: ie: \\ThisComputer\FolderName |
| 126 | |
| 127 | eg: CDirectoryChangeWatcher watcher; |
| 128 | |
| 129 | watcher.WatchDirectory(_T("\\\\ThisComputer\\FolderName",/ * other parameters * /) |
| 130 | |
| 131 | 3) Change the IP address of 'ThisComputer' |
| 132 | |
| 133 | ** ReadDirectoryChangesW() will fail some point after this. |
| 134 | |
| 135 | |
| 136 | 4) Exit the application... it may hang. |
| 137 | |
| 138 | |
| 139 | Anyways, that's what the bug fix is for. |
| 140 | |
| 141 | |
| 142 | 02/06/2002 New side effects for CDirectoryChangeHandler::On_ReadDirectoryChangeError() |
| 143 | |
| 144 | If CDirectoryChangeHandler::On_ReadDirectoryChangeError() is ever executed |
| 145 | the directory that you are watching will have been unwatched automatically due |
| 146 | to the error condition. |
| 147 | |
| 148 | A call to CDirectoryChangeWatcher::IsWatchingDirectory() will fail because the directory |
| 149 | is no longer being watched. You'll need to re-watch that directory. |
| 150 | |
| 151 | 02/09/2002 Added a parameter to CDirectoryChangeHandler::On_ReadDirectoryChangeError() |
| 152 | |
| 153 | Added the parameter: const CString & strDirectoryName |
| 154 | The new signature is now: |
| 155 | virtual void CDirectoryChangeHandler::On_ReadDirectoryChangeError(DWORD dwError, const CString & strDirectoryName); |
| 156 | |
| 157 | This new parameter gives you the name of the directory that the error occurred on. |
| 158 | |
| 159 | 04/25/2002 Provided a way to get around the requirement of a message pump. |
| 160 | A console app can now use this w/out problems by passing false |
| 161 | to the constructor of CDirectoryChangeWatcher. |
| 162 | An app w/ a message pump can also pass false if it so desires w/out problems. |
| 163 | |
| 164 | 04/25/2002 Added two new virtual functions to CDirectoryChangeHandler |
| 165 | |
| 166 | Added: |
| 167 | On_WatchStarted(DWORD dwError, const CString & strDirectoryName) |
| 168 | On_WatchStopped(const CString & strDirectoryName); |
| 169 | See header file for details. |
| 170 | |
| 171 | 04/27/2002 Added new function to CDirectoryChangeHandler: |
| 172 | |
| 173 | Added virtual bool On_FilterNotification(DWORD dwNotifyAction, LPCTSTR szFileName, LPCTSTR szNewFileName) |
| 174 | |
| 175 | This function is called before any notification function, and allows the |
| 176 | CDirectoryChangeHandler derived class to ignore file notifications |
| 177 | by performing a programmer defined test. |
| 178 | By ignore, i mean that |
| 179 | On_FileAdded(), On_FileRemoved(), On_FileModified(), or On_FileNameChanged() |
| 180 | will NOT be called if this function returns false. |
| 181 | |
| 182 | The default implementation always returns true, signifying that ALL notifications |
| 183 | are to be called. |
| 184 | |
| 185 | |
| 186 | 04/27/2002 Added new Parameters to CDirectoryChangeWatcher::WatchDirectory() |
| 187 | |
| 188 | The new parameters are: |
| 189 | LPCTSTR szIncludeFilter |
| 190 | LPCTSTR szExcludeFilter |
| 191 | Both parameters are defaulted to NULL. |
| 192 | Signature is now: |
| 193 | CDirectoryChangeWatcher::WatchDirectory(const CString & strDirToWatch, |
| 194 | DWORD dwChangesToWatchFor, |
| 195 | CDirectoryChangeHandler * pChangeHandler, |
| 196 | BOOL bWatchSubDirs = FALSE, |
| 197 | LPCTSTR szIncludeFilter = NULL, |
| 198 | LPCTSTR szExcludeFilter = NULL) |
| 199 | |
| 200 | 04/27/2002 Added support for include and exclude filters. |
| 201 | These filters allow you to receive notifications for just the files you |
| 202 | want... ie: you can specify that you only want to receive notifications |
| 203 | for changes to "*.txt" files or some other such file filter. |
| 204 | |
| 205 | NOTE: This feature is implemented w/ the PathMatchSpec() api function |
| 206 | which is only available on NT4.0 if Internet Explorer 4.0 or above is installed. |
| 207 | See MSDN for PathMatchSpec(). Win2000, and XP do not have to worry about it. |
| 208 | |
| 209 | Filter specifications: |
| 210 | Accepts wild card characters * and ?, just as you're used to for the DOS dir command. |
| 211 | It is possible to specify multiple extenstions in the filter by separating each filter spec |
| 212 | with a semi-colon. |
| 213 | eg: "*.txt;*.tmp;*.log" <-- this filter specifies all .txt, .tmp, & .log files |
| 214 | |
| 215 | |
| 216 | |
| 217 | Filters are passed as parameters to CDirectoryChangeWatcher::WatchDirectory() |
| 218 | |
| 219 | NOTE: The filters you specify take precedence over CDirectoryChangeHandler::On_FilterNotification(). |
| 220 | This means that if the file name does not pass the filters that you specify |
| 221 | when the watch is started, On_FilterNotification() will not be called. |
| 222 | Filter specifications are case insensitive, ie: ".tmp" and ".TMP" are the same |
| 223 | |
| 224 | |
| 225 | FILTER TYPES: |
| 226 | Include Filter: |
| 227 | If you specify an include filter, you are specifying that you |
| 228 | only want to receive notifications for specific file types. |
| 229 | eg: "*.log" means that you only want notifications for changes |
| 230 | to files w/ an exention of ".log". |
| 231 | Changes to ALL OTHER other file types are ignored. |
| 232 | An empty, or not specified include filter means that you want |
| 233 | notifications for changes of ALL file types. |
| 234 | Exclude filter: |
| 235 | |
| 236 | If you specify an exclude filter, you are specifying that |
| 237 | you do not wish to receive notifications for a specific type of file or files. |
| 238 | eg: "*.tmp" would mean that you do not want any notifications |
| 239 | regarding files that end w/ ".tmp" |
| 240 | An empty, or not specified exclude filter means that |
| 241 | you do not wish to exclude any file notifications. |
| 242 | |
| 243 | |
| 244 | |
| 245 | |
| 246 | |
| 247 | |
| 248 | |
| 249 | ************************************************************/ |
| 250 | |
| 251 | #include "stdafx.h" |
| 252 | #include "DirectoryChanges.h" |
| 253 | #include "DelayedDirectoryChangeHandler.h" |
| 254 | #include <list> |
| 255 | #include <shlwapi.h> |
| 256 | |
| 257 | using namespace std; |
| 258 | |
| 259 | #ifdef _DEBUG |
| 260 | #undef THIS_FILE |
| 261 | static char THIS_FILE[]=__FILE__; |
| 262 | #define new DEBUG_NEW |
| 263 | #endif |
| 264 | |
| 265 | // |
| 266 | // |
| 267 | // Fwd Declares & #define's |
| 268 | // |
| 269 | // |
| 270 | // |
| 271 | // Helper classes |
| 272 | class CPrivilegeEnabler; //for use w/ enabling process priveledges when this code is first used. It's a singleton. |
| 273 | |
| 274 | class CFileNotifyInformation;//helps CDirectoryChangeWatcher class notify CDirectoryChangeHandler class of changes to files. |
| 275 | |
| 276 | class CDelayedDirectoryChangeHandler; // Helps all notifications become handled by the main |
| 277 | // thread, or a worker thread depending upon a value passed to the |
| 278 | // constructor of CDirectoryChangeWatcher. |
| 279 | // |
| 280 | // For notifications to fire in the main thread, your app must have a message pump. |
| 281 | // |
| 282 | // The 'main' thread is the one that called CDirectoryChangeWatcher::WatchDirectory() |
| 283 | |
| 284 | // Helper functions |
| 285 | static BOOL EnablePrivilege(LPCTSTR pszPrivName, BOOL fEnable = TRUE); |
| 286 | static bool IsDirectory(const CString & strPath); |
| 287 | |
| 288 | //CString strGFtpAddress; |
| 289 | //CString strGFtpUserName; |
| 290 | //CString strGFtpUserPasswd; |
| 291 | |
| 292 | extern list<CString> AddedFiles; |
| 293 | extern HANDLE hGMutex; |
| 294 | extern BOOL bGSetFilter; |
| 295 | |
| 296 | ///////////////////////////////////////////////////////////////////// |
| 297 | // Helper functions. |
| 298 | BOOL EnablePrivilege(LPCTSTR pszPrivName, BOOL fEnable /*= TRUE*/) |
| 299 | // |
| 300 | // I think this code is from a Jeffrey Richter book... |
| 301 | // |
| 302 | // Enables user priviledges to be set for this process. |
| 303 | // |
| 304 | // Process needs to have access to certain priviledges in order |
| 305 | // to use the ReadDirectoryChangesW() API. See documentation. |
| 306 | { |
| 307 | BOOL fOk = FALSE; |
| 308 | // Assume function fails |
| 309 | HANDLE hToken; |
| 310 | // Try to open this process's access token |
| 311 | if (OpenProcessToken(GetCurrentProcess(), |
| 312 | TOKEN_ADJUST_PRIVILEGES, &hToken)) |
| 313 | { |
| 314 | // privilege |
| 315 | TOKEN_PRIVILEGES tp = { 1 }; |
| 316 | |
| 317 | if( LookupPrivilegeValue(NULL, pszPrivName, &tp.Privileges[0].Luid) ) |
| 318 | { |
| 319 | tp.Privileges[0].Attributes = fEnable ? SE_PRIVILEGE_ENABLED : 0; |
| 320 | |
| 321 | AdjustTokenPrivileges(hToken, FALSE, &tp, |
| 322 | sizeof(tp), NULL, NULL); |
| 323 | |
| 324 | fOk = (GetLastError() == ERROR_SUCCESS); |
| 325 | } |
| 326 | CloseHandle(hToken); |
| 327 | } |
| 328 | return(fOk); |
| 329 | } |
| 330 | |
| 331 | static bool IsDirectory(const CString & strPath) |
| 332 | // |
| 333 | // Returns: bool |
| 334 | // true if strPath is a path to a directory |
| 335 | // false otherwise. |
| 336 | // |
| 337 | { |
| 338 | DWORD dwAttrib = GetFileAttributes( strPath ); |
| 339 | return static_cast<bool>( ( dwAttrib != 0xffffffff |
| 340 | && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)) ); |
| 341 | |
| 342 | |
| 343 | } |
| 344 | /////////////////////////////////////////////// |
| 345 | //Helper class: |
| 346 | |
| 347 | class CFileNotifyInformation |
| 348 | /******************************* |
| 349 | |
| 350 | A Class to more easily traverse the FILE_NOTIFY_INFORMATION records returned |
| 351 | by ReadDirectoryChangesW(). |
| 352 | |
| 353 | FILE_NOTIFY_INFORMATION is defined in Winnt.h as: |
| 354 | |
| 355 | typedef struct _FILE_NOTIFY_INFORMATION { |
| 356 | DWORD NextEntryOffset; |
| 357 | DWORD Action; |
| 358 | DWORD FileNameLength; |
| 359 | WCHAR FileName[1]; |
| 360 | } FILE_NOTIFY_INFORMATION, *PFILE_NOTIFY_INFORMATION; |
| 361 | |
| 362 | ReadDirectoryChangesW basically puts x amount of these records in a |
| 363 | buffer that you specify. |
| 364 | The FILE_NOTIFY_INFORMATION structure is a 'dynamically sized' structure (size depends on length |
| 365 | of the file name (+ sizeof the DWORDs in the struct)) |
| 366 | |
| 367 | Because each structure contains an offset to the 'next' file notification |
| 368 | it is basically a singly linked list. This class treats the structure in that way. |
| 369 | |
| 370 | |
| 371 | Sample Usage: |
| 372 | BYTE Read_Buffer[ 4096 ]; |
| 373 | |
| 374 | ... |
| 375 | ReadDirectoryChangesW(...Read_Buffer, 4096,...); |
| 376 | ... |
| 377 | |
| 378 | CFileNotifyInformation notify_info( Read_Buffer, 4096); |
| 379 | do{ |
| 380 | switch( notify_info.GetAction() ) |
| 381 | { |
| 382 | case xx: |
| 383 | notify_info.GetFileName(); |
| 384 | } |
| 385 | |
| 386 | while( notify_info.GetNextNotifyInformation() ); |
| 387 | |
| 388 | ********************************/ |
| 389 | { |
| 390 | public: |
| 391 | CFileNotifyInformation( BYTE * lpFileNotifyInfoBuffer, DWORD dwBuffSize) |
| 392 | : m_pBuffer( lpFileNotifyInfoBuffer ), |
| 393 | m_dwBufferSize( dwBuffSize ) |
| 394 | { |
| 395 | ASSERT( lpFileNotifyInfoBuffer && dwBuffSize ); |
| 396 | |
| 397 | m_pCurrentRecord = (PFILE_NOTIFY_INFORMATION) m_pBuffer; |
| 398 | } |
| 399 | |
| 400 | |
| 401 | BOOL GetNextNotifyInformation(); |
| 402 | |
| 403 | BOOL CopyCurrentRecordToBeginningOfBuffer(OUT DWORD & ref_dwSizeOfCurrentRecord); |
| 404 | |
| 405 | DWORD GetAction() const;//gets the type of file change notifiation |
| 406 | CString GetFileName()const;//gets the file name from the FILE_NOTIFY_INFORMATION record |
| 407 | CString GetFileNameWithPath(const CString & strRootPath) const;//same as GetFileName() only it prefixes the strRootPath into the file name |
| 408 | |
| 409 | |
| 410 | protected: |
| 411 | BYTE * m_pBuffer;//<--all of the FILE_NOTIFY_INFORMATION records 'live' in the buffer this points to... |
| 412 | DWORD m_dwBufferSize; |
| 413 | PFILE_NOTIFY_INFORMATION m_pCurrentRecord;//this points to the current FILE_NOTIFY_INFORMATION record in m_pBuffer |
| 414 | |
| 415 | }; |
| 416 | |
| 417 | BOOL CFileNotifyInformation::GetNextNotifyInformation() |
| 418 | /*************** |
| 419 | Sets the m_pCurrentRecord to the next FILE_NOTIFY_INFORMATION record. |
| 420 | |
| 421 | Even if this return FALSE, (unless m_pCurrentRecord is NULL) |
| 422 | m_pCurrentRecord will still point to the last record in the buffer. |
| 423 | ****************/ |
| 424 | { |
| 425 | if( m_pCurrentRecord |
| 426 | && m_pCurrentRecord->NextEntryOffset != 0UL)//is there another record after this one? |
| 427 | { |
| 428 | //set the current record to point to the 'next' record |
| 429 | PFILE_NOTIFY_INFORMATION pOld = m_pCurrentRecord; |
| 430 | m_pCurrentRecord = (PFILE_NOTIFY_INFORMATION) ((LPBYTE)m_pCurrentRecord + m_pCurrentRecord->NextEntryOffset); |
| 431 | |
| 432 | ASSERT( (DWORD)((BYTE*)m_pCurrentRecord - m_pBuffer) < m_dwBufferSize);//make sure we haven't gone too far |
| 433 | |
| 434 | if( (DWORD)((BYTE*)m_pCurrentRecord - m_pBuffer) > m_dwBufferSize ) |
| 435 | { |
| 436 | //we've gone too far.... this data is hosed. |
| 437 | // |
| 438 | // This sometimes happens if the watched directory becomes deleted... remove the FILE_SHARE_DELETE flag when using CreateFile() to get the handle to the directory... |
| 439 | m_pCurrentRecord = pOld; |
| 440 | } |
| 441 | |
| 442 | return (BOOL)(m_pCurrentRecord != pOld); |
| 443 | } |
| 444 | return FALSE; |
| 445 | } |
| 446 | |
| 447 | BOOL CFileNotifyInformation::CopyCurrentRecordToBeginningOfBuffer(OUT DWORD & ref_dwSizeOfCurrentRecord) |
| 448 | /***************************************** |
| 449 | Copies the FILE_NOTIFY_INFORMATION record to the beginning of the buffer |
| 450 | specified in the constructor. |
| 451 | |
| 452 | The size of the current record is returned in DWORD & dwSizeOfCurrentRecord. |
| 453 | |
| 454 | *****************************************/ |
| 455 | { |
| 456 | ASSERT( m_pBuffer && m_pCurrentRecord ); |
| 457 | if( !m_pCurrentRecord ) return FALSE; |
| 458 | |
| 459 | BOOL bRetVal = TRUE; |
| 460 | |
| 461 | //determine the size of the current record. |
| 462 | ref_dwSizeOfCurrentRecord = sizeof( FILE_NOTIFY_INFORMATION ); |
| 463 | //subtract out sizeof FILE_NOTIFY_INFORMATION::FileName[1] |
| 464 | WCHAR FileName[1];//same as is defined for FILE_NOTIFY_INFORMATION::FileName |
| 465 | UNREFERENCED_PARAMETER(FileName); |
| 466 | ref_dwSizeOfCurrentRecord -= sizeof(FileName); |
| 467 | //and replace it w/ value of FILE_NOTIFY_INFORMATION::FileNameLength |
| 468 | ref_dwSizeOfCurrentRecord += m_pCurrentRecord->FileNameLength; |
| 469 | |
| 470 | ASSERT( (DWORD)((LPBYTE)m_pCurrentRecord + ref_dwSizeOfCurrentRecord) <= m_dwBufferSize ); |
| 471 | |
| 472 | ASSERT( (void*)m_pBuffer != (void*)m_pCurrentRecord );//if this is the case, your buffer is way too small |
| 473 | if( (void*)m_pBuffer != (void*) m_pCurrentRecord ) |
| 474 | {//copy the m_pCurrentRecord to the beginning of m_pBuffer |
| 475 | |
| 476 | ASSERT( (DWORD)m_pCurrentRecord > (DWORD)m_pBuffer + ref_dwSizeOfCurrentRecord);//will it overlap? |
| 477 | __try{ |
| 478 | memcpy(m_pBuffer, m_pCurrentRecord, ref_dwSizeOfCurrentRecord); |
| 479 | bRetVal = TRUE; |
| 480 | } |
| 481 | __except(EXCEPTION_EXECUTE_HANDLER) |
| 482 | { |
| 483 | TRACE(_T("EXCEPTION! CFileNotifyInformation::CopyCurrentRecordToBeginningOfBuffer() -- probably because bytes overlapped in a call to memcpy()")); |
| 484 | bRetVal = FALSE; |
| 485 | } |
| 486 | } |
| 487 | //else |
| 488 | //there was only one record in this buffer, and m_pCurrentRecord is already at the beginning of the buffer |
| 489 | return bRetVal; |
| 490 | } |
| 491 | |
| 492 | DWORD CFileNotifyInformation::GetAction() const |
| 493 | { |
| 494 | ASSERT( m_pCurrentRecord ); |
| 495 | if( m_pCurrentRecord ) |
| 496 | return m_pCurrentRecord->Action; |
| 497 | return 0UL; |
| 498 | } |
| 499 | |
| 500 | CString CFileNotifyInformation::GetFileName() const |
| 501 | { |
| 502 | // |
| 503 | //BUG FIX: |
| 504 | // File Name's longer than 130 characters are truncated |
| 505 | // |
| 506 | // Thanks Edric @ uo_edric@hotmail.com for pointing this out. |
| 507 | if( m_pCurrentRecord ) |
| 508 | { |
| 509 | WCHAR wcFileName[ MAX_PATH + 1] = {0};//L""; |
| 510 | memcpy( wcFileName, |
| 511 | m_pCurrentRecord->FileName, |
| 512 | //min( MAX_PATH, m_pCurrentRecord->FileNameLength) <-- buggy line |
| 513 | min( (MAX_PATH * sizeof(WCHAR)), m_pCurrentRecord->FileNameLength)); |
| 514 | |
| 515 | |
| 516 | return CString( wcFileName ); |
| 517 | } |
| 518 | return CString(); |
| 519 | } |
| 520 | |
| 521 | static inline bool HasTrailingBackslash(const CString & str ) |
| 522 | { |
| 523 | if( str.GetLength() > 0 |
| 524 | && str[ str.GetLength() - 1 ] == _T('\\') ) |
| 525 | return true; |
| 526 | return false; |
| 527 | } |
| 528 | CString CFileNotifyInformation::GetFileNameWithPath(const CString & strRootPath) const |
| 529 | { |
| 530 | CString strFileName( strRootPath ); |
| 531 | //if( strFileName.Right(1) != _T("\\") ) |
| 532 | if( !HasTrailingBackslash( strRootPath ) ) |
| 533 | strFileName += _T("\\"); |
| 534 | |
| 535 | strFileName += GetFileName(); |
| 536 | return strFileName; |
| 537 | } |
| 538 | ///////////////////////////////////////////////////////////////////////////////// |
| 539 | class CPrivilegeEnabler |
| 540 | // |
| 541 | // Enables privileges for this process |
| 542 | // first time CDirectoryChangeWatcher::WatchDirectory() is called. |
| 543 | // |
| 544 | // It's a singleton. |
| 545 | // |
| 546 | { |
| 547 | private: |
| 548 | CPrivilegeEnabler();//ctor |
| 549 | public: |
| 550 | ~CPrivilegeEnabler(){}; |
| 551 | |
| 552 | static CPrivilegeEnabler & Instance(); |
| 553 | //friend static CPrivilegeEnabler & Instance(); |
| 554 | }; |
| 555 | |
| 556 | CPrivilegeEnabler::CPrivilegeEnabler() |
| 557 | { |
| 558 | LPCTSTR arPrivelegeNames[] = { |
| 559 | SE_BACKUP_NAME, // these two are required for the FILE_FLAG_BACKUP_SEMANTICS flag used in the call to |
| 560 | SE_RESTORE_NAME,// CreateFile() to open the directory handle for ReadDirectoryChangesW |
| 561 | |
| 562 | SE_CHANGE_NOTIFY_NAME //just to make sure...it's on by default for all users. |
| 563 | //<others here as needed> |
| 564 | }; |
| 565 | for(int i = 0; i < sizeof(arPrivelegeNames) / sizeof(arPrivelegeNames[0]); ++i) |
| 566 | { |
| 567 | if( !EnablePrivilege(arPrivelegeNames[i], TRUE) ) |
| 568 | { |
| 569 | TRACE(_T("Unable to enable privilege: %s -- GetLastError(): %d\n"), arPrivelegeNames[i], GetLastError()); |
| 570 | TRACE(_T("CDirectoryChangeWatcher notifications may not work as intended due to insufficient access rights/process privileges.\n")); |
| 571 | TRACE(_T("File: %s Line: %d\n"), _T(__FILE__), __LINE__); |
| 572 | } |
| 573 | } |
| 574 | } |
| 575 | |
| 576 | CPrivilegeEnabler & CPrivilegeEnabler::Instance() |
| 577 | { |
| 578 | static CPrivilegeEnabler theInstance;//constructs this first time it's called. |
| 579 | return theInstance; |
| 580 | } |
| 581 | // |
| 582 | // |
| 583 | // |
| 584 | /////////////////////////////////////////////////////////// |
| 585 | |
| 586 | |
| 587 | // |
| 588 | // |
| 589 | ////////////////////////////////////////////////////////////////////// |
| 590 | // Construction/Destruction |
| 591 | ////////////////////////////////////////////////////////////////////// |
| 592 | CDirectoryChangeHandler::CDirectoryChangeHandler() |
| 593 | : m_nRefCnt( 1 ), |
| 594 | m_pDirChangeWatcher( NULL ), |
| 595 | m_nWatcherRefCnt( 0L ) |
| 596 | { |
| 597 | } |
| 598 | |
| 599 | CDirectoryChangeHandler::~CDirectoryChangeHandler() |
| 600 | { |
| 601 | UnwatchDirectory(); |
| 602 | } |
| 603 | |
| 604 | long CDirectoryChangeHandler::AddRef() |
| 605 | { |
| 606 | return InterlockedIncrement(&m_nRefCnt); |
| 607 | } |
| 608 | |
| 609 | long CDirectoryChangeHandler::Release() |
| 610 | { |
| 611 | long nRefCnt = -1; |
| 612 | if( (nRefCnt = InterlockedDecrement(&m_nRefCnt)) == 0 ) |
| 613 | delete this; |
| 614 | return nRefCnt; |
| 615 | } |
| 616 | long CDirectoryChangeHandler::CurRefCnt()const |
| 617 | { |
| 618 | return m_nRefCnt; |
| 619 | } |
| 620 | |
| 621 | BOOL CDirectoryChangeHandler::UnwatchDirectory() |
| 622 | { |
| 623 | CSingleLock lock(&m_csWatcher, TRUE); |
| 624 | ASSERT( lock.IsLocked() ); |
| 625 | |
| 626 | if( m_pDirChangeWatcher ) |
| 627 | return m_pDirChangeWatcher->UnwatchDirectory( this ); |
| 628 | return TRUE; |
| 629 | } |
| 630 | |
| 631 | long CDirectoryChangeHandler::ReferencesWatcher(CDirectoryChangeWatcher * pDirChangeWatcher) |
| 632 | { |
| 633 | ASSERT( pDirChangeWatcher ); |
| 634 | CSingleLock lock(&m_csWatcher, TRUE); |
| 635 | if( m_pDirChangeWatcher |
| 636 | && m_pDirChangeWatcher != pDirChangeWatcher ) |
| 637 | { |
| 638 | TRACE(_T("CDirectoryChangeHandler...is becoming used by a different CDirectoryChangeWatcher!\n")); |
| 639 | TRACE(_T("Directories being handled by this object will now be unwatched.\nThis object is now being used to ") |
| 640 | _T("handle changes to a directory watched by different CDirectoryChangeWatcher object, probably on a different directory")); |
| 641 | |
| 642 | if( UnwatchDirectory() ) |
