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.

2028 lines
41 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. userdiff.c
  5. Abstract:
  6. This module contains routines for updating the userdifr hive.
  7. In the following, the list of changes to be applied when a user
  8. logs on after an upgrade will be called UserRun. UserRun is
  9. developed from three sources:
  10. 1) The UserRun generated on the last upgrade. This forms the
  11. basis for this upgrade's UserRun.
  12. 2) The list of changes shipped with the system. Call this
  13. UserShip. The changes from all build numbers that are
  14. present in UserShip but not in UserRun are copied into
  15. UserRun. (Note that if a build number is already present
  16. in UserRun, we don't copy the UserShip changes. This
  17. means that changes cannot be made retroactively in UserShip.)
  18. 3) Changes made during the current upgrade. These changes are
  19. detected at run time (see watch.c). All changes detected
  20. during the upgrade are added to UserRun.
  21. Author:
  22. Chuck Lenzmeier (chuckl)
  23. Revision History:
  24. --*/
  25. #include "setupp.h"
  26. #pragma hdrstop
  27. //
  28. // Debugging aids.
  29. //
  30. #if USERDIFF_DEBUG
  31. DWORD UserdiffDebugLevel = 0;
  32. #define dprintf(_lvl_,_x_) if ((_lvl_) <= UserdiffDebugLevel) DbgPrint _x_
  33. #else
  34. #define dprintf(_lvl_,_x_)
  35. #endif
  36. //
  37. // Macro to calculate length of string including terminator.
  38. //
  39. #define SZLEN(_wcs) ((wcslen(_wcs) + 1) * sizeof(WCHAR))
  40. //
  41. // Context record used in this module to track registry state.
  42. //
  43. typedef struct _USERRUN_CONTEXT {
  44. BOOL UserRunLoaded;
  45. HKEY UserRunKey;
  46. HKEY BuildKey;
  47. HKEY FilesKey;
  48. HKEY HiveKey;
  49. ULONG FilesIndex;
  50. ULONG HiveIndex;
  51. HKEY UserShipKey;
  52. } USERRUN_CONTEXT, *PUSERRUN_CONTEXT;
  53. //
  54. // Context record used in MakeUserRunEnumRoutine.
  55. //
  56. typedef struct _KEY_ENUM_CONTEXT {
  57. PUSERRUN_CONTEXT UserRunContext;
  58. PWCH CurrentPath;
  59. } KEY_ENUM_CONTEXT, *PKEY_ENUM_CONTEXT;
  60. //
  61. // Forward declaration of local subroutines.
  62. //
  63. DWORD
  64. LoadUserRun (
  65. OUT PUSERRUN_CONTEXT Context,
  66. IN PWCH UserRunPath
  67. );
  68. DWORD
  69. MergeUserShipIntoUserRun (
  70. IN OUT PUSERRUN_CONTEXT Context,
  71. IN PWCH UserShipPath
  72. );
  73. DWORD
  74. CreateAndLoadUserRun (
  75. OUT PUSERRUN_CONTEXT Context,
  76. IN PWCH UserRunPath
  77. );
  78. DWORD
  79. OpenUserRunKeys (
  80. IN OUT PUSERRUN_CONTEXT Context
  81. );
  82. VOID
  83. UnloadUserRun (
  84. IN OUT PUSERRUN_CONTEXT Context
  85. );
  86. DWORD
  87. CheckUserShipKey (
  88. IN PVOID Context,
  89. IN DWORD KeyNameLength,
  90. IN PWCH KeyName
  91. );
  92. DWORD
  93. MakeUserRunEnumRoutine (
  94. IN PVOID Context,
  95. IN PWATCH_ENTRY Entry
  96. );
  97. DWORD
  98. MakeAddDirectory (
  99. IN PUSERRUN_CONTEXT Context,
  100. IN PWCH Name
  101. );
  102. DWORD
  103. MakeAddValue (
  104. IN PUSERRUN_CONTEXT Context,
  105. IN PWCH Name
  106. );
  107. DWORD
  108. MakeDeleteValue (
  109. IN PUSERRUN_CONTEXT Context,
  110. IN PWCH Name
  111. );
  112. DWORD
  113. MakeAddKey (
  114. IN PUSERRUN_CONTEXT Context,
  115. IN PWCH Name
  116. );
  117. DWORD
  118. MakeDeleteKey (
  119. IN PUSERRUN_CONTEXT Context,
  120. IN PWCH Name
  121. );
  122. DWORD
  123. AddDirectory (
  124. IN PUSERRUN_CONTEXT Context,
  125. IN PWCH FullPath,
  126. IN PWCH Path
  127. );
  128. DWORD
  129. AddKey (
  130. IN PUSERRUN_CONTEXT Context,
  131. IN PWCH Path
  132. );
  133. DWORD
  134. AddValueDuringAddKey (
  135. IN PVOID Context,
  136. IN DWORD ValueNameLength,
  137. IN PWCH ValueName,
  138. IN DWORD ValueType,
  139. IN PVOID ValueData,
  140. IN DWORD ValueDataLength
  141. );
  142. DWORD
  143. AddKeyDuringAddKey (
  144. IN PVOID Context,
  145. IN DWORD KeyNameLength,
  146. IN PWCH KeyName
  147. );
  148. DWORD
  149. AddValue (
  150. IN PUSERRUN_CONTEXT Context,
  151. IN PWCH KeyName OPTIONAL,
  152. IN PWCH ValueName,
  153. IN DWORD ValueType,
  154. IN PVOID ValueData,
  155. IN DWORD ValueDataLength
  156. );
  157. DWORD
  158. CreateUserRunSimpleFileKey (
  159. IN PUSERRUN_CONTEXT Context,
  160. IN DWORD Action,
  161. IN PWCH Name
  162. );
  163. DWORD
  164. CreateUserRunKey (
  165. IN PUSERRUN_CONTEXT Context,
  166. IN BOOL IsFileKey,
  167. OUT PHKEY NewKeyHandle
  168. );
  169. DWORD
  170. QueryValue (
  171. IN PWCH KeyName OPTIONAL,
  172. IN PWCH ValueName,
  173. OUT PDWORD ValueType,
  174. OUT PVOID *ValueData,
  175. OUT PDWORD ValueDataLength
  176. );
  177. VOID
  178. SzToMultiSz (
  179. IN PWCH Sz,
  180. OUT PWCH *MultiSz,
  181. OUT PDWORD MultiSzLength
  182. );
  183. DWORD
  184. MakeUserdifr (
  185. IN PVOID WatchHandle
  186. )
  187. /*++
  188. Routine Description:
  189. Creates the UserRun hive based on the changes made to the current user's
  190. profile directory and the HKEY_CURRENT_USER key.
  191. Arguments:
  192. WatchHandle - supplies the handle returned by WatchStart.
  193. Return Value:
  194. DWORD - Win32 status of the operation.
  195. --*/
  196. {
  197. HKEY userrunKey;
  198. USERRUN_CONTEXT context;
  199. DWORD error;
  200. DWORD disposition;
  201. WCHAR userRunPath[MAX_PATH + 1];
  202. WCHAR userShipPath[MAX_PATH + 1];
  203. //
  204. // Merge UserShip with UserRun.
  205. //
  206. // If both UserRun and UserShip exist, merge into UserRun those build
  207. // keys from UserShip that do no exist in UserRun.
  208. //
  209. // If the UserRun hive file doesn't exist, this means that no
  210. // upgrade has ever been run on this machine. Copy the UserShip
  211. // hive file into place as UserRun. This effectively does the
  212. // registry merge by using a file copy.
  213. //
  214. // In the unlikely event that neither UserRun nor UserShip exists,
  215. // create an empty UserRun.
  216. //
  217. //
  218. // Initialize the context record.
  219. //
  220. context.UserRunLoaded = FALSE;
  221. context.UserRunKey = NULL;
  222. context.BuildKey = NULL;
  223. context.FilesKey = NULL;
  224. context.HiveKey = NULL;
  225. //
  226. // Enable SeBackupPrivilege and SeRestorePrivilege.
  227. //
  228. pSetupEnablePrivilege( SE_BACKUP_NAME, TRUE );
  229. pSetupEnablePrivilege( SE_RESTORE_NAME, TRUE );
  230. //
  231. // Check to see whether UserRun exists.
  232. //
  233. error = GetWindowsDirectory( userRunPath, MAX_PATH );
  234. if( error == 0) {
  235. MYASSERT(FALSE);
  236. return ERROR_PATH_NOT_FOUND;
  237. }
  238. wcscat( userRunPath, TEXT("\\") );
  239. wcscpy( userShipPath, userRunPath );
  240. wcscat( userRunPath, USERRUN_PATH );
  241. wcscat( userShipPath, USERSHIP_PATH );
  242. if ( FileExists( userRunPath, NULL ) ) {
  243. //
  244. // UserRun exists. Load it into the registry. Check to see whether
  245. // UserShip exists.
  246. //
  247. error = LoadUserRun( &context, userRunPath );
  248. if ( error == NO_ERROR ) {
  249. if ( FileExists( userShipPath, NULL ) ) {
  250. //
  251. // UserShip also exists. Merge UserShip into UserRun.
  252. //
  253. error = MergeUserShipIntoUserRun( &context, userShipPath );
  254. } else {
  255. //
  256. // UserShip doesn't exist. Just use the existing UserRun.
  257. //
  258. }
  259. }
  260. } else {
  261. //
  262. // UserRun doesn't exist. If UserShip exists, just copy the UserShip
  263. // hive file into place as UserRun. If neither one exists, create
  264. // an empty UserRun.
  265. //
  266. if ( FileExists( userShipPath, NULL ) ) {
  267. //
  268. // UserShip exists. Copy UserShip into UserRun.
  269. //
  270. if ( !CopyFile( userShipPath, userRunPath, TRUE ) ) {
  271. error = GetLastError();
  272. } else {
  273. //
  274. // Load the new UserRun.
  275. //
  276. error = LoadUserRun( &context, userRunPath );
  277. }
  278. } else {
  279. //
  280. // UserShip doesn't exist. Create an empty UserRun.
  281. //
  282. error = CreateAndLoadUserRun( &context, userRunPath );
  283. }
  284. }
  285. //
  286. // Add changes from this upgrade to UserRun.
  287. //
  288. if ( error == NO_ERROR ) {
  289. error = OpenUserRunKeys( &context );
  290. if ( error == NO_ERROR ) {
  291. error = WatchEnum( WatchHandle, &context, MakeUserRunEnumRoutine );
  292. }
  293. }
  294. //
  295. // Unload the UserRun hive.
  296. //
  297. UnloadUserRun( &context );
  298. return error;
  299. } // MakeUserdifr
  300. DWORD
  301. LoadUserRun (
  302. OUT PUSERRUN_CONTEXT Context,
  303. IN PWCH UserRunPath
  304. )
  305. /*++
  306. Routine Description:
  307. Loads the UserRun hive into the registry and opens the root key.
  308. Arguments:
  309. Context - pointer to context record.
  310. UserRunPath - supplies the path to the UserRun hive file.
  311. Return Value:
  312. DWORD - Win32 status of the operation.
  313. --*/
  314. {
  315. DWORD error;
  316. //
  317. // Load the UserRun hive into the registry.
  318. //
  319. error = RegLoadKey( HKEY_USERS, USERRUN_KEY, UserRunPath );
  320. if ( error != NO_ERROR ) {
  321. return error;
  322. }
  323. Context->UserRunLoaded = TRUE;
  324. //
  325. // Open the UserRun root.
  326. //
  327. error = RegOpenKeyEx( HKEY_USERS,
  328. USERRUN_KEY,
  329. 0,
  330. KEY_READ | KEY_WRITE,
  331. &Context->UserRunKey );
  332. return error;
  333. } // LoadUserRun
  334. DWORD
  335. MergeUserShipIntoUserRun (
  336. IN OUT PUSERRUN_CONTEXT Context,
  337. IN PWCH UserShipPath
  338. )
  339. /*++
  340. Routine Description:
  341. Merges the UserShip hive into the UserRun hive.
  342. Arguments:
  343. Context - pointer to context record.
  344. UserShipPath - supplies the path to the UserShip file.
  345. Return Value:
  346. DWORD - Win32 status of the operation.
  347. --*/
  348. {
  349. DWORD error;
  350. DWORD disposition;
  351. //
  352. // Load the UserShip hive into the registry.
  353. //
  354. error = RegLoadKey( HKEY_USERS, USERSHIP_KEY, UserShipPath );
  355. if ( error == NO_ERROR ) {
  356. //
  357. // Open the UserShip root.
  358. //
  359. error = RegOpenKeyEx( HKEY_USERS,
  360. USERSHIP_KEY,
  361. 0,
  362. KEY_READ | KEY_WRITE,
  363. &Context->UserShipKey );
  364. if ( error == NO_ERROR ) {
  365. //
  366. // Enumerate the build number keys in UserShip, looking for
  367. // builds that aren't represented in UserRun.
  368. //
  369. error = EnumerateKey( Context->UserShipKey,
  370. Context,
  371. NULL, // don't enumerate values
  372. CheckUserShipKey );
  373. //
  374. // Close the UserShip root.
  375. //
  376. RegCloseKey( Context->UserShipKey );
  377. }
  378. //
  379. // Unload the UserShip hive.
  380. //
  381. RegUnLoadKey( HKEY_USERS, USERSHIP_KEY );
  382. }
  383. return error;
  384. } // MergeUserShipIntoUserRun
  385. DWORD
  386. CreateAndLoadUserRun (
  387. OUT PUSERRUN_CONTEXT Context,
  388. IN PWCH UserRunPath
  389. )
  390. /*++
  391. Routine Description:
  392. Create a new UserRun hive and load it into the registry.
  393. Arguments:
  394. Context - pointer to context record.
  395. UserRunPath - supplies the path to the UserRun file.
  396. Return Value:
  397. DWORD - Win32 status of the operation.
  398. --*/
  399. {
  400. DWORD error;
  401. DWORD disposition;
  402. HKEY userRunKey;
  403. //
  404. // Create the UserRun key under HKEY_CURRENT_USER.
  405. //
  406. // NOTE: Trying to create this under HKEY_USERS doesn't work.
  407. //
  408. error = RegCreateKeyEx( HKEY_CURRENT_USER,
  409. USERRUN_KEY,
  410. 0,
  411. NULL,
  412. REG_OPTION_NON_VOLATILE,
  413. KEY_ALL_ACCESS,
  414. NULL,
  415. &userRunKey,
  416. &disposition );
  417. if ( error != NO_ERROR ) {
  418. return error;
  419. }
  420. //
  421. // Save the newly created UserRun key to a hive file.
  422. //
  423. error = RegSaveKey( userRunKey,
  424. UserRunPath,
  425. NULL );
  426. //
  427. // Close and delete the UserRun key.
  428. //
  429. RegCloseKey( userRunKey );
  430. RegDeleteKey( HKEY_CURRENT_USER, USERRUN_KEY );
  431. //
  432. // Now load UserRun back into the registry.
  433. //
  434. if ( error == NO_ERROR ) {
  435. error = LoadUserRun( Context, UserRunPath );
  436. }
  437. return error;
  438. } // CreateAndLoadUserRun
  439. DWORD
  440. OpenUserRunKeys (
  441. IN OUT PUSERRUN_CONTEXT Context
  442. )
  443. /*++
  444. Routine Description:
  445. Opens the core keys in the UserRun hive.
  446. Arguments:
  447. Context - pointer to context record.
  448. Return Value:
  449. DWORD - Win32 status of the operation.
  450. --*/
  451. {
  452. DWORD error;
  453. DWORD disposition;
  454. OSVERSIONINFO versionInfo;
  455. WCHAR buildNumber[6];
  456. //
  457. // Get the current build number.
  458. //
  459. versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  460. if ( !GetVersionEx( &versionInfo ) ) {
  461. return GetLastError();
  462. }
  463. wsprintf( buildNumber, TEXT("%d"), LOWORD(versionInfo.dwBuildNumber) );
  464. //
  465. // Open/create a subkey for the current build.
  466. //
  467. error = RegCreateKeyEx( Context->UserRunKey,
  468. buildNumber,
  469. 0,
  470. NULL,
  471. REG_OPTION_NON_VOLATILE,
  472. KEY_READ | KEY_WRITE,
  473. NULL,
  474. &Context->BuildKey,
  475. &disposition );
  476. if ( error != NO_ERROR ) {
  477. return error;
  478. }
  479. //
  480. // Create a Files subkey.
  481. //
  482. error = RegCreateKeyEx( Context->BuildKey,
  483. FILES_KEY,
  484. 0,
  485. NULL,
  486. REG_OPTION_NON_VOLATILE,
  487. KEY_READ | KEY_WRITE,
  488. NULL,
  489. &Context->FilesKey,
  490. &disposition );
  491. if ( error != NO_ERROR ) {
  492. return error;
  493. }
  494. //
  495. // Create a Hive subkey.
  496. //
  497. error = RegCreateKeyEx( Context->BuildKey,
  498. HIVE_KEY,
  499. 0,
  500. NULL,
  501. REG_OPTION_NON_VOLATILE,
  502. KEY_READ | KEY_WRITE,
  503. NULL,
  504. &Context->HiveKey,
  505. &disposition );
  506. if ( error != NO_ERROR ) {
  507. return error;
  508. }
  509. //
  510. // Set the FilesIndex and HiveIndex so that we append to whatever
  511. // information already exists for the current build.
  512. //
  513. error = RegQueryInfoKey( Context->FilesKey,
  514. NULL,
  515. NULL,
  516. NULL,
  517. &Context->FilesIndex,
  518. NULL,
  519. NULL,
  520. NULL,
  521. NULL,
  522. NULL,
  523. NULL,
  524. NULL );
  525. if ( error != NO_ERROR ) {
  526. return error;
  527. }
  528. error = RegQueryInfoKey( Context->HiveKey,
  529. NULL,
  530. NULL,
  531. NULL,
  532. &Context->HiveIndex,
  533. NULL,
  534. NULL,
  535. NULL,
  536. NULL,
  537. NULL,
  538. NULL,
  539. NULL );
  540. return error;
  541. } // OpenUserRunKeys
  542. VOID
  543. UnloadUserRun (
  544. IN OUT PUSERRUN_CONTEXT Context
  545. )
  546. /*++
  547. Routine Description:
  548. Unloads the UserRun hive from the registry.
  549. Arguments:
  550. Context - pointer to context record.
  551. Return Value:
  552. None.
  553. --*/
  554. {
  555. //
  556. // Close the core keys, if they are open.
  557. //
  558. if ( Context->HiveKey != NULL ) {
  559. RegCloseKey( Context->HiveKey );
  560. }
  561. if ( Context->FilesKey != NULL ) {
  562. RegCloseKey( Context->FilesKey );
  563. }
  564. if ( Context->BuildKey != NULL ) {
  565. RegCloseKey( Context->BuildKey );
  566. }
  567. //
  568. // Close the root key, if it is open.
  569. //
  570. if ( Context->UserRunKey != NULL ) {
  571. RegCloseKey( Context->UserRunKey );
  572. }
  573. //
  574. // Unload the hive, if it has been loaded.
  575. //
  576. if ( Context->UserRunLoaded ) {
  577. RegUnLoadKey( HKEY_USERS, USERRUN_KEY );
  578. }
  579. return;
  580. } // UnloadUserRun
  581. DWORD
  582. CheckUserShipKey (
  583. IN PVOID Context,
  584. IN DWORD KeyNameLength,
  585. IN PWCH KeyName
  586. )
  587. /*++
  588. Routine Description:
  589. Checks an enumerated key in the UserShip hive to see if a corresponding
  590. key is present in the UserRun hive. If not, copies the key from UserShip
  591. into UserRun.
  592. Arguments:
  593. Context - pointer to context record.
  594. KeyNameLength - length in characters of key name.
  595. KeyName - pointer to name of key.
  596. Return Value:
  597. DWORD - Win32 status of the operation.
  598. --*/
  599. {
  600. PUSERRUN_CONTEXT context = Context;
  601. DWORD error;
  602. DWORD disposition;
  603. HKEY userRunBuildKey;
  604. HKEY userShipBuildKey;
  605. WCHAR path[MAX_PATH + 1];
  606. //
  607. // We have the name of a key in UserShip. Try to open the
  608. // corresponding key in UserRun.
  609. //
  610. error = RegCreateKeyEx( context->UserRunKey,
  611. KeyName,
  612. 0,
  613. NULL,
  614. REG_OPTION_NON_VOLATILE,
  615. KEY_READ | KEY_WRITE,
  616. NULL,
  617. &userRunBuildKey,
  618. &disposition );
  619. if ( error != NO_ERROR ) {
  620. return error;
  621. }
  622. //
  623. // No error occurred. The key was either opened or created.
  624. //
  625. if ( disposition == REG_OPENED_EXISTING_KEY ) {
  626. //
  627. // The key already existed in UserRun. We assume that it already
  628. // contains the information that is in UserShip.
  629. //
  630. } else {
  631. //
  632. // The key didn't exist in UserRun. Copy the key from UserShip
  633. // into UserRun. This is done by saving the UserShip key to
  634. // a file, then restoring the file back under the UserRun key.
  635. //
  636. // Note that the copy operation will fail if the file already
  637. // exists.
  638. //
  639. error = RegOpenKeyEx( context->UserShipKey,
  640. KeyName,
  641. 0,
  642. KEY_READ,
  643. &userShipBuildKey );
  644. if ( error == NO_ERROR ) {
  645. GetWindowsDirectory( path, MAX_PATH );
  646. wcscat( path, TEXT("\\") );
  647. wcscat( path, USERTMP_PATH );
  648. error = RegSaveKey( userShipBuildKey,
  649. path,
  650. NULL );
  651. if ( error == NO_ERROR ) {
  652. error = RegRestoreKey( userRunBuildKey,
  653. path,
  654. 0 );
  655. DeleteFile( path );
  656. }
  657. RegCloseKey( userShipBuildKey );
  658. }
  659. }
  660. //
  661. // Close the UserRun key.
  662. //
  663. RegCloseKey( userRunBuildKey );
  664. return error;
  665. } // CheckUserShipKey
  666. DWORD
  667. MakeUserRunEnumRoutine (
  668. IN PVOID Context,
  669. IN PWATCH_ENTRY Entry
  670. )
  671. /*++
  672. Routine Description:
  673. EnumRoutine for the MakeUserdifr operation. Calls the appropriate
  674. processing routine based on the entry type (file/directory/key/value)
  675. and the change type (changed, new, deleted).
  676. Arguments:
  677. Context - context value passed to WatchEnum.
  678. Entry - description of the changed entry.
  679. Return Value:
  680. DWORD - Win32 status of the operation.
  681. --*/
  682. {
  683. PUSERRUN_CONTEXT context = Context;
  684. DWORD index;
  685. HKEY newKey;
  686. DWORD error;
  687. DWORD dword;
  688. //
  689. // Call the appropriate processing routine.
  690. //
  691. switch ( Entry->EntryType ) {
  692. case WATCH_DIRECTORY:
  693. switch ( Entry->ChangeType ) {
  694. case WATCH_NEW:
  695. dprintf( 1, ("New directory %ws\n", Entry->Name) );
  696. error = MakeAddDirectory( Context, Entry->Name );
  697. break;
  698. case WATCH_DELETED:
  699. dprintf( 1, ("Deleted directory %ws\n", Entry->Name) );
  700. error = CreateUserRunSimpleFileKey( Context, 2, Entry->Name );
  701. break;
  702. default:
  703. error = ERROR_INVALID_PARAMETER;
  704. }
  705. break;
  706. case WATCH_FILE:
  707. switch ( Entry->ChangeType ) {
  708. case WATCH_NEW:
  709. case WATCH_CHANGED:
  710. dprintf( 1, ("New or changed file %ws\n", Entry->Name) );
  711. error = CreateUserRunSimpleFileKey( Context, 3, Entry->Name );
  712. break;
  713. case WATCH_DELETED:
  714. dprintf( 1, ("Deleted file %ws\n", Entry->Name) );
  715. error = CreateUserRunSimpleFileKey( Context, 4, Entry->Name );
  716. break;
  717. default:
  718. error = ERROR_INVALID_PARAMETER;
  719. }
  720. break;
  721. case WATCH_KEY:
  722. switch ( Entry->ChangeType ) {
  723. case WATCH_NEW:
  724. dprintf( 1, ("New key %ws\n", Entry->Name) );
  725. error = MakeAddKey( Context, Entry->Name );
  726. break;
  727. case WATCH_DELETED:
  728. dprintf( 1, ("Deleted key %ws\n", Entry->Name) );
  729. error = MakeDeleteKey( Context, Entry->Name );
  730. break;
  731. default:
  732. error = ERROR_INVALID_PARAMETER;
  733. }
  734. break;
  735. case WATCH_VALUE:
  736. switch ( Entry->ChangeType ) {
  737. case WATCH_NEW:
  738. case WATCH_CHANGED:
  739. dprintf( 1, ("New or changed value %ws\n", Entry->Name) );
  740. error = MakeAddValue( Context, Entry->Name );
  741. break;
  742. case WATCH_DELETED:
  743. dprintf( 1, ("Deleted value %ws\n", Entry->Name) );
  744. error = MakeDeleteValue( Context, Entry->Name );
  745. break;
  746. default:
  747. error = ERROR_INVALID_PARAMETER;
  748. }
  749. break;
  750. default:
  751. error = ERROR_INVALID_PARAMETER;
  752. }
  753. return error;
  754. } // MakeUserRunEnumRoutine
  755. DWORD
  756. MakeAddDirectory (
  757. IN PUSERRUN_CONTEXT Context,
  758. IN PWCH Name
  759. )
  760. /*++
  761. Routine Description:
  762. Adds entries to the UserRun hive for a new directory.
  763. Arguments:
  764. Context - context value passed to WatchEnum.
  765. Name - name of new directory (relative to root of watched tree).
  766. Return Value:
  767. DWORD - Win32 status of the operation.
  768. --*/
  769. {
  770. WCHAR fullpath[MAX_PATH + 1];
  771. PWCH path;
  772. BOOL ok;
  773. //
  774. // Get the full path to the new directory. "fullpath" is the full path;
  775. // "path" is just this directory.
  776. //
  777. ok = GetSpecialFolderPath ( CSIDL_PROGRAMS, fullpath );
  778. if ( !ok ) {
  779. return GetLastError();
  780. }
  781. wcscat( fullpath, TEXT("\\") );
  782. path = fullpath + wcslen(fullpath);
  783. wcscpy( path, Name );
  784. //
  785. // Call AddDirectory to do the recursive work.
  786. //
  787. return AddDirectory( Context, fullpath, path );
  788. } // MakeAddDirectory
  789. DWORD
  790. MakeAddValue (
  791. IN PUSERRUN_CONTEXT Context,
  792. IN PWCH Name
  793. )
  794. /*++
  795. Routine Description:
  796. Adds an entry to the UserRun hive for a new value.
  797. Arguments:
  798. Context - context value passed to WatchEnum.
  799. Name - name of new value (relative to HKEY_CURRENT_USER).
  800. Return Value:
  801. DWORD - Win32 status of the operation.
  802. --*/
  803. {
  804. HKEY newKey;
  805. PWCH keyName;
  806. PWCH valueName;
  807. PWCH splitPoint;
  808. DWORD valueType;
  809. PVOID valueData;
  810. DWORD valueDataLength;
  811. DWORD error;
  812. DWORD dword;
  813. //
  814. // Split the name into key and value portions.
  815. //
  816. splitPoint = wcsrchr( Name, TEXT('\\') );
  817. if ( splitPoint != NULL ) {
  818. keyName = Name;
  819. valueName = splitPoint + 1;
  820. *splitPoint = 0;
  821. } else {
  822. keyName = NULL;
  823. valueName = Name;
  824. }
  825. //
  826. // Query the value data.
  827. //
  828. valueData = NULL;
  829. error = QueryValue( keyName, valueName, &valueType, &valueData, &valueDataLength );
  830. //
  831. // Add an entry for the value.
  832. //
  833. if ( error == NO_ERROR ) {
  834. error = AddValue( Context, keyName, valueName, valueType, valueData, valueDataLength );
  835. }
  836. //
  837. // Free the value data buffer allocated by QueryValue.
  838. //
  839. if ( valueData != NULL ) {
  840. MyFree( valueData );
  841. }
  842. //
  843. // Restore the input value name string.
  844. //
  845. if ( splitPoint != NULL ) {
  846. *splitPoint = TEXT('\\');
  847. }
  848. return error;
  849. } // MakeAddValue
  850. DWORD
  851. MakeDeleteValue (
  852. IN PUSERRUN_CONTEXT Context,
  853. IN PWCH Name
  854. )
  855. /*++
  856. Routine Description:
  857. Adds an entry to the UserRun hive for a deleted value.
  858. Arguments:
  859. Context - context value passed to WatchEnum.
  860. Name - name of deleted value (relative to HKEY_CURRENT_USER).
  861. Return Value:
  862. DWORD - Win32 status of the operation.
  863. --*/
  864. {
  865. HKEY newKey;
  866. PWCH keyName;
  867. PWCH valueName;
  868. PWCH valueNames;
  869. PWCH splitPoint;
  870. DWORD valueNamesLength;
  871. DWORD error;
  872. DWORD dword;
  873. error = NO_ERROR;
  874. //
  875. // Split the name into key and value portions. Create a MULTI_SZ
  876. // version of the deleted name (to match userdiff format).
  877. //
  878. splitPoint = wcsrchr( Name, TEXT('\\') );
  879. if ( splitPoint != NULL ) {
  880. keyName = Name;
  881. valueName = splitPoint + 1;
  882. *splitPoint = 0;
  883. } else {
  884. keyName = NULL;
  885. valueName = Name;
  886. }
  887. SzToMultiSz( valueName, &valueNames, &valueNamesLength );
  888. if ( valueNames == NULL ) {
  889. error = ERROR_NOT_ENOUGH_MEMORY;
  890. }
  891. //
  892. // Create an entry key and popuplate it.
  893. //
  894. newKey = NULL;
  895. if ( error == NO_ERROR ) {
  896. error = CreateUserRunKey( Context, FALSE, &newKey );
  897. }
  898. if ( error == NO_ERROR ) {
  899. dword = 4;
  900. error = RegSetValueEx( newKey, ACTION_VALUE, 0, REG_DWORD, (PBYTE)&dword, sizeof(DWORD) );
  901. }
  902. if ( error == NO_ERROR ) {
  903. error = RegSetValueEx( newKey, KEYNAME_VALUE, 0, REG_SZ, (PBYTE)keyName, SZLEN(keyName) );
  904. }
  905. if ( error == NO_ERROR ) {
  906. error = RegSetValueEx( newKey, VALUENAMES_VALUE, 0, REG_MULTI_SZ, (PBYTE)valueNames, valueNamesLength );
  907. }
  908. if ( error == NO_ERROR ) {
  909. if ( *valueNames == 0 ) {
  910. dword = 1;
  911. error = RegSetValueEx( newKey, FLAGS_VALUE, 0, REG_DWORD, (PBYTE)&dword, sizeof(DWORD) );
  912. }
  913. }
  914. if ( newKey != NULL ) {
  915. RegCloseKey( newKey );
  916. }
  917. //
  918. // Free the buffer allocated by SzToMultiSz.
  919. //
  920. if ( valueNames != NULL ) {
  921. MyFree( valueNames );
  922. }
  923. //
  924. // Restore the input value name string.
  925. //
  926. if ( splitPoint != NULL ) {
  927. *splitPoint = TEXT('\\');
  928. }
  929. return error;
  930. } // MakeDeleteValue
  931. DWORD
  932. MakeAddKey (
  933. IN PUSERRUN_CONTEXT Context,
  934. IN PWCH Name
  935. )
  936. /*++
  937. Routine Description:
  938. Adds entries to the UserRun hive for a new key.
  939. Arguments:
  940. Context - context value passed to WatchEnum.
  941. Name - name of new key (relative to HKEY_CURRENT_USER).
  942. Return Value:
  943. DWORD - Win32 status of the operation.
  944. --*/
  945. {
  946. WCHAR path[MAX_PATH + 1];
  947. //
  948. // Copy the key name into a large buffer and call AddKey to do the
  949. // recursive work.
  950. //
  951. wcscpy( path, Name );
  952. return AddKey( Context, path );
  953. } // MakeAddKey
  954. DWORD
  955. MakeDeleteKey (
  956. IN PUSERRUN_CONTEXT Context,
  957. IN PWCH Name
  958. )
  959. /*++
  960. Routine Description:
  961. Adds an entry to the UserRun hive for a deleted key.
  962. Arguments:
  963. Context - context value passed to WatchEnum.
  964. Name - name of deleted key (relative to HKEY_CURRENT_USER).
  965. Return Value:
  966. DWORD - Win32 status of the operation.
  967. --*/
  968. {
  969. HKEY newKey;
  970. DWORD error;
  971. DWORD dword;
  972. //
  973. // Create an entry key and popuplate it.
  974. //
  975. newKey = NULL;
  976. error = CreateUserRunKey( Context, FALSE, &newKey );
  977. if ( error == NO_ERROR ) {
  978. dword = 2;
  979. error = RegSetValueEx( newKey, ACTION_VALUE, 0, REG_DWORD, (PBYTE)&dword, sizeof(DWORD) );
  980. }
  981. if ( error == NO_ERROR ) {
  982. error = RegSetValueEx( newKey, KEYNAME_VALUE, 0, REG_SZ, (PBYTE)Name, SZLEN(Name) );
  983. }
  984. if ( newKey != NULL ) {
  985. RegCloseKey( newKey );
  986. }
  987. return error;
  988. } // MakeDeleteKey
  989. DWORD
  990. AddDirectory (
  991. IN PUSERRUN_CONTEXT Context,
  992. IN PWCH FullPath,
  993. IN PWCH Path
  994. )
  995. /*++
  996. Routine Description:
  997. Recursively adds entries to the UserRun hive for a new directory
  998. and its subtree.
  999. Arguments:
  1000. Context - context value passed to WatchEnum.
  1001. FullPath - full path to directory.
  1002. Path - path to directory relative to root of watched directory.
  1003. Return Value:
  1004. DWORD - Win32 status of the operation.
  1005. --*/
  1006. {
  1007. HKEY newKey;
  1008. DWORD error;
  1009. DWORD dword;
  1010. HANDLE findHandle;
  1011. WIN32_FIND_DATA fileData;
  1012. BOOL ok;
  1013. //
  1014. // Create an entry key for the directory and popuplate it.
  1015. //
  1016. newKey = NULL;
  1017. error = CreateUserRunKey( Context, TRUE, &newKey );
  1018. if ( error == NO_ERROR ) {
  1019. dword = 1;
  1020. error = RegSetValueEx( newKey, ACTION_VALUE, 0, REG_DWORD, (PBYTE)&dword, sizeof(DWORD) );
  1021. }
  1022. if ( error == NO_ERROR ) {
  1023. error = RegSetValueEx( newKey, ITEM_VALUE, 0, REG_SZ, (PBYTE)Path, SZLEN(Path) );
  1024. }
  1025. if ( newKey != NULL ) {
  1026. RegCloseKey( newKey );
  1027. }
  1028. if ( error == NO_ERROR ) {
  1029. //
  1030. // Search the directory and add file and directory entries.
  1031. //
  1032. wcscat( Path, TEXT("\\*") );
  1033. findHandle = FindFirstFile( FullPath, &fileData );
  1034. Path[wcslen(Path) - 2] = 0;
  1035. if ( findHandle != INVALID_HANDLE_VALUE ) {
  1036. do {
  1037. //
  1038. // Append the name of the current directory entry to the path.
  1039. //
  1040. wcscat( Path, TEXT("\\") );
  1041. wcscat( Path, fileData.cFileName );
  1042. //
  1043. // If the current entry is a file, add an entry in UserRun
  1044. // for it. If the current entry is a directory, call
  1045. // AddDirectory recursively to process it.
  1046. //
  1047. if ( FlagOff(fileData.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY) ) {
  1048. error = CreateUserRunSimpleFileKey( Context, 3, Path );
  1049. } else if ((wcscmp(fileData.cFileName,TEXT(".")) != 0) &&
  1050. (wcscmp(fileData.cFileName,TEXT("..")) != 0)) {
  1051. error = AddDirectory( Context, FullPath, Path );
  1052. }
  1053. *wcsrchr( Path, TEXT('\\') ) = 0;
  1054. if ( error == NO_ERROR ) {
  1055. ok = FindNextFile( findHandle, &fileData );
  1056. }
  1057. } while ( (error == NO_ERROR) && ok );
  1058. FindClose( findHandle );
  1059. } // findHandle != INVALID_HANDLE_VALUE
  1060. }
  1061. return error;
  1062. } // AddDirectory
  1063. DWORD
  1064. AddKey (
  1065. IN PUSERRUN_CONTEXT Context,
  1066. IN PWCH Path
  1067. )
  1068. /*++
  1069. Routine Description:
  1070. Recursively adds entries to the UserRun hive for a new key
  1071. and its subtree.
  1072. Arguments:
  1073. Context - context value passed to WatchEnum.
  1074. Path - path to key relative to HKEY_CURRENT_USER.
  1075. Return Value:
  1076. DWORD - Win32 status of the operation.
  1077. --*/
  1078. {
  1079. HKEY newKey;
  1080. DWORD error;
  1081. DWORD dword;
  1082. HKEY findHandle;
  1083. KEY_ENUM_CONTEXT enumContext;
  1084. //
  1085. // Create an entry key for the key and popuplate it.
  1086. //
  1087. newKey = NULL;
  1088. error = CreateUserRunKey( Context, FALSE, &newKey );
  1089. if ( error == NO_ERROR ) {
  1090. dword = 1;
  1091. error = RegSetValueEx( newKey, ACTION_VALUE, 0, REG_DWORD, (PBYTE)&dword, sizeof(DWORD) );
  1092. }
  1093. if ( error == NO_ERROR ) {
  1094. error = RegSetValueEx( newKey, KEYNAME_VALUE, 0, REG_SZ, (PBYTE)Path, SZLEN(Path) );
  1095. }
  1096. if ( newKey != NULL ) {
  1097. RegCloseKey( newKey );
  1098. }
  1099. if ( error == NO_ERROR ) {
  1100. //
  1101. // Search the key and add value and key entries.
  1102. //
  1103. findHandle = NULL;
  1104. error = RegOpenKeyEx( HKEY_CURRENT_USER,
  1105. Path,
  1106. 0,
  1107. KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS,
  1108. &findHandle );
  1109. if ( error == NO_ERROR ) {
  1110. //
  1111. // Enumerate the values and subkeys of the key, adding entries
  1112. // to the UserRun hive for each one.
  1113. //
  1114. enumContext.UserRunContext = Context;
  1115. enumContext.CurrentPath = Path;
  1116. error = EnumerateKey( findHandle,
  1117. &enumContext,
  1118. AddValueDuringAddKey,
  1119. AddKeyDuringAddKey );
  1120. RegCloseKey( findHandle );
  1121. }
  1122. }
  1123. return error;
  1124. } // AddKey
  1125. DWORD
  1126. AddValueDuringAddKey (
  1127. IN PVOID Context,
  1128. IN DWORD ValueNameLength,
  1129. IN PWCH ValueName,
  1130. IN DWORD ValueType,
  1131. IN PVOID ValueData,
  1132. IN DWORD ValueDataLength
  1133. )
  1134. /*++
  1135. Routine Description:
  1136. Adds a value entry to UserRun during AddKey.
  1137. Arguments:
  1138. Context - context value passed to EnumerateKey.
  1139. ValueNameLength - length in characters of ValueName.
  1140. ValueName - pointer to name of the value.
  1141. ValueType - type of the value data.
  1142. ValueData - pointer to value data.
  1143. ValueDataLength - length in bytes of ValueData.
  1144. Return Value:
  1145. DWORD - Win32 status of the operation.
  1146. --*/
  1147. {
  1148. PKEY_ENUM_CONTEXT context = Context;
  1149. //
  1150. // Add the value entry to UserRun.
  1151. //
  1152. return AddValue( context->UserRunContext,
  1153. context->CurrentPath,
  1154. ValueName,
  1155. ValueType,
  1156. ValueData,
  1157. ValueDataLength );
  1158. } // AddValueDuringAddKey
  1159. DWORD
  1160. AddKeyDuringAddKey (
  1161. IN PVOID Context,
  1162. IN DWORD KeyNameLength,
  1163. IN PWCH KeyName
  1164. )
  1165. /*++
  1166. Routine Description:
  1167. Adds a key entry to UserRun during AddKey.
  1168. Arguments:
  1169. Context - context value passed to EnumerateKey.
  1170. KeyNameLength - length in characters of KeyName.
  1171. KeyName - pointer to name of the key.
  1172. Return Value:
  1173. DWORD - Win32 status of the operation.
  1174. --*/
  1175. {
  1176. PKEY_ENUM_CONTEXT context = Context;
  1177. DWORD error;
  1178. //
  1179. // Append the key name to the path and call AddKey to do the
  1180. // recursive work.
  1181. //
  1182. wcscat( context->CurrentPath, TEXT("\\") );
  1183. wcscat( context->CurrentPath, KeyName );
  1184. error = AddKey( context->UserRunContext, context->CurrentPath );
  1185. //
  1186. // Remove the key name from the path.
  1187. //
  1188. *wcsrchr( context->CurrentPath, TEXT('\\') ) = 0;
  1189. return error;
  1190. } // AddKeyDuringAddKey
  1191. DWORD
  1192. AddValue (
  1193. IN PUSERRUN_CONTEXT Context,
  1194. IN PWCH KeyName OPTIONAL,
  1195. IN PWCH ValueName,
  1196. IN DWORD ValueType,
  1197. IN PVOID ValueData,
  1198. IN DWORD ValueDataLength
  1199. )
  1200. /*++
  1201. Routine Description:
  1202. Adds an entry for a new value to UserRun.
  1203. Arguments:
  1204. Context - pointer to context record.
  1205. KeyName - pointer to name of the key containing the value.
  1206. ValueName - pointer to name of the value.
  1207. ValueType - type of the value data.
  1208. ValueData - pointer to value data.
  1209. ValueDataLength - length in bytes of ValueData.
  1210. Return Value:
  1211. DWORD - Win32 status of the operation.
  1212. --*/
  1213. {
  1214. HKEY newKey;
  1215. DWORD error;
  1216. DWORD dword;
  1217. //
  1218. // Create an entry key for the value and popuplate it.
  1219. //
  1220. newKey = NULL;
  1221. error = CreateUserRunKey( Context, FALSE, &newKey );
  1222. if ( error == NO_ERROR ) {
  1223. dword = 3;
  1224. error = RegSetValueEx( newKey, ACTION_VALUE, 0, REG_DWORD, (PBYTE)&dword, sizeof(DWORD) );
  1225. }
  1226. if ( error == NO_ERROR ) {
  1227. error = RegSetValueEx( newKey, KEYNAME_VALUE, 0, REG_SZ, (PBYTE)KeyName, SZLEN(KeyName) );
  1228. }
  1229. if ( error == NO_ERROR ) {
  1230. error = RegSetValueEx( newKey, VALUENAME_VALUE, 0, REG_SZ, (PBYTE)ValueName, SZLEN(ValueName) );
  1231. }
  1232. if ( error == NO_ERROR ) {
  1233. error = RegSetValueEx( newKey, VALUE_VALUE, 0, ValueType, (PBYTE)ValueData, ValueDataLength );
  1234. }
  1235. if ( newKey != NULL ) {
  1236. RegCloseKey( newKey );
  1237. }
  1238. return error;
  1239. } // AddValue
  1240. DWORD
  1241. CreateUserRunSimpleFileKey (
  1242. IN PUSERRUN_CONTEXT Context,
  1243. IN DWORD Action,
  1244. IN PWCH Name
  1245. )
  1246. /*++
  1247. Routine Description:
  1248. Creates an entry under the Files key for the "simple" cases -- delete
  1249. directory, add file, delete file.
  1250. Arguments:
  1251. Context - pointer to context record.
  1252. Action - value to store in Action value of entry.
  1253. Name - pointer to name of the file or directory.
  1254. Return Value:
  1255. DWORD - Win32 status of the operation.
  1256. --*/
  1257. {
  1258. HKEY newKey;
  1259. DWORD error;
  1260. //
  1261. // Create an entry key and popuplate it.
  1262. //
  1263. newKey = NULL;
  1264. error = CreateUserRunKey( Context, TRUE, &newKey );
  1265. if ( error == NO_ERROR ) {
  1266. error = RegSetValueEx( newKey, ACTION_VALUE, 0, REG_DWORD, (PBYTE)&Action, sizeof(DWORD) );
  1267. }
  1268. if ( error == NO_ERROR ) {
  1269. error = RegSetValueEx( newKey, ITEM_VALUE, 0, REG_SZ, (PBYTE)Name, SZLEN(Name) );
  1270. }
  1271. if ( newKey != NULL ) {
  1272. RegCloseKey( newKey );
  1273. }
  1274. return error;
  1275. } // CreateUserRunSimpleFileKey
  1276. DWORD
  1277. CreateUserRunKey (
  1278. IN PUSERRUN_CONTEXT Context,
  1279. IN BOOL IsFileKey,
  1280. OUT PHKEY NewKeyHandle
  1281. )
  1282. /*++
  1283. Routine Description:
  1284. Creates an indexed key in UserRun, under either the Files key or the
  1285. Hive key.
  1286. Arguments:
  1287. Context - pointer to context record.
  1288. IsFileKey - indicates whether to create the key under Files or Hive.
  1289. NewKeyHandle - returns the handle to the new key.
  1290. Return Value:
  1291. DWORD - Win32 status of the operation.
  1292. --*/
  1293. {
  1294. HKEY parentKeyHandle;
  1295. DWORD index;
  1296. WCHAR keyName[11];
  1297. DWORD disposition;
  1298. //
  1299. // Get the handle to the parent key and the index for this entry.
  1300. //
  1301. if ( IsFileKey ) {
  1302. parentKeyHandle = Context->FilesKey;
  1303. index = ++Context->FilesIndex;
  1304. } else {
  1305. parentKeyHandle = Context->HiveKey;
  1306. index = ++Context->HiveIndex;
  1307. }
  1308. //
  1309. // Convert the index number into a string.
  1310. //
  1311. wsprintf( keyName, TEXT("%d"), index );
  1312. //
  1313. // Create the entry key.
  1314. //
  1315. return RegCreateKeyEx( parentKeyHandle,
  1316. keyName,
  1317. 0,
  1318. NULL,
  1319. REG_OPTION_NON_VOLATILE,
  1320. KEY_ALL_ACCESS,
  1321. NULL,
  1322. NewKeyHandle,
  1323. &disposition );
  1324. } // CreateUserRunKey
  1325. DWORD
  1326. QueryValue (
  1327. IN PWCH KeyName OPTIONAL,
  1328. IN PWCH ValueName,
  1329. OUT PDWORD ValueType,
  1330. OUT PVOID *ValueData,
  1331. OUT PDWORD ValueDataLength
  1332. )
  1333. /*++
  1334. Routine Description:
  1335. Queries the data for a value.
  1336. Arguments:
  1337. KeyName - pointer to name of the key containing the value.
  1338. ValueName - pointer to name of the value.
  1339. ValueType - returns the type of the value data.
  1340. ValueData - returns a pointer to value data. This buffer must be
  1341. freed by the caller using MyFree.
  1342. ValueDataLength - length in bytes of ValueData.
  1343. Return Value:
  1344. DWORD - Win32 status of the operation.
  1345. --*/
  1346. {
  1347. HKEY hkey;
  1348. DWORD disposition;
  1349. DWORD error;
  1350. //
  1351. // Open the parent key.
  1352. //
  1353. if ( (KeyName == NULL) || (wcslen(KeyName) == 0) ) {
  1354. hkey = HKEY_CURRENT_USER;
  1355. } else {
  1356. error = RegCreateKeyEx( HKEY_CURRENT_USER,
  1357. KeyName,
  1358. 0,
  1359. NULL,
  1360. REG_OPTION_NON_VOLATILE,
  1361. KEY_ALL_ACCESS,
  1362. NULL,
  1363. &hkey,
  1364. &disposition );
  1365. if ( error != ERROR_SUCCESS ) {
  1366. return error;
  1367. }
  1368. }
  1369. //
  1370. // Query the value to get the length of its data.
  1371. //
  1372. *ValueDataLength = 0;
  1373. *ValueData = NULL;
  1374. error = RegQueryValueEx( hkey,
  1375. ValueName,
  1376. NULL,
  1377. ValueType,
  1378. NULL,
  1379. ValueDataLength );
  1380. //
  1381. // Allocate a buffer to hold the value data.
  1382. //
  1383. if ( error == NO_ERROR ) {
  1384. *ValueData = MyMalloc( *ValueDataLength );
  1385. if ( *ValueData == NULL ) {
  1386. error = ERROR_NOT_ENOUGH_MEMORY;
  1387. }
  1388. }
  1389. //
  1390. // Query the value again, this time retrieving the data.
  1391. //
  1392. if ( error == NO_ERROR ) {
  1393. error = RegQueryValueEx( hkey,
  1394. ValueName,
  1395. NULL,
  1396. ValueType,
  1397. *ValueData,
  1398. ValueDataLength );
  1399. if ( error != NO_ERROR ) {
  1400. MyFree( *ValueData );
  1401. }
  1402. }
  1403. //
  1404. // Close the parent key.
  1405. //
  1406. if ( hkey != HKEY_CURRENT_USER ) {
  1407. RegCloseKey( hkey );
  1408. }
  1409. return error;
  1410. }
  1411. VOID
  1412. SzToMultiSz (
  1413. IN PWCH Sz,
  1414. OUT PWCH *MultiSz,
  1415. OUT PDWORD MultiSzLength
  1416. )
  1417. /*++
  1418. Routine Description:
  1419. Creates a MULTI_SZ version of a null-terminated string. Allocates
  1420. a buffer, copies the string to the buffer, and appends an additional
  1421. null terminator.
  1422. Arguments:
  1423. Sz - pointer to the string that is to be copied.
  1424. MultiSz - returns a pointer to the MULTI_SZ version of Sz. The caller
  1425. must free this buffer using MyFree. If the allocation fails,
  1426. MultiSz will be NULL.
  1427. MultiSzLength - returns the length in bytes of MultiSz, including the
  1428. null terminators.
  1429. Return Value:
  1430. None.
  1431. --*/
  1432. {
  1433. DWORD szlen;
  1434. //
  1435. // Get the length of the input string and calculate the MULTI_SZ length.
  1436. //
  1437. szlen = wcslen(Sz);
  1438. *MultiSzLength = (szlen + 1 + 1) * sizeof(WCHAR);
  1439. //
  1440. // Allocate the MULTI_SZ buffer, copy the input string, and append
  1441. // an additional null.
  1442. //
  1443. *MultiSz = MyMalloc( *MultiSzLength );
  1444. if ( *MultiSz != NULL ) {
  1445. wcscpy( *MultiSz, Sz );
  1446. (*MultiSz)[szlen+1] = 0;
  1447. }
  1448. return;
  1449. }