Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2307 lines
61 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. watch.c
  5. Abstract:
  6. This module contains routines for watching changes to the
  7. current user's profile directory and the HKEY_CURRENT_USER key.
  8. Author:
  9. Chuck Lenzmeier (chuckl)
  10. Revision History:
  11. --*/
  12. #include "setupp.h"
  13. #pragma hdrstop
  14. //
  15. // Debugging aids.
  16. //
  17. #if WATCH_DEBUG
  18. DWORD WatchDebugLevel = 0;
  19. #define dprintf(_lvl_,_x_) if ((_lvl_) <= WatchDebugLevel) DbgPrint _x_
  20. #define DEBUG(_x_) _x_
  21. PWCH StartDirectoryName = TEXT("");
  22. PWCH StartKeyName = TEXT("");
  23. PWCH StopDirectoryName = TEXT("");
  24. PWCH StopKeyName = TEXT("");
  25. static PSZ Types[] = {"ACK!", "DIR ", "FILE ", "KEY ", "VALUE"};
  26. static PSZ States[] = {"NONE", "CHANGED", "DELETED", "NEW", "MATCHED"};
  27. #undef MyMalloc
  28. #undef MyFree
  29. #define MyMalloc MyMallocEx
  30. #define MyFree MyFreeEx
  31. PVOID
  32. MyMallocEx (
  33. IN DWORD Size
  34. );
  35. VOID
  36. MyFreeEx (
  37. IN PVOID p
  38. );
  39. VOID
  40. DumpMallocStats (
  41. PSZ Event
  42. );
  43. #else
  44. #define dprintf(_lvl_,_x_)
  45. #define DEBUG(_x_)
  46. #define DumpMallocStats(_event)
  47. #endif
  48. //
  49. // Additions to the change types in WatchEnum.
  50. //
  51. #define WATCH_NONE 0
  52. #define WATCH_MATCHED 4
  53. //
  54. // Common header for container entries (directories and keys).
  55. //
  56. typedef struct _CONTAINER_ENTRY {
  57. LIST_ENTRY SiblingListEntry;
  58. LIST_ENTRY ContainerList;
  59. LIST_ENTRY ObjectList;
  60. struct _CONTAINER_ENTRY *Parent;
  61. DWORD State;
  62. #if WATCH_DEBUG
  63. DWORD IsDirectory;
  64. #endif
  65. } CONTAINER_ENTRY, *PCONTAINER_ENTRY;
  66. //
  67. // Common header for object entries (files and values).
  68. //
  69. typedef struct _OBJECT_ENTRY {
  70. LIST_ENTRY SiblingListEntry;
  71. DWORD State;
  72. } OBJECT_ENTRY, *POBJECT_ENTRY;
  73. //
  74. // Macros for manipulating containers and objects.
  75. //
  76. #if WATCH_DEBUG
  77. #define SetIsDirectory(_container,_isdir) (_container)->IsDirectory = (_isdir)
  78. #else
  79. #define SetIsDirectory(_container,_isdir)
  80. #endif
  81. #define InitializeContainer(_container,_state,_parent,_isdir) { \
  82. InitializeListHead(&(_container)->ContainerList); \
  83. InitializeListHead(&(_container)->ObjectList); \
  84. (_container)->Parent = (PCONTAINER_ENTRY)(_parent); \
  85. (_container)->State = (_state); \
  86. SetIsDirectory((_container),(_isdir)); \
  87. }
  88. #define InitializeObject(_object,_state) (_object)->State = (_state);
  89. #define InsertContainer(_container,_subcontainer) \
  90. InsertTailList(&(_container)->ContainerList,&(_subcontainer)->SiblingListEntry)
  91. #define InsertObject(_container,_object) \
  92. InsertTailList(&(_container)->ObjectList,&(_object)->SiblingListEntry)
  93. #define RemoveObject(_object) RemoveEntryList(&(_object)->SiblingListEntry)
  94. #define RemoveContainer(_container) RemoveEntryList(&(_container)->SiblingListEntry)
  95. #define GetFirstObject(_container) \
  96. ((_container)->ObjectList.Flink != &(_container)->ObjectList ? \
  97. CONTAINING_RECORD( (_container)->ObjectList.Flink, \
  98. OBJECT_ENTRY, \
  99. SiblingListEntry ) : NULL)
  100. #define GetNextObject(_container,_object) \
  101. ((_object)->SiblingListEntry.Flink != &(_container)->ObjectList ? \
  102. CONTAINING_RECORD( (_object)->SiblingListEntry.Flink, \
  103. OBJECT_ENTRY, \
  104. SiblingListEntry ) : NULL)
  105. #define GetFirstContainer(_container) \
  106. ((_container)->ContainerList.Flink != &(_container)->ContainerList ? \
  107. CONTAINING_RECORD( (_container)->ContainerList.Flink, \
  108. CONTAINER_ENTRY, \
  109. SiblingListEntry ) : NULL)
  110. #define GetNextContainer(_container) \
  111. ((_container)->SiblingListEntry.Flink != &(_container)->Parent->ContainerList ? \
  112. CONTAINING_RECORD( (_container)->SiblingListEntry.Flink, \
  113. CONTAINER_ENTRY, \
  114. SiblingListEntry ) : NULL)
  115. #define GetParent(_container) (_container)->Parent
  116. #define GetEntryState(_entry) (_entry)->State
  117. #define SetEntryState(_entry,_state) ((_entry)->State = (_state))
  118. #if WATCH_DEBUG
  119. #define CONTAINER_NAME(_container) \
  120. (_container)->IsDirectory ? ((PDIRECTORY_ENTRY)(_container))->Name : \
  121. ((PKEY_ENTRY)(_container))->Name
  122. #define OBJECT_NAME(_container,_object) \
  123. (_container)->IsDirectory ? ((PFILE_ENTRY)(_object))->Name : \
  124. ((PVALUE_ENTRY)(_object))->Name
  125. #endif
  126. //
  127. // Structures for entries in the watch tree.
  128. //
  129. typedef struct _DIRECTORY_ENTRY {
  130. CONTAINER_ENTRY ;
  131. WCHAR Name[1];
  132. } DIRECTORY_ENTRY, *PDIRECTORY_ENTRY;
  133. typedef struct _FILE_ENTRY {
  134. OBJECT_ENTRY ;
  135. FILETIME LastWriteTime;
  136. WCHAR Name[1];
  137. } FILE_ENTRY, *PFILE_ENTRY;
  138. typedef struct _KEY_ENTRY {
  139. CONTAINER_ENTRY ;
  140. HKEY Handle;
  141. WCHAR Name[1];
  142. } KEY_ENTRY, *PKEY_ENTRY;
  143. typedef struct _VALUE_ENTRY {
  144. OBJECT_ENTRY ;
  145. DWORD Type;
  146. DWORD NameLength;
  147. DWORD ValueDataLength;
  148. WCHAR Name[1];
  149. } VALUE_ENTRY, *PVALUE_ENTRY;
  150. //
  151. // The root of the watch tree is allocated as a ROOT_ENTRY followed by
  152. // a DIRECTORY_ENTRY and a KEY_ENTRY.
  153. //
  154. typedef struct _ROOT_ENTRY {
  155. PDIRECTORY_ENTRY RootDirectoryEntry;
  156. PKEY_ENTRY RootKeyEntry;
  157. } ROOT_ENTRY, *PROOT_ENTRY;
  158. //
  159. // Macro for comparing file times.
  160. //
  161. #define TIMES_EQUAL(_a,_b) \
  162. (((_a).dwLowDateTime == (_b).dwLowDateTime) && \
  163. ((_a).dwHighDateTime == (_b).dwHighDateTime))
  164. typedef struct _KEY_ENUM_CONTEXT {
  165. PKEY_ENTRY ParentKey;
  166. PWCH CurrentPath;
  167. } KEY_ENUM_CONTEXT, *PKEY_ENUM_CONTEXT;
  168. //
  169. // Forward declaration of local subroutines.
  170. //
  171. VOID
  172. WatchFreeChildren (
  173. IN PCONTAINER_ENTRY Container
  174. );
  175. DWORD
  176. WatchDirStart (
  177. IN PROOT_ENTRY Root
  178. );
  179. DWORD
  180. WatchDirStop (
  181. IN PROOT_ENTRY Root
  182. );
  183. DWORD
  184. WatchKeyStart (
  185. IN PROOT_ENTRY Root
  186. );
  187. DWORD
  188. WatchKeyStop (
  189. IN PROOT_ENTRY Root
  190. );
  191. DWORD
  192. AddValueAtStart (
  193. IN PVOID Context,
  194. IN DWORD ValueNameLength,
  195. IN PWCH ValueName,
  196. IN DWORD ValueType,
  197. IN PVOID ValueData,
  198. IN DWORD ValueDataLength
  199. );
  200. DWORD
  201. AddKeyAtStart (
  202. IN PVOID Context,
  203. IN DWORD KeyNameLength,
  204. IN PWCH KeyName
  205. );
  206. DWORD
  207. CheckValueAtStop (
  208. IN PVOID Context,
  209. IN DWORD ValueNameLength,
  210. IN PWCH ValueName,
  211. IN DWORD ValueType,
  212. IN PVOID ValueData,
  213. IN DWORD ValueDataLength
  214. );
  215. DWORD
  216. CheckKeyAtStop (
  217. IN PVOID Context,
  218. IN DWORD KeyNameLength,
  219. IN PWCH KeyName
  220. );
  221. DWORD
  222. WatchStart (
  223. OUT PVOID *WatchHandle
  224. )
  225. /*++
  226. Routine Description:
  227. Starts watching. Captures the initial state of the Start Menu directory
  228. and HKEY_CURRENT_USER.
  229. Arguments:
  230. WatchHandle - returns a handle for calls to the other Watch routines.
  231. Return Value:
  232. DWORD - Win32 status of the operation.
  233. --*/
  234. {
  235. PROOT_ENTRY root;
  236. PDIRECTORY_ENTRY rootDirectory;
  237. PKEY_ENTRY rootKey;
  238. DWORD dirSize;
  239. DWORD keySize;
  240. DWORD size;
  241. DWORD error;
  242. //
  243. // Calculate the size of the root entry, which includes entries for the
  244. // root directory and the root key. The root directory and the root key
  245. // do not have names, so we don't have to allocate additional space.
  246. //
  247. // Allocate and initialize the root entry.
  248. //
  249. #if !WATCH_DEBUG
  250. dirSize = (sizeof(DIRECTORY_ENTRY) + 7) & ~7;
  251. keySize = (sizeof(KEY_ENTRY) + 7) & ~7;
  252. #else
  253. dirSize = (sizeof(DIRECTORY_ENTRY) + (wcslen(StartDirectoryName)*sizeof(WCHAR)) + 7) & ~7;
  254. keySize = (sizeof(KEY_ENTRY) + (wcslen(StartKeyName)*sizeof(WCHAR)) + 7) & ~7;
  255. #endif
  256. root = MyMalloc( ((sizeof(ROOT_ENTRY) + 7) & ~7) + dirSize + keySize );
  257. if ( root == NULL ) {
  258. return ERROR_NOT_ENOUGH_MEMORY;
  259. }
  260. rootDirectory = (PDIRECTORY_ENTRY)(root + 1);
  261. rootKey = (PKEY_ENTRY)((PCHAR)rootDirectory + dirSize);
  262. root->RootDirectoryEntry = rootDirectory;
  263. root->RootKeyEntry = rootKey;
  264. //
  265. // Initialize the root directory and the root key.
  266. //
  267. InitializeContainer( rootDirectory, 0, NULL, TRUE );
  268. InitializeContainer( rootKey, 0, NULL, FALSE );
  269. rootKey->Handle = NULL;
  270. #if !WATCH_DEBUG
  271. rootDirectory->Name[0] = 0;
  272. rootKey->Name[0] = 0;
  273. #else
  274. wcscpy( rootDirectory->Name, StartDirectoryName );
  275. wcscpy( rootKey->Name, StartKeyName );
  276. #endif
  277. //
  278. // Start watching the Start Menu directory and the current user key.
  279. //
  280. error = WatchDirStart( root );
  281. DumpMallocStats( "After WatchDirStart" );
  282. if ( error == NO_ERROR ) {
  283. error = WatchKeyStart( root );
  284. DumpMallocStats( "After WatchKeyStart" );
  285. }
  286. //
  287. // If an error occurred, free the root entry. Otherwise, return the
  288. // address of the root entry as the watch handle.
  289. //
  290. if ( error != NO_ERROR ) {
  291. WatchFree( root );
  292. DumpMallocStats( "After WatchFree" );
  293. } else {
  294. *WatchHandle = root;
  295. }
  296. return error;
  297. } // WatchStart
  298. DWORD
  299. WatchStop (
  300. IN PVOID WatchHandle
  301. )
  302. /*++
  303. Routine Description:
  304. Stops watching. Compares the current state of the directory and key
  305. to the initial state.
  306. Arguments:
  307. WatchHandle - supplies the handle returned by WatchStart.
  308. Return Value:
  309. DWORD - Win32 status of the operation.
  310. --*/
  311. {
  312. PROOT_ENTRY root;
  313. DWORD error;
  314. root = WatchHandle;
  315. //
  316. // Stop watching the Start Menu directory and the current user key.
  317. // Capture the differences from the initial state.
  318. //
  319. #if WATCH_DEBUG
  320. if ( (wcslen(StopDirectoryName) > wcslen(root->RootDirectoryEntry->Name)) ||
  321. (wcslen(StopKeyName) > wcslen(root->RootKeyEntry->Name)) ) {
  322. return ERROR_INVALID_PARAMETER;
  323. }
  324. wcscpy( root->RootDirectoryEntry->Name, StopDirectoryName );
  325. wcscpy( root->RootKeyEntry->Name, StopKeyName );
  326. #endif
  327. error = WatchDirStop( root );
  328. DumpMallocStats( "After WatchDirStop" );
  329. if ( error == NO_ERROR ) {
  330. error = WatchKeyStop( root );
  331. DumpMallocStats( "After WatchKeyStop" );
  332. }
  333. return error;
  334. } // WatchStop
  335. DWORD
  336. WatchEnum (
  337. IN PVOID WatchHandle,
  338. IN PVOID Context,
  339. IN PWATCH_ENUM_ROUTINE EnumRoutine
  340. )
  341. /*++
  342. Routine Description:
  343. Enumerates the new, changed, and deleted elements of the watched
  344. directory and key. Call the EnumRoutine for each such entry.
  345. Arguments:
  346. WatchHandle - handle returned by WatchStart.
  347. Context - context value to be passed to EnumRoutine.
  348. EnumRoutine - routine to call for each entry.
  349. Return Value:
  350. DWORD - Win32 status of the operation.
  351. --*/
  352. {
  353. PROOT_ENTRY root;
  354. PWCH name;
  355. PCONTAINER_ENTRY rootContainer;
  356. PCONTAINER_ENTRY currentContainer;
  357. PCONTAINER_ENTRY container;
  358. POBJECT_ENTRY object;
  359. WATCH_ENTRY enumEntry;
  360. DWORD i;
  361. DWORD containerNameOffset;
  362. DWORD objectNameOffset;
  363. DWORD containerType;
  364. DWORD objectType;
  365. DWORD error;
  366. WCHAR currentPath[MAX_PATH + 1];
  367. root = WatchHandle;
  368. //
  369. // Loop twice -- once for the watched directory and once for
  370. // the watched key.
  371. //
  372. for ( i = 0; i < 2; i++ ) {
  373. //
  374. // Set up for walking the appropriate tree.
  375. //
  376. if ( i == 0 ) {
  377. rootContainer = (PCONTAINER_ENTRY)root->RootDirectoryEntry;
  378. containerType = WATCH_DIRECTORY;
  379. objectType = WATCH_FILE;
  380. containerNameOffset = FIELD_OFFSET( DIRECTORY_ENTRY, Name );
  381. objectNameOffset = FIELD_OFFSET( FILE_ENTRY, Name );
  382. } else {
  383. rootContainer = (PCONTAINER_ENTRY)root->RootKeyEntry;
  384. containerType = WATCH_KEY;
  385. objectType = WATCH_VALUE;
  386. containerNameOffset = FIELD_OFFSET( KEY_ENTRY, Name );
  387. objectNameOffset = FIELD_OFFSET( VALUE_ENTRY, Name );
  388. }
  389. currentContainer = rootContainer;
  390. wcscpy( currentPath, (PWCH)((PCHAR)rootContainer + containerNameOffset) );
  391. enumEntry.Name = currentPath;
  392. if ( wcslen(currentPath) == 0 ) {
  393. enumEntry.Name += 1; // skip leading backslash
  394. }
  395. do {
  396. //
  397. // Call the EnumRoutine for each object (file/value) in the
  398. // container (directory/key). All objects remaining in the
  399. // tree are either changed, new, or deleted.
  400. //
  401. object = GetFirstObject( currentContainer );
  402. while ( object != NULL ) {
  403. enumEntry.EntryType = objectType;
  404. enumEntry.ChangeType = GetEntryState( object );
  405. wcscat( currentPath, L"\\" );
  406. wcscat( currentPath, (PWCH)((PCHAR)object + objectNameOffset) );
  407. error = EnumRoutine( Context, &enumEntry );
  408. if ( error != NO_ERROR ) {
  409. dprintf( 0, ("EnumRoutine returned %d\n", error) );
  410. return error;
  411. }
  412. *wcsrchr(currentPath, L'\\') = 0;
  413. object = GetNextObject( currentContainer, object );
  414. }
  415. //
  416. // If the current container has subcontainers, recurse
  417. // into the first one.
  418. //
  419. container = GetFirstContainer( currentContainer );
  420. if ( container != NULL ) {
  421. currentContainer = container;
  422. wcscat( currentPath, L"\\" );
  423. wcscat( currentPath, (PWCH)((PCHAR)currentContainer + containerNameOffset) );
  424. } else {
  425. //
  426. // The container has no subcontainers. Walk back up the
  427. // tree looking for a sibling container to process.
  428. //
  429. while ( TRUE ) {
  430. //
  431. // If the current container is the root container, we're done.
  432. //
  433. if ( currentContainer == rootContainer ) {
  434. currentContainer = NULL;
  435. break;
  436. }
  437. //
  438. // If the current container is new or deleted, call
  439. // the EnumRoutine.
  440. //
  441. if ( GetEntryState(currentContainer) != WATCH_MATCHED ) {
  442. enumEntry.EntryType = containerType;
  443. enumEntry.ChangeType = GetEntryState( currentContainer );
  444. error = EnumRoutine( Context, &enumEntry );
  445. if ( error != NO_ERROR ) {
  446. dprintf( 0, ("EnumRoutine returned %d\n", error) );
  447. return error;
  448. }
  449. }
  450. //
  451. // Strip the name of the current container off of the path.
  452. //
  453. *wcsrchr(currentPath, L'\\') = 0;
  454. //
  455. // If the parent container has more subcontainers, recurse
  456. // into the next one. Otherwise, move up to the parent
  457. // container and try again.
  458. //
  459. container = GetNextContainer( currentContainer );
  460. if ( container != NULL ) {
  461. currentContainer = container;
  462. wcscat( currentPath, L"\\" );
  463. wcscat( currentPath, (PWCH)((PCHAR)currentContainer + containerNameOffset) );
  464. break;
  465. } else {
  466. currentContainer = GetParent( currentContainer );
  467. }
  468. }
  469. }
  470. } while ( currentContainer != NULL );
  471. } // for
  472. return NO_ERROR;
  473. } // WatchEnum
  474. VOID
  475. WatchFree (
  476. IN PVOID WatchHandle
  477. )
  478. /*++
  479. Routine Description:
  480. Frees the watch data structures.
  481. Arguments:
  482. WatchHandle - supplies the handle returned by WatchStart.
  483. Return Value:
  484. DWORD - Win32 status of the operation.
  485. --*/
  486. {
  487. PROOT_ENTRY root;
  488. root = WatchHandle;
  489. //
  490. // Free the directory tree, and key tree, and the root entry.
  491. //
  492. WatchFreeChildren( (PCONTAINER_ENTRY)root->RootDirectoryEntry );
  493. WatchFreeChildren( (PCONTAINER_ENTRY)root->RootKeyEntry );
  494. MyFree( root );
  495. DumpMallocStats( "After WatchFree" );
  496. return;
  497. } // WatchFree
  498. VOID
  499. WatchFreeChildren (
  500. IN PCONTAINER_ENTRY RootContainer
  501. )
  502. /*++
  503. Routine Description:
  504. Frees the children of a container (directory or key). Note that the
  505. container itself is not freed.
  506. Arguments:
  507. RootContainer - the container whose children are to be freed.
  508. Return Value:
  509. None.
  510. --*/
  511. {
  512. PCONTAINER_ENTRY currentContainer;
  513. PCONTAINER_ENTRY container;
  514. PCONTAINER_ENTRY parent;
  515. POBJECT_ENTRY object;
  516. #if WATCH_DEBUG
  517. WCHAR currentPath[MAX_PATH + 1];
  518. #endif
  519. DEBUG( wcscpy( currentPath, CONTAINER_NAME(RootContainer) ) );
  520. //
  521. // Delete children starting at the root container.
  522. //
  523. currentContainer = RootContainer;
  524. do {
  525. //
  526. // Delete all objects (files or values) within the container.
  527. //
  528. object = GetFirstObject( currentContainer );
  529. while ( object != NULL ) {
  530. dprintf( 2, ("Deleting entry for object %ws\\%ws: %s\n", currentPath, OBJECT_NAME(currentContainer,object), States[GetEntryState(object)]) );
  531. RemoveObject( object );
  532. MyFree( object );
  533. object = GetFirstObject( currentContainer );
  534. }
  535. //
  536. // If the container has subcontainers, recurse into the first one.
  537. //
  538. container = GetFirstContainer( currentContainer );
  539. if ( container != NULL ) {
  540. currentContainer = container;
  541. DEBUG( wcscat( currentPath, L"\\" ) );
  542. DEBUG( wcscat( currentPath, CONTAINER_NAME(currentContainer) ) );
  543. } else {
  544. //
  545. // The container has no subcontainers. Walk back up the
  546. // tree looking for a sibling container to process.
  547. //
  548. while ( TRUE ) {
  549. //
  550. // If the current container is the root container, we're done.
  551. //
  552. if ( currentContainer == RootContainer ) {
  553. currentContainer = NULL;
  554. break;
  555. }
  556. DEBUG( dprintf( 2, ("Deleting entry for container %ws: %s\n", currentPath, States[GetEntryState(currentContainer)]) ) );
  557. DEBUG( *wcsrchr(currentPath, L'\\') = 0 );
  558. //
  559. // Free the current container.
  560. //
  561. parent = GetParent( currentContainer );
  562. RemoveContainer( currentContainer );
  563. MyFree( currentContainer );
  564. //
  565. // If the parent container has more subcontainers,
  566. // recurse into the first one. Otherwise, move up
  567. // to the parent container and loop back to free it.
  568. //
  569. currentContainer = GetFirstContainer( parent );
  570. if ( currentContainer != NULL ) {
  571. DEBUG( wcscat( currentPath, L"\\" ) );
  572. DEBUG( wcscat( currentPath, CONTAINER_NAME(currentContainer) ) );
  573. break;
  574. } else {
  575. currentContainer = parent;
  576. }
  577. }
  578. }
  579. } while ( currentContainer != NULL );
  580. return;
  581. } // WatchFreeChildren
  582. DWORD
  583. WatchDirStart (
  584. IN PROOT_ENTRY Root
  585. )
  586. /*++
  587. Routine Description:
  588. Starts watching the current user's profile directory. Captures the
  589. initial state of the directory tree.
  590. Arguments:
  591. Root - pointer to the ROOT_ENTRY allocated by WatchStart.
  592. Return Value:
  593. DWORD - Win32 status of the operation.
  594. --*/
  595. {
  596. PDIRECTORY_ENTRY rootDirectory;
  597. PDIRECTORY_ENTRY currentDirectory;
  598. PDIRECTORY_ENTRY newDirectory;
  599. PFILE_ENTRY newFile;
  600. WIN32_FIND_DATA fileData;
  601. HANDLE findHandle;
  602. DWORD error;
  603. BOOL ok;
  604. WCHAR currentPath[MAX_PATH + 1];
  605. //
  606. // Get the address of the root directory entry.
  607. //
  608. rootDirectory = Root->RootDirectoryEntry;
  609. currentDirectory = rootDirectory;
  610. //
  611. // Get the full path to the current user's profile directory.
  612. //
  613. ok = GetSpecialFolderPath ( CSIDL_PROGRAMS, currentPath );
  614. if ( !ok ) {
  615. return GetLastError();
  616. }
  617. DEBUG( if ( wcslen( rootDirectory->Name ) != 0 ) {
  618. wcscat( currentPath, TEXT("\\") );
  619. wcscat( currentPath, rootDirectory->Name );
  620. } )
  621. do {
  622. //
  623. // Look for files/directories in the current directory.
  624. //
  625. wcscat( currentPath, L"\\*" );
  626. dprintf( 2, ("FindFirst for %ws\n", currentPath) );
  627. findHandle = FindFirstFile( currentPath, &fileData );
  628. currentPath[wcslen(currentPath) - 2] = 0;
  629. if ( findHandle != INVALID_HANDLE_VALUE ) {
  630. do {
  631. if ( FlagOff(fileData.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY) ) {
  632. //
  633. // The entry returned is for a file. Add it to the tree,
  634. // capturing the file's LastWriteTime.
  635. //
  636. dprintf( 2, (" found file %ws\\%ws\n", currentPath, fileData.cFileName) );
  637. newFile = MyMalloc( (DWORD)(sizeof(FILE_ENTRY) - sizeof(WCHAR) +
  638. ((wcslen(fileData.cFileName) + 1) * sizeof(WCHAR))) );
  639. if ( newFile == NULL ) {
  640. FindClose( findHandle );
  641. return ERROR_NOT_ENOUGH_MEMORY;
  642. }
  643. InitializeObject( newFile, 0 );
  644. wcscpy( newFile->Name, fileData.cFileName );
  645. newFile->LastWriteTime = fileData.ftLastWriteTime;
  646. InsertObject( currentDirectory, newFile );
  647. } else if ((wcscmp(fileData.cFileName,L".") != 0) &&
  648. (wcscmp(fileData.cFileName,L"..") != 0)) {
  649. //
  650. // The entry returned is for a directory. Add it to the tree.
  651. //
  652. dprintf( 2, (" found directory %ws\\%ws\n", currentPath, fileData.cFileName) );
  653. newDirectory = MyMalloc( (DWORD)(sizeof(DIRECTORY_ENTRY) - sizeof(WCHAR) +
  654. ((wcslen(fileData.cFileName) + 1) * sizeof(WCHAR))) );
  655. if ( newDirectory == NULL ) {
  656. FindClose( findHandle );
  657. return ERROR_NOT_ENOUGH_MEMORY;
  658. }
  659. InitializeContainer( newDirectory, 0, currentDirectory, TRUE );
  660. wcscpy( newDirectory->Name, fileData.cFileName );
  661. InsertContainer( currentDirectory, newDirectory );
  662. }
  663. //
  664. // Find another entry in the directory.
  665. //
  666. ok = FindNextFile( findHandle, &fileData );
  667. } while ( ok );
  668. //
  669. // All entries found. Close the find handle.
  670. //
  671. FindClose( findHandle );
  672. } // findHandle != INVALID_HANDLE_VALUE
  673. //
  674. // If the current directory has subdirectories, recurse into the
  675. // first one.
  676. //
  677. newDirectory = (PDIRECTORY_ENTRY)GetFirstContainer( currentDirectory );
  678. if ( newDirectory != NULL ) {
  679. currentDirectory = newDirectory;
  680. wcscat( currentPath, L"\\" );
  681. wcscat( currentPath, currentDirectory->Name );
  682. } else {
  683. //
  684. // The directory has no subdirectories. Walk back up the
  685. // tree looking for a sibling directory to process.
  686. //
  687. while ( TRUE ) {
  688. //
  689. // If the current directory is the root directory, we're done.
  690. //
  691. if ( currentDirectory == rootDirectory ) {
  692. currentDirectory = NULL;
  693. break;
  694. }
  695. //
  696. // Strip the name of the current directory off of the path.
  697. //
  698. *wcsrchr(currentPath, L'\\') = 0;
  699. //
  700. // If the parent directory has more subdirectories,
  701. // recurse into the next one. Otherwise, move up
  702. // to the parent directory and try again.
  703. //
  704. newDirectory = (PDIRECTORY_ENTRY)GetNextContainer( currentDirectory );
  705. if ( newDirectory != NULL ) {
  706. currentDirectory = newDirectory;
  707. wcscat( currentPath, L"\\" );
  708. wcscat( currentPath, currentDirectory->Name );
  709. break;
  710. } else {
  711. currentDirectory = (PDIRECTORY_ENTRY)GetParent( currentDirectory );
  712. }
  713. }
  714. }
  715. } while ( currentDirectory != NULL );
  716. return NO_ERROR;
  717. } // WatchDirStart
  718. DWORD
  719. WatchDirStop (
  720. IN PROOT_ENTRY Root
  721. )
  722. /*++
  723. Routine Description:
  724. Stops watching the current user's profile directory. Captures the
  725. differences between the initial state and the current state.
  726. Arguments:
  727. Root - pointer to the ROOT_ENTRY allocated by WatchStart.
  728. Return Value:
  729. DWORD - Win32 status of the operation.
  730. --*/
  731. {
  732. PDIRECTORY_ENTRY rootDirectory;
  733. PDIRECTORY_ENTRY currentDirectory;
  734. PDIRECTORY_ENTRY directory;
  735. PFILE_ENTRY file;
  736. WIN32_FIND_DATA fileData;
  737. HANDLE findHandle;
  738. DWORD error;
  739. BOOL ok;
  740. WCHAR currentPath[MAX_PATH + 1];
  741. //
  742. // Get the address of the root directory entry.
  743. //
  744. rootDirectory = Root->RootDirectoryEntry;
  745. currentDirectory = rootDirectory;
  746. //
  747. // Get the full path to the current user's directory.
  748. //
  749. ok = GetSpecialFolderPath ( CSIDL_PROGRAMS, currentPath );
  750. if ( !ok ) {
  751. return GetLastError();
  752. }
  753. DEBUG( if ( wcslen( rootDirectory->Name ) != 0 ) {
  754. wcscat( currentPath, TEXT("\\") );
  755. wcscat( currentPath, rootDirectory->Name );
  756. } )
  757. do {
  758. //
  759. // Look for files/directories in the current directory.
  760. //
  761. wcscat( currentPath, L"\\*" );
  762. dprintf( 2, ("FindFirst for %ws\n", currentPath) );
  763. findHandle = FindFirstFile( currentPath, &fileData );
  764. currentPath[wcslen(currentPath) - 2] = 0;
  765. if ( findHandle != INVALID_HANDLE_VALUE ) {
  766. do {
  767. if ( FlagOff(fileData.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY) ) {
  768. //
  769. // The entry returned is for a file. Check to see if
  770. // this file existed at the start.
  771. //
  772. dprintf( 2, (" found file %ws\\%ws\n", currentPath, fileData.cFileName) );
  773. ok = FALSE;
  774. file = (PFILE_ENTRY)GetFirstObject( currentDirectory );
  775. while ( file != NULL ) {
  776. if ( _wcsicmp( file->Name, fileData.cFileName ) == 0 ) {
  777. ok = TRUE;
  778. break;
  779. }
  780. file = (PFILE_ENTRY)GetNextObject( currentDirectory, file );
  781. }
  782. if ( ok ) {
  783. //
  784. // The file existed at the start. If its LastWriteTime
  785. // hasn't changed, remove it from the watch tree.
  786. // Otherwise, mark it as changed.
  787. //
  788. if ( TIMES_EQUAL( file->LastWriteTime, fileData.ftLastWriteTime ) ) {
  789. dprintf( 2, (" Deleting entry for unchanged file %ws\\%ws\n", currentPath, file->Name) );
  790. RemoveObject( file );
  791. MyFree( file );
  792. } else {
  793. dprintf( 1, (" Marking entry for changed file %ws\\%ws\n", currentPath, file->Name) );
  794. SetEntryState( file, WATCH_CHANGED );
  795. }
  796. } else {
  797. //
  798. // The file is new. Add it to the tree.
  799. //
  800. file = MyMalloc( (DWORD)(sizeof(FILE_ENTRY) - sizeof(WCHAR) +
  801. ((wcslen(fileData.cFileName) + 1) * sizeof(WCHAR))) );
  802. if ( file == NULL ) {
  803. FindClose( findHandle );
  804. return ERROR_NOT_ENOUGH_MEMORY;
  805. }
  806. InitializeObject( file, WATCH_NEW );
  807. wcscpy( file->Name, fileData.cFileName );
  808. dprintf( 1, (" Adding entry for new file %ws\\%ws\n", currentPath, file->Name) );
  809. InsertObject( currentDirectory, file );
  810. }
  811. } else if ((wcscmp(fileData.cFileName,L".") != 0) &&
  812. (wcscmp(fileData.cFileName,L"..") != 0)) {
  813. //
  814. // The entry returned is for a directory. Check to see if
  815. // this directory existed at the start.
  816. //
  817. dprintf( 2, (" found directory %ws\\%ws\n", currentPath, fileData.cFileName) );
  818. ok = FALSE;
  819. directory = (PDIRECTORY_ENTRY)GetFirstContainer( currentDirectory );
  820. while ( directory != NULL ) {
  821. if ( _wcsicmp( directory->Name, fileData.cFileName ) == 0 ) {
  822. ok = TRUE;
  823. break;
  824. }
  825. directory = (PDIRECTORY_ENTRY)GetNextContainer( directory );
  826. }
  827. if ( ok ) {
  828. //
  829. // The directory existed at the start. Mark it as
  830. // matched. (We can't delete matched directories,
  831. // as we do files, because they need to be in the
  832. // tree for recursion.)
  833. //
  834. SetEntryState( directory, WATCH_MATCHED );
  835. } else {
  836. //
  837. // The directory is new. Add it to the tree.
  838. //
  839. directory = MyMalloc( (DWORD)(sizeof(DIRECTORY_ENTRY) - sizeof(WCHAR) +
  840. ((wcslen(fileData.cFileName) + 1) * sizeof(WCHAR))) );
  841. if ( directory == NULL ) {
  842. FindClose( findHandle );
  843. return ERROR_NOT_ENOUGH_MEMORY;
  844. }
  845. InitializeContainer( directory, WATCH_NEW, currentDirectory, TRUE );
  846. wcscpy( directory->Name, fileData.cFileName );
  847. dprintf( 1, (" Adding entry for new directory %ws\\%ws\n", currentPath, directory->Name) );
  848. InsertContainer( currentDirectory, directory );
  849. }
  850. }
  851. //
  852. // Find another entry in the directory.
  853. //
  854. ok = FindNextFile( findHandle, &fileData );
  855. } while ( ok );
  856. //
  857. // All entries found. Close the find handle.
  858. //
  859. FindClose( findHandle );
  860. } // findHandle != INVALID_HANDLE_VALUE
  861. //
  862. // Any file entries in the current directory that were not removed
  863. // (because they were matched), marked as changed (because the
  864. // file time had changed), or added (for new files) represent files
  865. // that have been deleted. Mark them as such.
  866. //
  867. file = (PFILE_ENTRY)GetFirstObject( currentDirectory );
  868. while ( file != NULL ) {
  869. if ( GetEntryState(file) == WATCH_NONE ) {
  870. dprintf( 1, (" Marking entry for deleted file %ws\\%ws\n", currentPath, file->Name) );
  871. SetEntryState( file, WATCH_DELETED );
  872. }
  873. file = (PFILE_ENTRY)GetNextObject( currentDirectory, file );
  874. }
  875. //
  876. // Any subdirectory entries in the current directory that were not
  877. // marked as matched (directory still exists) or added (new directory)
  878. // represent directories that have been deleted. Mark them as such
  879. // and delete the entries for the their children -- we don't need
  880. // these entries any more.
  881. //
  882. directory = (PDIRECTORY_ENTRY)GetFirstContainer( currentDirectory );
  883. while ( directory != NULL ) {
  884. if ( GetEntryState(directory) == WATCH_NONE ) {
  885. dprintf( 1, (" Marking entry for deleted directory %ws\\%ws\n", currentPath, directory->Name) );
  886. SetEntryState( directory, WATCH_DELETED );
  887. WatchFreeChildren( (PCONTAINER_ENTRY)directory );
  888. }
  889. directory = (PDIRECTORY_ENTRY)GetNextContainer( directory );
  890. }
  891. //
  892. // Find a subdirectory of the current directory that is marked as
  893. // matched. We don't need to walk the subtrees for new or deleted
  894. // directories.
  895. //
  896. directory = (PDIRECTORY_ENTRY)GetFirstContainer( currentDirectory );
  897. while ( directory != NULL ) {
  898. if ( GetEntryState(directory) == WATCH_MATCHED ) {
  899. break;
  900. }
  901. directory = (PDIRECTORY_ENTRY)GetNextContainer( directory );
  902. }
  903. //
  904. // If a matched subdirectory was found, recurse into it.
  905. //
  906. if ( directory != NULL ) {
  907. currentDirectory = directory;
  908. wcscat( currentPath, L"\\" );
  909. wcscat( currentPath, currentDirectory->Name );
  910. } else {
  911. //
  912. // The directory has no matched subdirectories. Walk back up the
  913. // tree looking for a sibling directory to process.
  914. //
  915. while ( TRUE ) {
  916. //
  917. // If the current directory is the root directory, we're done.
  918. //
  919. if ( currentDirectory == rootDirectory ) {
  920. currentDirectory = NULL;
  921. break;
  922. }
  923. //
  924. // Strip the name of the current directory off of the path.
  925. //
  926. *wcsrchr(currentPath, L'\\') = 0;
  927. //
  928. // If the parent directories has more matched subdirectories,
  929. // recurse into the next one. Otherwise, move up to the
  930. // parent directory and try again.
  931. //
  932. directory = (PDIRECTORY_ENTRY)GetNextContainer( currentDirectory );
  933. while ( directory != NULL ) {
  934. if ( GetEntryState(directory) == WATCH_MATCHED ) {
  935. break;
  936. }
  937. directory = (PDIRECTORY_ENTRY)GetNextContainer( directory );
  938. }
  939. if ( directory != NULL ) {
  940. currentDirectory = directory;
  941. wcscat( currentPath, L"\\" );
  942. wcscat( currentPath, currentDirectory->Name );
  943. break;
  944. } else {
  945. currentDirectory = (PDIRECTORY_ENTRY)GetParent( currentDirectory );
  946. }
  947. }
  948. }
  949. } while ( currentDirectory != NULL );
  950. return NO_ERROR;
  951. } // WatchDirStop
  952. DWORD
  953. WatchKeyStart (
  954. IN PROOT_ENTRY Root
  955. )
  956. /*++
  957. Routine Description:
  958. Starts watching the current user key. Captures the initial state of the
  959. key tree.
  960. Arguments:
  961. Root - pointer to the ROOT_ENTRY allocated by WatchStart.
  962. Return Value:
  963. DWORD - Win32 status of the operation.
  964. --*/
  965. {
  966. PKEY_ENTRY rootKey;
  967. PKEY_ENTRY currentKey;
  968. PKEY_ENTRY newKey;
  969. DWORD error;
  970. KEY_ENUM_CONTEXT context;
  971. #if WATCH_DEBUG
  972. WCHAR currentPath[MAX_PATH + 1];
  973. #endif
  974. //
  975. // Get the address of the root key entry.
  976. //
  977. rootKey = Root->RootKeyEntry;
  978. currentKey = rootKey;
  979. DEBUG( wcscpy( currentPath, rootKey->Name ) );
  980. do {
  981. //
  982. // Open the current key. If the current key is the root key, then
  983. // just use the HKEY_CURRENT_USER predefined key. Otherwise, open
  984. // the current key relative to the parent key.
  985. //
  986. if ( (currentKey == rootKey)
  987. #if WATCH_DEBUG
  988. && (wcslen(currentKey->Name) == 0)
  989. #endif
  990. ) {
  991. currentKey->Handle = HKEY_CURRENT_USER;
  992. } else {
  993. error = RegOpenKeyEx(
  994. #if WATCH_DEBUG
  995. currentKey == rootKey ?
  996. HKEY_CURRENT_USER :
  997. #endif
  998. ((PKEY_ENTRY)GetParent(currentKey))->Handle,
  999. currentKey->Name,
  1000. 0,
  1001. KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS,
  1002. &currentKey->Handle );
  1003. if ( error != NO_ERROR ) {
  1004. goto cleanup;
  1005. }
  1006. }
  1007. //
  1008. // Enumerate the values and subkeys of the key, adding entries
  1009. // to the watch tree for each one.
  1010. //
  1011. context.ParentKey = currentKey;
  1012. DEBUG( context.CurrentPath = currentPath );
  1013. error = EnumerateKey( currentKey->Handle,
  1014. &context,
  1015. AddValueAtStart,
  1016. AddKeyAtStart );
  1017. if ( error != NO_ERROR ) {
  1018. goto cleanup;
  1019. }
  1020. //
  1021. // If the current key has subkeys, recurse into the first one.
  1022. //
  1023. newKey = (PKEY_ENTRY)GetFirstContainer( currentKey );
  1024. if ( newKey != NULL ) {
  1025. currentKey = newKey;
  1026. DEBUG( wcscat( currentPath, L"\\" ) );
  1027. DEBUG( wcscat( currentPath, currentKey->Name ) );
  1028. } else {
  1029. //
  1030. // The key has no subkeys. Walk back up the tree looking
  1031. // for a sibling key to process.
  1032. //
  1033. while ( TRUE ) {
  1034. //
  1035. // Close the handle to the key.
  1036. //
  1037. if ( currentKey->Handle != HKEY_CURRENT_USER ) {
  1038. RegCloseKey( currentKey->Handle );
  1039. }
  1040. currentKey->Handle = NULL;
  1041. //
  1042. // If the current key is the root key, we're done.
  1043. //
  1044. if ( currentKey == rootKey ) {
  1045. currentKey = NULL;
  1046. break;
  1047. }
  1048. DEBUG( *wcsrchr(currentPath, L'\\') = 0 );
  1049. //
  1050. // If the parent key has more subkeys, recurse into the next
  1051. // one. Otherwise, move up to the parent key and try again.
  1052. //
  1053. newKey = (PKEY_ENTRY)GetNextContainer( currentKey );
  1054. if ( newKey != NULL ) {
  1055. currentKey = newKey;
  1056. DEBUG( wcscat( currentPath, L"\\" ) );
  1057. DEBUG( wcscat( currentPath, currentKey->Name ) );
  1058. break;
  1059. } else {
  1060. currentKey = (PKEY_ENTRY)GetParent( currentKey );
  1061. }
  1062. }
  1063. }
  1064. } while ( currentKey != NULL );
  1065. return NO_ERROR;
  1066. cleanup:
  1067. //
  1068. // Error cleanup. Walk back up the tree closing handles.
  1069. //
  1070. do {
  1071. if ( (currentKey->Handle != NULL) && (currentKey->Handle != HKEY_CURRENT_USER) ) {
  1072. RegCloseKey( currentKey->Handle );
  1073. }
  1074. currentKey->Handle = NULL;
  1075. currentKey = (PKEY_ENTRY)GetParent( currentKey );
  1076. } while ( currentKey != NULL );
  1077. return error;
  1078. } // WatchKeyStart
  1079. DWORD
  1080. WatchKeyStop (
  1081. IN PROOT_ENTRY Root
  1082. )
  1083. /*++
  1084. Routine Description:
  1085. Stops watching the current user key. Captures the differences
  1086. between the initial state and the current state.
  1087. Arguments:
  1088. Root - pointer to the ROOT_ENTRY allocated by WatchStart.
  1089. Return Value:
  1090. DWORD - Win32 status of the operation.
  1091. --*/
  1092. {
  1093. PKEY_ENTRY rootKey;
  1094. PKEY_ENTRY currentKey;
  1095. PKEY_ENTRY key;
  1096. PVALUE_ENTRY value;
  1097. DWORD error;
  1098. KEY_ENUM_CONTEXT context;
  1099. #if WATCH_DEBUG
  1100. WCHAR currentPath[MAX_PATH + 1];
  1101. #endif
  1102. //
  1103. // Get the address of the root key entry.
  1104. //
  1105. rootKey = Root->RootKeyEntry;
  1106. currentKey = rootKey;
  1107. DEBUG( wcscpy( currentPath, rootKey->Name ) );
  1108. do {
  1109. //
  1110. // Open the current key. If the current key is the root key, then
  1111. // just use the HKEY_CURRENT_USER predefined key. Otherwise, open
  1112. // the current key relative to the parent key.
  1113. //
  1114. if ( (currentKey == rootKey)
  1115. #if WATCH_DEBUG
  1116. && (wcslen(currentKey->Name) == 0)
  1117. #endif
  1118. ) {
  1119. currentKey->Handle = HKEY_CURRENT_USER;
  1120. } else {
  1121. error = RegOpenKeyEx(
  1122. #if WATCH_DEBUG
  1123. currentKey == rootKey ?
  1124. HKEY_CURRENT_USER :
  1125. #endif
  1126. ((PKEY_ENTRY)GetParent(currentKey))->Handle,
  1127. currentKey->Name,
  1128. 0,
  1129. KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS,
  1130. &currentKey->Handle );
  1131. if ( error != NO_ERROR ) {
  1132. goto cleanup;
  1133. }
  1134. }
  1135. //
  1136. // Enumerate the values and subkeys of the key, checking entries
  1137. // in the watch tree for each one.
  1138. //
  1139. context.ParentKey = currentKey;
  1140. DEBUG( context.CurrentPath = currentPath );
  1141. error = EnumerateKey( currentKey->Handle,
  1142. &context,
  1143. CheckValueAtStop,
  1144. CheckKeyAtStop );
  1145. if ( error != NO_ERROR ) {
  1146. goto cleanup;
  1147. }
  1148. //
  1149. // Any value entries in the current key that were not removed
  1150. // (because they were matched), marked as changed (because the
  1151. // value data had changed), or added (for new values) represent
  1152. // values that have been deleted. Mark them as such.
  1153. //
  1154. value = (PVALUE_ENTRY)GetFirstObject( currentKey );
  1155. while ( value != NULL ) {
  1156. if ( GetEntryState(value) == WATCH_NONE ) {
  1157. dprintf( 1, (" Marking entry for deleted value %ws\\%ws\n", currentPath, value->Name) );
  1158. SetEntryState( value, WATCH_DELETED );
  1159. }
  1160. value = (PVALUE_ENTRY)GetNextObject( currentKey, value );
  1161. }
  1162. //
  1163. // Any subkey entries in the current key that were not marked as
  1164. // matched (subkey still exists) or added (new subkey) represent
  1165. // subkeys that have been deleted. Mark them as such and delete
  1166. // the entries for the their children -- we don't need these
  1167. // entries any more.
  1168. //
  1169. key = (PKEY_ENTRY)GetFirstContainer( currentKey );
  1170. while ( key != NULL ) {
  1171. if ( GetEntryState(key) == WATCH_NONE ) {
  1172. dprintf( 1, (" Marking entry for deleted key %ws\\%ws\n", currentPath, key->Name) );
  1173. SetEntryState( key, WATCH_DELETED );
  1174. WatchFreeChildren( (PCONTAINER_ENTRY)key );
  1175. }
  1176. key = (PKEY_ENTRY)GetNextContainer( key );
  1177. }
  1178. //
  1179. // Find a subkey of the current directory that is marked as matched.
  1180. // We don't need to walk the subtrees for new or deleted keys.
  1181. //
  1182. key = (PKEY_ENTRY)GetFirstContainer( currentKey );
  1183. while ( key != NULL ) {
  1184. if ( GetEntryState(key) == WATCH_MATCHED ) {
  1185. break;
  1186. }
  1187. key = (PKEY_ENTRY)GetNextContainer( key );
  1188. }
  1189. //
  1190. // If a matched subkey was found, recurse into it.
  1191. //
  1192. if ( key != NULL ) {
  1193. currentKey = key;
  1194. DEBUG( wcscat( currentPath, L"\\" ) );
  1195. DEBUG( wcscat( currentPath, currentKey->Name ) );
  1196. } else {
  1197. //
  1198. // The key has no matched subkeys. Walk back up the
  1199. // tree looking for a sibling key to process.
  1200. //
  1201. while ( TRUE ) {
  1202. //
  1203. // Close the handle to the key.
  1204. //
  1205. if ( currentKey->Handle != HKEY_CURRENT_USER ) {
  1206. RegCloseKey( currentKey->Handle );
  1207. }
  1208. currentKey->Handle = NULL;
  1209. //
  1210. // If the current key is the root key, we're done.
  1211. //
  1212. if ( currentKey == rootKey ) {
  1213. currentKey = NULL;
  1214. break;
  1215. }
  1216. DEBUG( *wcsrchr(currentPath, L'\\') = 0 );
  1217. //
  1218. // If the parent key has more matched subkeys, recurse
  1219. // into the next one. Otherwise, move up to the parent
  1220. // key and try again.
  1221. //
  1222. key = (PKEY_ENTRY)GetNextContainer( currentKey );
  1223. while ( key != NULL ) {
  1224. if ( GetEntryState(key) == WATCH_MATCHED ) {
  1225. break;
  1226. }
  1227. key = (PKEY_ENTRY)GetNextContainer( key );
  1228. }
  1229. if ( key != NULL ) {
  1230. currentKey = key;
  1231. DEBUG( wcscat( currentPath, L"\\" ) );
  1232. DEBUG( wcscat( currentPath, currentKey->Name ) );
  1233. break;
  1234. } else {
  1235. currentKey = (PKEY_ENTRY)GetParent( currentKey );
  1236. }
  1237. }
  1238. }
  1239. } while ( currentKey != NULL );
  1240. return NO_ERROR;
  1241. cleanup:
  1242. //
  1243. // Error cleanup. Walk back up the tree closing handles.
  1244. //
  1245. do {
  1246. if ( (currentKey->Handle != NULL) && (currentKey->Handle != HKEY_CURRENT_USER) ) {
  1247. RegCloseKey( currentKey->Handle );
  1248. }
  1249. currentKey->Handle = NULL;
  1250. currentKey = (PKEY_ENTRY)GetParent( currentKey );
  1251. } while ( currentKey != NULL );
  1252. return error;
  1253. } // WatchKeyStop
  1254. DWORD
  1255. EnumerateKey (
  1256. IN HKEY KeyHandle,
  1257. IN PVOID Context,
  1258. IN PVALUE_ENUM_ROUTINE ValueEnumRoutine OPTIONAL,
  1259. IN PKEY_ENUM_ROUTINE KeyEnumRoutine OPTIONAL
  1260. )
  1261. /*++
  1262. Routine Description:
  1263. Enumerates the values and subkeys in a key. Calls an EnumRoutine for
  1264. each value and subkey.
  1265. Arguments:
  1266. KeyHandle - handle to the key to be enumerated.
  1267. Context - context value to be passed to EnumRoutine.
  1268. ValueEnumRoutine - routine to call for each value. If omitted, values
  1269. are not enumerated.
  1270. KeyEnumRoutine - routine to call for each key. If omitted, keys are
  1271. not enumerated.
  1272. Return Value:
  1273. DWORD - Win32 status of the operation.
  1274. --*/
  1275. {
  1276. DWORD error;
  1277. DWORD keyCount;
  1278. DWORD valueCount;
  1279. DWORD i;
  1280. DWORD type;
  1281. DWORD nameLength;
  1282. DWORD maxKeyNameLength;
  1283. DWORD maxValueNameLength;
  1284. DWORD dataLength;
  1285. DWORD maxValueDataLength;
  1286. PWCH nameBuffer;
  1287. PVOID dataBuffer;
  1288. FILETIME time;
  1289. //
  1290. // Query information about the key that is needed to query
  1291. // its values and subkeys.
  1292. //
  1293. error = RegQueryInfoKey( KeyHandle,
  1294. NULL,
  1295. NULL,
  1296. NULL,
  1297. &keyCount,
  1298. &maxKeyNameLength,
  1299. NULL,
  1300. &valueCount,
  1301. &maxValueNameLength,
  1302. &maxValueDataLength,
  1303. NULL,
  1304. NULL );
  1305. if ( error != NO_ERROR ) {
  1306. return error;
  1307. }
  1308. if ( ValueEnumRoutine != NULL ) {
  1309. //
  1310. // Allocate a buffer large enough for the longest value name and
  1311. // another buffer large enough for the longest value data.
  1312. //
  1313. nameBuffer = MyMalloc( (maxValueNameLength + 1) * sizeof(WCHAR) );
  1314. if ( nameBuffer == NULL ) {
  1315. return ERROR_NOT_ENOUGH_MEMORY;
  1316. }
  1317. dataBuffer = MyMalloc( maxValueDataLength );
  1318. if ( dataBuffer == NULL ) {
  1319. MyFree( nameBuffer );
  1320. return ERROR_NOT_ENOUGH_MEMORY;
  1321. }
  1322. //
  1323. // Query the key's values.
  1324. //
  1325. for ( i = 0; i < valueCount; i++ ) {
  1326. nameLength = maxValueNameLength + 1;
  1327. dataLength = maxValueDataLength;
  1328. error = RegEnumValue( KeyHandle,
  1329. i,
  1330. nameBuffer,
  1331. &nameLength,
  1332. NULL,
  1333. &type,
  1334. dataBuffer,
  1335. &dataLength );
  1336. if ( error != NO_ERROR ) {
  1337. MyFree( dataBuffer );
  1338. MyFree( nameBuffer );
  1339. return error;
  1340. }
  1341. //
  1342. // Call the EnumRoutine.
  1343. //
  1344. error = ValueEnumRoutine( Context,
  1345. nameLength,
  1346. nameBuffer,
  1347. type,
  1348. dataBuffer,
  1349. dataLength );
  1350. if ( error != NO_ERROR ) {
  1351. MyFree( dataBuffer );
  1352. MyFree( nameBuffer );
  1353. return error;
  1354. }
  1355. }
  1356. //
  1357. // Free the value data and value name buffers.
  1358. //
  1359. MyFree( dataBuffer );
  1360. dataBuffer = NULL;
  1361. MyFree( nameBuffer );
  1362. }
  1363. if ( KeyEnumRoutine != NULL) {
  1364. //
  1365. // Allocate a buffer large enough for the longest subkey name.
  1366. //
  1367. nameBuffer = MyMalloc( (maxKeyNameLength + 1) * sizeof(WCHAR) );
  1368. if ( nameBuffer == NULL ) {
  1369. return ERROR_NOT_ENOUGH_MEMORY;
  1370. }
  1371. //
  1372. // Query the key's subkeys.
  1373. //
  1374. for ( i = 0; i < keyCount; i++ ) {
  1375. nameLength = maxKeyNameLength + 1;
  1376. error = RegEnumKeyEx( KeyHandle,
  1377. i,
  1378. nameBuffer,
  1379. &nameLength,
  1380. NULL,
  1381. NULL,
  1382. NULL,
  1383. &time );
  1384. if ( error != NO_ERROR ) {
  1385. MyFree( nameBuffer );
  1386. return error;
  1387. }
  1388. //
  1389. // Call the EnumRoutine.
  1390. //
  1391. error = KeyEnumRoutine( Context,
  1392. nameLength,
  1393. nameBuffer );
  1394. if ( error != NO_ERROR ) {
  1395. MyFree( nameBuffer );
  1396. return error;
  1397. }
  1398. }
  1399. //
  1400. // Free the key name buffer.
  1401. //
  1402. MyFree( nameBuffer );
  1403. }
  1404. return NO_ERROR;
  1405. } // EnumerateKey
  1406. DWORD
  1407. AddValueAtStart (
  1408. IN PVOID Context,
  1409. IN DWORD ValueNameLength,
  1410. IN PWCH ValueName,
  1411. IN DWORD ValueType,
  1412. IN PVOID ValueData,
  1413. IN DWORD ValueDataLength
  1414. )
  1415. /*++
  1416. Routine Description:
  1417. Adds a value entry to the watch tree during WatchKeyStart.
  1418. Arguments:
  1419. Context - context value passed to EnumerateKey.
  1420. ValueNameLength - length in characters of ValueName.
  1421. ValueName - pointer to name of the value.
  1422. ValueType - type of the value data.
  1423. ValueData - pointer to value data.
  1424. ValueDataLength - length in bytes of ValueData.
  1425. Return Value:
  1426. DWORD - Win32 status of the operation.
  1427. --*/
  1428. {
  1429. PKEY_ENUM_CONTEXT context = Context;
  1430. PVALUE_ENTRY newValue;
  1431. //
  1432. // Add the value to the tree, capturing the value data.
  1433. //
  1434. dprintf( 2, (" found value %ws\\%ws\n", context->CurrentPath, ValueName) );
  1435. newValue = MyMalloc( sizeof(VALUE_ENTRY) - sizeof(WCHAR) +
  1436. ((ValueNameLength + 1) * sizeof(WCHAR)) +
  1437. ValueDataLength );
  1438. if ( newValue == NULL ) {
  1439. return ERROR_NOT_ENOUGH_MEMORY;
  1440. }
  1441. InitializeObject( newValue, 0 );
  1442. wcscpy( newValue->Name, ValueName );
  1443. newValue->Type = ValueType;
  1444. newValue->NameLength = ValueNameLength;
  1445. newValue->ValueDataLength = ValueDataLength;
  1446. memcpy( &newValue->Name + ValueNameLength + 1, ValueData, ValueDataLength );
  1447. InsertObject( context->ParentKey, newValue );
  1448. return NO_ERROR;
  1449. } // AddValueAtStart
  1450. DWORD
  1451. AddKeyAtStart (
  1452. IN PVOID Context,
  1453. IN DWORD KeyNameLength,
  1454. IN PWCH KeyName
  1455. )
  1456. /*++
  1457. Routine Description:
  1458. Adds a key entry to the watch tree during WatchKeyStart.
  1459. Arguments:
  1460. Context - context value passed to EnumerateKey.
  1461. KeyNameLength - length in characters of KeyName.
  1462. KeyName - pointer to name of the key.
  1463. Return Value:
  1464. DWORD - Win32 status of the operation.
  1465. --*/
  1466. {
  1467. PKEY_ENUM_CONTEXT context = Context;
  1468. PKEY_ENTRY newKey;
  1469. //
  1470. // Add the key to the tree.
  1471. //
  1472. dprintf( 2, (" found key %ws\\%ws\n", context->CurrentPath, KeyName) );
  1473. newKey = MyMalloc( sizeof(KEY_ENTRY) - sizeof(WCHAR) +
  1474. ((KeyNameLength + 1) * sizeof(WCHAR)) );
  1475. if ( newKey == NULL ) {
  1476. return ERROR_NOT_ENOUGH_MEMORY;
  1477. }
  1478. InitializeContainer( newKey, 0, context->ParentKey, FALSE );
  1479. wcscpy( newKey->Name, KeyName );
  1480. newKey->Handle = NULL;
  1481. InsertContainer( context->ParentKey, newKey );
  1482. return NO_ERROR;
  1483. } // AddKeyAtStart
  1484. DWORD
  1485. CheckValueAtStop (
  1486. IN PVOID Context,
  1487. IN DWORD ValueNameLength,
  1488. IN PWCH ValueName,
  1489. IN DWORD ValueType,
  1490. IN PVOID ValueData,
  1491. IN DWORD ValueDataLength
  1492. )
  1493. /*++
  1494. Routine Description:
  1495. Checks the watch tree for an enumerated value during WatchKeyStop.
  1496. Arguments:
  1497. Context - context value passed to EnumerateKey.
  1498. ValueNameLength - length in characters of ValueName.
  1499. ValueName - pointer to name of the value.
  1500. ValueType - type of the value data.
  1501. ValueData - pointer to value data.
  1502. ValueDataLength - length in bytes of ValueData.
  1503. Return Value:
  1504. DWORD - Win32 status of the operation.
  1505. --*/
  1506. {
  1507. PKEY_ENUM_CONTEXT context = Context;
  1508. PVALUE_ENTRY value;
  1509. BOOL ok;
  1510. //
  1511. // Check to see if the value existed at the start.
  1512. //
  1513. dprintf( 2, (" found value %ws\\%ws\n", context->CurrentPath, ValueName) );
  1514. ok = FALSE;
  1515. value = (PVALUE_ENTRY)GetFirstObject( context->ParentKey );
  1516. while ( value != NULL ) {
  1517. if ( _wcsicmp( value->Name, ValueName ) == 0 ) {
  1518. ok = TRUE;
  1519. break;
  1520. }
  1521. value = (PVALUE_ENTRY)GetNextObject( context->ParentKey, value );
  1522. }
  1523. if ( ok ) {
  1524. //
  1525. // The value existed at the start. If its data hasn't changed,
  1526. // remove it from the tree. Otherwise, mark it as changed.
  1527. //
  1528. if ( (value->Type == ValueType) &&
  1529. (value->ValueDataLength == ValueDataLength) &&
  1530. (memcmp( &value->Name + value->NameLength + 1,
  1531. ValueData,
  1532. ValueDataLength ) == 0) ) {
  1533. dprintf( 2, ("Deleting entry for unchanged value %ws\\%ws\n", context->CurrentPath, ValueName) );
  1534. RemoveObject( value );
  1535. MyFree( value );
  1536. } else {
  1537. dprintf( 1, (" Marking entry for changed value %ws\\%ws\n", context->CurrentPath, ValueName) );
  1538. SetEntryState( value, WATCH_CHANGED );
  1539. }
  1540. } else {
  1541. //
  1542. // The value is new. Add it to the tree.
  1543. //
  1544. // Note that we do not bother to save the value's data here,
  1545. // even though we have it in hand. The routines that
  1546. // populate userdifr already have to deal with querying
  1547. // value data, so the code is simpler this way.
  1548. //
  1549. value = MyMalloc( sizeof(VALUE_ENTRY) - sizeof(WCHAR) +
  1550. ((ValueNameLength + 1) * sizeof(WCHAR)) );
  1551. if ( value == NULL ) {
  1552. return ERROR_NOT_ENOUGH_MEMORY;
  1553. }
  1554. InitializeObject( value, WATCH_NEW );
  1555. wcscpy( value->Name, ValueName );
  1556. dprintf( 1, (" Adding entry for new value %ws\\%ws\n", context->CurrentPath, ValueName) );
  1557. InsertObject( context->ParentKey, value );
  1558. }
  1559. return NO_ERROR;
  1560. } // CheckValueAtStop
  1561. DWORD
  1562. CheckKeyAtStop (
  1563. IN PVOID Context,
  1564. IN DWORD KeyNameLength,
  1565. IN PWCH KeyName
  1566. )
  1567. /*++
  1568. Routine Description:
  1569. Checks the watch tree for an enumerated key during WatchKeyStop.
  1570. Arguments:
  1571. Context - context value passed to EnumerateKey.
  1572. KeyNameLength - length in characters of KeyName.
  1573. KeyName - pointer to name of the key.
  1574. Return Value:
  1575. DWORD - Win32 status of the operation.
  1576. --*/
  1577. {
  1578. PKEY_ENUM_CONTEXT context = Context;
  1579. PKEY_ENTRY key;
  1580. BOOL ok;
  1581. //
  1582. // Check to see if the subkey existed at the start.
  1583. //
  1584. dprintf( 2, (" found key %ws\\%ws\n", context->CurrentPath, KeyName) );
  1585. ok = FALSE;
  1586. key = (PKEY_ENTRY)GetFirstContainer( context->ParentKey );
  1587. while ( key != NULL ) {
  1588. if ( _wcsicmp( key->Name, KeyName ) == 0 ) {
  1589. ok = TRUE;
  1590. break;
  1591. }
  1592. key = (PKEY_ENTRY)GetNextContainer( key );
  1593. }
  1594. if ( ok ) {
  1595. //
  1596. // The key existed at the start. Mark it as matched.
  1597. // (We can't delete matched keys, as we do values,
  1598. // because they need to be in the tree for recursion.)
  1599. //
  1600. SetEntryState( key, WATCH_MATCHED );
  1601. } else {
  1602. //
  1603. // The key is new. Add it to the tree.
  1604. //
  1605. key = MyMalloc( sizeof(KEY_ENTRY) - sizeof(WCHAR) +
  1606. ((KeyNameLength + 1) * sizeof(WCHAR)) );
  1607. if ( key == NULL ) {
  1608. return ERROR_NOT_ENOUGH_MEMORY;
  1609. }
  1610. InitializeContainer( key, WATCH_NEW, context->ParentKey, FALSE );
  1611. wcscpy( key->Name, KeyName );
  1612. dprintf( 1, (" Adding entry for new key %ws\\%ws\n", context->CurrentPath, KeyName) );
  1613. InsertContainer( context->ParentKey, key );
  1614. }
  1615. return NO_ERROR;
  1616. } // CheckKeyAtStop
  1617. //
  1618. // Debug code for tracking allocates and frees.
  1619. //
  1620. #if WATCH_DEBUG
  1621. #undef MyMalloc
  1622. #undef MyFree
  1623. DWORD TotalAllocs = 0;
  1624. DWORD TotalFrees = 0;
  1625. DWORD PeakAllocs = 0;
  1626. DWORD TotalAllocated = 0;
  1627. DWORD TotalFreed = 0;
  1628. DWORD PeakAllocated = 0;
  1629. PVOID
  1630. MyMallocEx (
  1631. DWORD Size
  1632. )
  1633. {
  1634. PVOID p = MyMalloc( Size + 8 );
  1635. if ( p == NULL ) {
  1636. dprintf( 0, ("MyMallocEx: failure allocating %d bytes\n", Size) );
  1637. DumpMallocStats("");
  1638. DbgBreakPoint();
  1639. return NULL;
  1640. }
  1641. TotalAllocs++;
  1642. if ( (TotalAllocs - TotalFrees) > PeakAllocs ) {
  1643. PeakAllocs = TotalAllocs - TotalFrees;
  1644. }
  1645. TotalAllocated += Size;
  1646. if ( (TotalAllocated - TotalFreed) > PeakAllocated ) {
  1647. PeakAllocated = TotalAllocated - TotalFreed;
  1648. }
  1649. *(PDWORD)p = Size;
  1650. return (PVOID)((PCHAR)p + 8);
  1651. }
  1652. VOID
  1653. MyFreeEx (
  1654. PVOID p
  1655. )
  1656. {
  1657. PVOID pp = (PVOID)((PCHAR)p - 8);
  1658. TotalFrees++;
  1659. TotalFreed += *(PDWORD)pp;
  1660. MyFree( pp );
  1661. }
  1662. VOID
  1663. DumpMallocStats (
  1664. PSZ Event
  1665. )
  1666. {
  1667. if ( *Event != 0 ) {
  1668. dprintf( 0, ("%s\n", Event) );
  1669. }
  1670. dprintf( 0, ("Allocations %d, frees %d, active allocs %d\n",
  1671. TotalAllocs, TotalFrees, TotalAllocs - TotalFrees) );
  1672. dprintf( 0, ("Bytes allocated %d, bytes freed %d, active bytes %d\n",
  1673. TotalAllocated, TotalFreed, TotalAllocated - TotalFreed) );
  1674. dprintf( 0, ("Peak allocs %d, peak bytes %d\n",
  1675. PeakAllocs, PeakAllocated) );
  1676. return;
  1677. }
  1678. #endif
  1679. typedef HRESULT (*PFNSHGETFOLDERPATH)(HWND hwnd, int csidl, HANDLE hToken, DWORD dwType, LPTSTR pszPath);
  1680. BOOL
  1681. GetSpecialFolderPath (
  1682. IN INT csidl,
  1683. IN LPWSTR lpPath
  1684. )
  1685. /*++
  1686. Routine Description:
  1687. Gets the path to the requested special folder.
  1688. (This function was copied from userenv.dll)
  1689. Arguments:
  1690. csid - CSIDL of the special folder
  1691. lpPath - Path to place result in assumed to be MAX_PATH in size
  1692. Return Value:
  1693. TRUE if successful
  1694. FALSE if an error occurs
  1695. --*/
  1696. {
  1697. HRESULT hResult;
  1698. HINSTANCE hInstShell32;
  1699. PFNSHGETFOLDERPATH pfnSHGetFolderPath;
  1700. //
  1701. // Load the function we need
  1702. //
  1703. hInstShell32 = LoadLibrary(L"shell32.dll");
  1704. if (!hInstShell32) {
  1705. SetupDebugPrint1( L"SETUP: GetSpecialFolderPath() failed to load shell32. Error = %d.", GetLastError() );
  1706. return FALSE;
  1707. }
  1708. pfnSHGetFolderPath = (PFNSHGETFOLDERPATH)GetProcAddress (hInstShell32, "SHGetFolderPathW");
  1709. if (!pfnSHGetFolderPath) {
  1710. SetupDebugPrint1( L"SETUP: GetSpecialFolderPath() failed to find SHGetFolderPath(). Error = %d.", GetLastError() );
  1711. FreeLibrary (hInstShell32);
  1712. return FALSE;
  1713. }
  1714. //
  1715. // Ask the shell for the folder location
  1716. //
  1717. hResult = pfnSHGetFolderPath (
  1718. NULL,
  1719. csidl | CSIDL_FLAG_CREATE,
  1720. (HANDLE) -1, // this specifies .Default
  1721. 0,
  1722. lpPath);
  1723. if (S_OK != hResult) {
  1724. SetupDebugPrint1( L"SETUP: GetSpecialFolderPath: SHGetFolderPath() returned %d.", hResult );
  1725. }
  1726. //
  1727. // Clean up
  1728. //
  1729. FreeLibrary (hInstShell32);
  1730. return (S_OK == hResult);
  1731. }