Leaked source code of windows server 2003
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.

1603 lines
56 KiB

  1. /*++
  2. Copyright (c) Microsoft Corporation. All rights reserved.
  3. Module Name:
  4. restore.c
  5. Abstract:
  6. This module contains the following Plug and Play registry merge-restore
  7. routines:
  8. AsrRestorePlugPlayRegistryData
  9. Author:
  10. Jim Cavalaris (jamesca) 3-07-2000
  11. Environment:
  12. User-mode only.
  13. Revision History:
  14. 07-March-2000 jamesca
  15. Creation and initial implementation.
  16. --*/
  17. //
  18. // includes
  19. //
  20. #include "precomp.h"
  21. #include "util.h"
  22. #include "debug.h"
  23. #include <regstr.h>
  24. #include <cfgmgr32.h>
  25. //
  26. // private memory allocation definitions
  27. //
  28. #define MyMalloc(size) LocalAlloc(0, size);
  29. #define MyFree(entry) LocalFree(entry);
  30. #define MyRealloc(entry,size) LocalReAlloc(entry, size, 0);
  31. //
  32. // global variables to store the root keys we're working on.
  33. //
  34. // (we really shouldn't have to do this, but it's the easiest way for us to
  35. // reach into one set of keys while recursing through the other set.)
  36. //
  37. HKEY PnpSifRestoreSourceEnumKeyHandle = NULL;
  38. HKEY PnpSifRestoreTargetEnumKeyHandle = NULL;
  39. HKEY PnpSifRestoreSourceClassKeyHandle = NULL;
  40. HKEY PnpSifRestoreTargetClassKeyHandle = NULL;
  41. //
  42. // private definitions
  43. //
  44. // INVALID_FLAGS macro from pnp\inc\cfgmgrp.h
  45. #define INVALID_FLAGS(ulFlags, ulAllowed) ((ulFlags) & ~(ulAllowed))
  46. // private flags for internal RestoreSpecialRegistryData routine
  47. #define PNPSIF_RESTORE_TYPE_DEFAULT (0x00000000)
  48. #define PNPSIF_RESTORE_TYPE_ENUM (0x00000001)
  49. #define PNPSIF_RESTORE_TYPE_CLASS (0x00000002)
  50. #define PNPSIF_RESTORE_REPLACE_TARGET_VALUES_ON_COLLISION (0x00000010)
  51. #define PNPSIF_RESTORE_BITS (0x00000013)
  52. // Enum level definitions describing the subkey types for the specified handles.
  53. #define ENUM_LEVEL_ENUMERATORS (0x0000003)
  54. #define ENUM_LEVEL_DEVICES (0x0000002)
  55. #define ENUM_LEVEL_INSTANCES (0x0000001)
  56. // Class level definitions describing the subkey types for the specified handles.
  57. #define CLASS_LEVEL_CLASSGUIDS (0x0000002)
  58. #define CLASS_LEVEL_DRVINSTS (0x0000001)
  59. //
  60. // private prototypes
  61. //
  62. BOOL
  63. RestoreEnumKey(
  64. IN HKEY hSourceEnumKey,
  65. IN HKEY hTargetEnumKey
  66. );
  67. BOOL
  68. RestoreClassKey(
  69. IN HKEY hSourceClassKey,
  70. IN HKEY hTargetClassKey
  71. );
  72. BOOL
  73. RestoreSpecialRegistryData(
  74. IN HKEY hSourceKey,
  75. IN HKEY hTargetKey,
  76. IN ULONG ulLevel,
  77. IN OUT PVOID pContext,
  78. IN ULONG ulFlags
  79. );
  80. BOOL
  81. MyReplaceKey(
  82. IN HKEY hSourceKey,
  83. IN HKEY hTargetKey
  84. );
  85. BOOL
  86. IsString4DigitOrdinal(
  87. IN LPTSTR pszSubkeyName
  88. );
  89. BOOL
  90. IsDeviceConfigured(
  91. IN HKEY hInstanceKey
  92. );
  93. BOOL
  94. ReplaceClassKeyForInstance(
  95. IN HKEY hSourceInstanceKey,
  96. IN HKEY hSourceRootClassKey,
  97. IN HKEY hTargetRootClassKey
  98. );
  99. //
  100. // routines
  101. //
  102. BOOL
  103. AsrRestorePlugPlayRegistryData(
  104. IN HKEY SourceSystemKey,
  105. IN HKEY TargetSystemKey,
  106. IN DWORD Flags,
  107. IN PVOID Reserved
  108. )
  109. /*++
  110. Routine Description:
  111. This routine restores plug and play data from the the specified source
  112. SYSTEM key to the specified target SYSTEM key, merging intermediate subkeys
  113. and values as appropriate.
  114. Arguments:
  115. SourceSystemKey - Handle to the HKLM\SYSTEM key within the "source"
  116. registry, whose data is to be "merged" into the
  117. corresponding SYSTEM key of the "target" registry, as
  118. specified by TargetSystemKey.
  119. TargetSystemKey - Handle to the HKLM\SYSTEM key within the "target"
  120. registry, that is to receive additional data from the
  121. corresponding SYSTEM key of the "source" registry, as
  122. specified by SourceSystemKey.
  123. Flags - Not used, must be zero.
  124. Reserved - Reserved for future use, must be NULL.
  125. Return Value:
  126. TRUE if successful, FALSE otherwise. Upon failure, additional information
  127. can be retrieved by calling GetLastError().
  128. Notes:
  129. This routine was written specifically to assist in the restoration of the
  130. Plug-and-Play specific registry keys that cannot simply be restored from
  131. backup. It is intended to be called within the context of a
  132. backup-and-restore application's "restore" phase.
  133. During a backup-and-restore application's "restore" phase, the registry that
  134. has been backed-up onto backup medium is to become the new system registry.
  135. Certain Plug and Play values and registry keys, are actually copied into the
  136. backed-up registry from the current registry. Rather than using the backup
  137. registry, or the current registry exclusively, Plug and Play data contained
  138. in these keys should be merged from the current registry into the backup
  139. registry in a way that is appropriate for each key. The backup registry can
  140. then safely replace the current registry as the system registry.
  141. In the context of a backup-and-restore application's "restore" phase, the
  142. "Source" registry key is the one contained in the current running system
  143. registry. The "Target" registry is that which has been backed-up, and will
  144. become the system registry, upon reboot.
  145. The calling thread/process must have both the SE_BACKUP_NAME and
  146. SE_RESTORE_NAME privileges.
  147. --*/
  148. {
  149. LONG result = ERROR_SUCCESS;
  150. HRESULT hr;
  151. HKEY hSystemSelectKey = NULL;
  152. TCHAR pszRegKeySelect[] = TEXT("Select");
  153. TCHAR pszRegValueCurrent[] = TEXT("Current");
  154. TCHAR szBuffer[128];
  155. DWORD dwType, dwSize;
  156. DWORD dwSourceCCS, dwTargetCCS;
  157. //
  158. // Make sure the user didn't pass us anything in the Reserved parameter.
  159. //
  160. if (Reserved) {
  161. SetLastError(ERROR_INVALID_PARAMETER);
  162. return FALSE;
  163. }
  164. //
  165. // Make sure that the user supplied us with valid flags.
  166. //
  167. if(INVALID_FLAGS(Flags, 0x0)) {
  168. SetLastError(ERROR_INVALID_FLAGS);
  169. return FALSE;
  170. }
  171. //
  172. // Determine the CurrentControlSet for the source.
  173. //
  174. result = RegOpenKeyEx(SourceSystemKey,
  175. pszRegKeySelect,
  176. 0,
  177. KEY_READ,
  178. &hSystemSelectKey);
  179. if (result != ERROR_SUCCESS) {
  180. hSystemSelectKey = NULL;
  181. goto Clean0;
  182. }
  183. dwSize = sizeof(DWORD);
  184. result = RegQueryValueEx(hSystemSelectKey,
  185. pszRegValueCurrent,
  186. 0,
  187. &dwType,
  188. (LPBYTE)&dwSourceCCS,
  189. &dwSize);
  190. RegCloseKey(hSystemSelectKey);
  191. hSystemSelectKey = NULL;
  192. if ((result != ERROR_SUCCESS) ||
  193. (dwType != REG_DWORD)) {
  194. goto Clean0;
  195. }
  196. //
  197. // Determine the CurrentControlSet for the target.
  198. //
  199. result = RegOpenKeyEx(TargetSystemKey,
  200. pszRegKeySelect,
  201. 0,
  202. KEY_READ,
  203. &hSystemSelectKey);
  204. if (result != ERROR_SUCCESS) {
  205. hSystemSelectKey = NULL;
  206. goto Clean0;
  207. }
  208. dwSize = sizeof(DWORD);
  209. result = RegQueryValueEx(hSystemSelectKey,
  210. pszRegValueCurrent,
  211. 0,
  212. &dwType,
  213. (LPBYTE)&dwTargetCCS,
  214. &dwSize);
  215. RegCloseKey(hSystemSelectKey);
  216. hSystemSelectKey = NULL;
  217. if ((result != ERROR_SUCCESS) ||
  218. (dwType != REG_DWORD)) {
  219. goto Clean0;
  220. }
  221. //
  222. // Open the source CurrentControlSet\Enum key.
  223. //
  224. hr = StringCchPrintf(szBuffer,
  225. SIZECHARS(szBuffer),
  226. TEXT("ControlSet%03d\\Enum"),
  227. dwSourceCCS);
  228. if (FAILED(hr)) {
  229. result = HRESULT_CODE(hr);
  230. goto Clean0;
  231. }
  232. result = RegOpenKeyEx(SourceSystemKey,
  233. szBuffer,
  234. 0,
  235. KEY_READ, // only need to read from the source
  236. &PnpSifRestoreSourceEnumKeyHandle);
  237. if (result != ERROR_SUCCESS) {
  238. goto Clean0;
  239. }
  240. //
  241. // Open the target CurrentControlSet\Enum key.
  242. //
  243. hr = StringCchPrintf(szBuffer,
  244. SIZECHARS(szBuffer),
  245. TEXT("ControlSet%03d\\Enum"),
  246. dwTargetCCS);
  247. if (FAILED(hr)) {
  248. result = HRESULT_CODE(hr);
  249. goto Clean0;
  250. }
  251. result = RegOpenKeyEx(TargetSystemKey,
  252. szBuffer,
  253. 0,
  254. KEY_ALL_ACCESS, // need full access to the target
  255. &PnpSifRestoreTargetEnumKeyHandle);
  256. if (result != ERROR_SUCCESS) {
  257. goto Clean0;
  258. }
  259. //
  260. // Open the source CurrentControlSet\Control\Class key.
  261. //
  262. hr = StringCchPrintf(szBuffer,
  263. SIZECHARS(szBuffer),
  264. TEXT("ControlSet%03d\\Control\\Class"),
  265. dwSourceCCS);
  266. if (FAILED(hr)) {
  267. result = HRESULT_CODE(hr);
  268. goto Clean0;
  269. }
  270. result = RegOpenKeyEx(SourceSystemKey,
  271. szBuffer,
  272. 0,
  273. KEY_READ, // only need to read from the source
  274. &PnpSifRestoreSourceClassKeyHandle);
  275. if (result != ERROR_SUCCESS) {
  276. goto Clean0;
  277. }
  278. //
  279. // Open the target CurrentControlSet\Control\Class key.
  280. //
  281. hr = StringCchPrintf(szBuffer,
  282. SIZECHARS(szBuffer),
  283. TEXT("ControlSet%03d\\Control\\Class"),
  284. dwTargetCCS);
  285. if (FAILED(hr)) {
  286. result = HRESULT_CODE(hr);
  287. goto Clean0;
  288. }
  289. result = RegOpenKeyEx(TargetSystemKey,
  290. szBuffer,
  291. 0,
  292. KEY_ALL_ACCESS, // need full access to the target
  293. &PnpSifRestoreTargetClassKeyHandle);
  294. if (result != ERROR_SUCCESS) {
  295. goto Clean0;
  296. }
  297. //
  298. // NOTE: We restore the Enum branch first, then restore the Class branch.
  299. // The code that restores these keys depends on it, so do not change this!!
  300. //
  301. // This order makes sense, because relevant Class keys correspond to exactly
  302. // one Enum instance key (but an Enum key may or may not have a Class key),
  303. // so Class keys are only really meaningful in the context of the instance
  304. // key they belong to. This behavior should NOT need to change.
  305. //
  306. //
  307. // Do the merge-restore for the Enum keys, ignore any errors.
  308. //
  309. if (!RestoreEnumKey(PnpSifRestoreSourceEnumKeyHandle,
  310. PnpSifRestoreTargetEnumKeyHandle)) {
  311. DBGTRACE((DBGF_ERRORS,
  312. TEXT("PNPSIF: RestoreEnumKey failed, ")
  313. TEXT("error == 0x%08lx\n"),
  314. GetLastError()));
  315. }
  316. //
  317. // Do the merge-restore for the Class keys, ignore any errors.
  318. //
  319. if (!RestoreClassKey(PnpSifRestoreSourceClassKeyHandle,
  320. PnpSifRestoreTargetClassKeyHandle)) {
  321. DBGTRACE((DBGF_ERRORS,
  322. TEXT("PNPSIF: RestoreClassKey failed, ")
  323. TEXT("error == 0x%08lx\n"),
  324. GetLastError()));
  325. }
  326. Clean0:
  327. //
  328. // Close the open handles.
  329. //
  330. if (PnpSifRestoreSourceEnumKeyHandle) {
  331. RegCloseKey(PnpSifRestoreSourceEnumKeyHandle);
  332. PnpSifRestoreSourceEnumKeyHandle = NULL;
  333. }
  334. if (PnpSifRestoreTargetEnumKeyHandle) {
  335. RegCloseKey(PnpSifRestoreTargetEnumKeyHandle);
  336. PnpSifRestoreTargetEnumKeyHandle = NULL;
  337. }
  338. if (PnpSifRestoreSourceClassKeyHandle) {
  339. RegCloseKey(PnpSifRestoreSourceClassKeyHandle);
  340. PnpSifRestoreSourceClassKeyHandle = NULL;
  341. }
  342. if (PnpSifRestoreTargetClassKeyHandle) {
  343. RegCloseKey(PnpSifRestoreTargetClassKeyHandle);
  344. PnpSifRestoreTargetClassKeyHandle = NULL;
  345. }
  346. if (result != ERROR_SUCCESS) {
  347. SetLastError(result);
  348. }
  349. return (result == ERROR_SUCCESS);
  350. } // AsrRestorePlugPlayRegistryData()
  351. //
  352. // private worker routines for AsrRestorePlugPlayRegistryData
  353. //
  354. BOOL
  355. RestoreEnumKey(
  356. IN HKEY hSourceEnumKey,
  357. IN HKEY hTargetEnumKey
  358. )
  359. /*++
  360. Routine Description:
  361. Restores new device instances in the source (current) Enum key to the target
  362. Enum key, located in the backup registry to be restored. By doing this, the
  363. Enum key from the backup set can safely replace the current Enum key.
  364. All intermediate values from the source (current) registry are restored to
  365. the target (backup), to account for the device instance hash values located
  366. at the root of the Enum key, that have been updated during setup.
  367. During an ASR backup and restore operation, the hash values in the source
  368. (current) registry are propogated to the current registry during textmode
  369. setup, as they are stored the asrpnp.sif file. Since hash values may be
  370. modified by setup, the values in the source (current) registry will be more
  371. relevant than those in the target (backup), so source values should always
  372. be preseverd.
  373. Arguments:
  374. hSourceEnumKey - Handle to the HKLM\SYSTEM\CurrentControlSet\Enum key within
  375. the "source" registry, whose data is to be "merged" into
  376. the corresponding SYSTEM key of the "target" registry, as
  377. specified by hTargetEnumKey.
  378. hTargetEnumKey - Handle to the HKLM\SYSTEM\CurrentControlSet\Enum key within
  379. the "target" registry, that is to receive additional data
  380. from the corresponding SYSTEM key of the "source" registry,
  381. as specified by hSourceEnumKey.
  382. Return Value:
  383. TRUE if successful, FALSE otherwise. Upon failure, additional information
  384. can be retrieved by calling GetLastError().
  385. --*/
  386. {
  387. BOOL bIsRootEnumerated = FALSE;
  388. return RestoreSpecialRegistryData(hSourceEnumKey,
  389. hTargetEnumKey,
  390. ENUM_LEVEL_ENUMERATORS, // (0x0000003)
  391. (PVOID)&bIsRootEnumerated,
  392. PNPSIF_RESTORE_TYPE_ENUM |
  393. PNPSIF_RESTORE_REPLACE_TARGET_VALUES_ON_COLLISION
  394. );
  395. } // RestoreEnumKey
  396. BOOL
  397. RestoreClassKey(
  398. IN HKEY hSourceClassKey,
  399. IN HKEY hTargetClassKey
  400. )
  401. /*++
  402. Routine Description:
  403. Restores new elements of the source Class key to the target Class key,
  404. located in the backup registry to be restored.
  405. Intermediate values from the source registry are coied to the the target
  406. registry only when they do not already exist in the target. Otherwise, the
  407. target values are preseved.
  408. Arguments:
  409. hSourceClassKey - Handle to the HKLM\SYSTEM\CurrentControlSet\Control\Class
  410. key within the "source" registry, whose data is to be
  411. "merged" into the corresponding SYSTEM key of the "target"
  412. registry, as specified by hTargetClassKey.
  413. hTargetClassKey - Handle to the HKLM\SYSTEM\CurrentControlSet\Control\Class
  414. key within the "target" registry, that is to receive
  415. additional data from the corresponding SYSTEM key of the
  416. "source" registry, as specified by hSourceClassKey.
  417. Return Value:
  418. TRUE if successful, FALSE otherwise. Upon failure, additional information
  419. can be retrieved by calling GetLastError().
  420. --*/
  421. {
  422. return RestoreSpecialRegistryData(hSourceClassKey,
  423. hTargetClassKey,
  424. CLASS_LEVEL_CLASSGUIDS, // (0x00000002)
  425. (PVOID)NULL,
  426. PNPSIF_RESTORE_TYPE_CLASS
  427. );
  428. } // RestoreClassKey
  429. BOOL
  430. RestoreSpecialRegistryData(
  431. IN HKEY hSourceKey,
  432. IN HKEY hTargetKey,
  433. IN ULONG ulLevel,
  434. IN OUT PVOID pContext,
  435. IN ULONG ulFlags
  436. )
  437. /*++
  438. Routine Description:
  439. This routine restores the specified source key to the specified target key,
  440. merging intermediate subkeys and values. Values in the subkeys located
  441. above the specified depth level are merged from the source into the target
  442. (with collisions handled according to the specified flags). Subkeys and
  443. values at and below the level specified are merged from the source key to
  444. the target key, with subkeys from the sources replacing any corresponding
  445. subkeys in the target.
  446. Arguments:
  447. hSourceKey - Handle to a key within the source registry, whose data is to be
  448. "merged" into the corresponding key of the target registry, as
  449. specified by hTargetKey.
  450. hTargetKey - Handle to a key within the target registry, that is to receive
  451. data from the corresponding key of the source registry, as
  452. specified by hSourceKey.
  453. ulLevel - Specifies the subkey depth at which "replacement" will
  454. take place. For subkeys above that depth, data in the target
  455. registry will be preserved if present, and copied otherwise.
  456. For keys at the specified level, data from the specified target
  457. key will be replaced by the source key.
  458. pContext - Specifies caller-supplied context for the operation that is
  459. specific to the type of subkeys being restored (see ulFlags
  460. below), and the specified ulLevel parameter.
  461. ulFlags - Supplies flags specifying options for the restoration of the
  462. registry keys. May be one of the following values:
  463. PNPSIF_RESTORE_TYPE_ENUM:
  464. Specifies that the subkeys being restored are subkeys of
  465. the SYSTEM\CurrentControlSet\Enum branch. Device hardware
  466. settings can be inspected at the appropriate ulLevel.
  467. For Enum branch subkeys, the ulLevel parameter describes
  468. also the type of the subkeys contained under the hSourceKey
  469. and hTargetKey keys. May be one of:
  470. ENUM_LEVEL_ENUMERATORS
  471. ENUM_LEVEL_DEVICES
  472. ENUM_LEVEL_INSTANCES
  473. PNPSIF_RESTORE_TYPE_CLASS:
  474. Specifies that the subkeys being restored are subkeys of
  475. the SYSTEM\CurrentControlSet\Control\Class branch. Setup
  476. class or device software settings can be inspected at the
  477. appropriate ulLevel.
  478. For Class branch subkeys, the ulLevel parameter also
  479. describes the type of the subkeys contained under the
  480. hSourceKey and hTargetKey keys. May be one of:
  481. CLASS_LEVEL_CLASSGUIDS
  482. CLASS_LEVEL_DRVINSTS
  483. PNPSIF_RESTORE_REPLACE_TARGET_VALUES_ON_COLLISION:
  484. When a collision occurs while merging values between the
  485. source and target at intermediate levels, replace the
  486. target value with that from the source (the default
  487. behavior is to preserve existing target values above
  488. 'ulLevel' in depth from the supplied keys; below 'ulLevel',
  489. only source key values will be present.)
  490. NOTE: it's probably worth noting that a corresponding flag for
  491. replace-target-keys-on-collision is not at all necessary, since
  492. such a behavior can already be implemented with the ulLevel
  493. parameter. (i.e. the ulLevel parameter actually specifies the
  494. level at which all source keys will replace target keys; to
  495. specify that this should ALWAYS be done is the same as
  496. specifying ulLevel == 0)
  497. Return Value:
  498. TRUE if successful, FALSE otherwise. Upon failure, additional information
  499. can be retrieved by calling GetLastError().
  500. Notes:
  501. This routine was written specifically to assist in the restoration of the
  502. Plug-and-Play specific registry keys that cannot simply be restored from
  503. backup. It is intended to be called within the context of a
  504. backup-and-restore application's "restore" phase.
  505. During a backup-and-restore application's "restore" phase, the registry that
  506. has been backed-up onto backup medium is to become the new system registry.
  507. Certain Plug and Play values and registry keys, are actually copied into the
  508. backed-up registry from the current registry. Rather than using the backup
  509. registry, or the current registry exclusively, Plug and Play data contained
  510. in these keys should be merged from the current registry into the backup
  511. registry in a way that is appropriate for each key. The backup registry can
  512. then safely replace the current registry as the system registry.
  513. In the context of a backup-and-restore application's "restore" phase, the
  514. "Source" registry key is the one contained in the current running system
  515. registry. The "Target" registry is that which has been backed-up, and will
  516. become the system registry, upon reboot.
  517. The different restore behaviors required for each of the different sets of
  518. keys can be specified with the appropriate ulLevel, and ulFlags.
  519. --*/
  520. {
  521. LONG result = ERROR_SUCCESS;
  522. DWORD dwIndex = 0;
  523. DWORD dwSubkeyCount, dwMaxSubkeyNameLength;
  524. DWORD dwValueCount, dwMaxValueNameLength, dwMaxValueDataLength;
  525. DWORD dwDisposition;
  526. LPTSTR pszSubkeyName = NULL, pszValueName = NULL;
  527. LPBYTE pValueData = NULL;
  528. BOOL bPossibleRedundantInstanceKeys = FALSE;
  529. //
  530. // Validate parameters
  531. //
  532. if (INVALID_FLAGS(ulFlags, PNPSIF_RESTORE_BITS)) {
  533. SetLastError(ERROR_INVALID_FLAGS);
  534. return FALSE;
  535. }
  536. if ((hTargetKey == NULL) ||
  537. (hTargetKey == INVALID_HANDLE_VALUE) ||
  538. (hSourceKey == NULL) ||
  539. (hSourceKey == INVALID_HANDLE_VALUE)) {
  540. SetLastError(ERROR_INVALID_PARAMETER);
  541. return FALSE;
  542. }
  543. if (ulLevel == 0) {
  544. //
  545. // This is the level at which we should stop merging, so just replace
  546. // hTargetKey with hSourceKey, and we're done.
  547. //
  548. return MyReplaceKey(hSourceKey, hTargetKey);
  549. } else {
  550. //
  551. // At levels above the replacement level, perform a non-destructive
  552. // merge of intermediate subkeys and values; that is, only copy keys and
  553. // values from the source to the target if they do not already exist at
  554. // the target. Otherwise, leave the target keys and values alone, and
  555. // keep traversing subkeys as necessary.
  556. //
  557. //
  558. // Find out information about the hSourceKey values and subkeys.
  559. //
  560. result = RegQueryInfoKey(hSourceKey,
  561. NULL,
  562. NULL,
  563. NULL,
  564. &dwSubkeyCount,
  565. &dwMaxSubkeyNameLength,
  566. NULL,
  567. &dwValueCount,
  568. &dwMaxValueNameLength,
  569. &dwMaxValueDataLength,
  570. NULL,
  571. NULL);
  572. if (result != ERROR_SUCCESS) {
  573. DBGTRACE((DBGF_ERRORS,
  574. TEXT("PNPSIF: RegQueryInfoKey failed, ")
  575. TEXT("error == 0x%08lx\n"),
  576. result));
  577. goto Clean0;
  578. }
  579. //
  580. // Allocate space to fold the largest hSourceKey subkey, value and data.
  581. //
  582. dwMaxSubkeyNameLength++;
  583. pszSubkeyName = MyMalloc(dwMaxSubkeyNameLength * sizeof(TCHAR));
  584. if (pszSubkeyName == NULL) {
  585. result = ERROR_NOT_ENOUGH_MEMORY;
  586. DBGTRACE((DBGF_ERRORS,
  587. TEXT("PNPSIF: MyMalloc failed allocating subkey name string, ")
  588. TEXT("error == 0x%08lx\n"),
  589. result));
  590. goto Clean0;
  591. }
  592. dwMaxValueNameLength++;
  593. pszValueName = MyMalloc(dwMaxValueNameLength * sizeof(TCHAR));
  594. if (pszValueName == NULL) {
  595. result = ERROR_NOT_ENOUGH_MEMORY;
  596. DBGTRACE((DBGF_ERRORS,
  597. TEXT("PNPSIF: MyMalloc failed allocating value name string, ")
  598. TEXT("error == 0x%08lx\n"),
  599. result));
  600. goto Clean0;
  601. }
  602. pValueData = MyMalloc(dwMaxValueDataLength * sizeof(TCHAR));
  603. if (pValueData == NULL) {
  604. result = ERROR_NOT_ENOUGH_MEMORY;
  605. DBGTRACE((DBGF_ERRORS,
  606. TEXT("PNPSIF: MyMalloc failed allocating value data buffer, ")
  607. TEXT("error == 0x%08lx\n"),
  608. result));
  609. goto Clean0;
  610. }
  611. //
  612. // Enumerate all hSourceKey values.
  613. //
  614. for (dwIndex = 0; dwIndex < dwValueCount; dwIndex++) {
  615. DWORD dwValueNameLength = dwMaxValueNameLength;
  616. DWORD dwValueDataLength = dwMaxValueDataLength;
  617. DWORD dwType;
  618. result = RegEnumValue(hSourceKey,
  619. dwIndex,
  620. pszValueName,
  621. &dwValueNameLength,
  622. 0,
  623. &dwType,
  624. pValueData,
  625. &dwValueDataLength);
  626. if (result != ERROR_SUCCESS) {
  627. //
  628. // Error enumerating values - whatchya gonna do?
  629. // Just move on and try to merge subkeys the best we can?
  630. //
  631. DBGTRACE((DBGF_ERRORS,
  632. TEXT("PNPSIF: RegEnumValue returned error == 0x%08lx\n"),
  633. result));
  634. goto EnumSubkeys;
  635. }
  636. DBGTRACE((DBGF_INFO,
  637. TEXT("PNPSIF: Enumerated value %d == '%s' on hSourceKey.\n"),
  638. dwIndex, pszValueName));
  639. //
  640. // Query to see if this value exists in the hTargetKey
  641. //
  642. result = RegQueryValueEx(hTargetKey,
  643. pszValueName,
  644. 0,
  645. NULL,
  646. NULL,
  647. NULL);
  648. if ((result == ERROR_SUCCESS) &&
  649. !(ulFlags & PNPSIF_RESTORE_REPLACE_TARGET_VALUES_ON_COLLISION)) {
  650. //
  651. // The enumerated value already exists under the target key, and
  652. // we are NOT supposed to replace it.
  653. //
  654. DBGTRACE((DBGF_INFO,
  655. TEXT("PNPSIF: Value '%s' already exists on hTargetKey.\n"),
  656. pszValueName));
  657. } else if ((result == ERROR_FILE_NOT_FOUND) ||
  658. (ulFlags & PNPSIF_RESTORE_REPLACE_TARGET_VALUES_ON_COLLISION)){
  659. //
  660. // The enumerated value doesn't exist under the target key, or
  661. // it does and we're supposed to replace it.
  662. //
  663. result = RegSetValueEx(hTargetKey,
  664. pszValueName,
  665. 0,
  666. dwType,
  667. pValueData,
  668. dwValueDataLength);
  669. if (result != ERROR_SUCCESS) {
  670. //
  671. // Error setting value - whatchya gonna do?
  672. // Just move on to the next enumerated value.
  673. //
  674. DBGTRACE((DBGF_ERRORS,
  675. TEXT("PNPSIF: RegSetValueEx failed setting value '%s', ")
  676. TEXT("error == 0x%08lx\n"),
  677. pszValueName, result));
  678. }
  679. } else {
  680. //
  681. // RegQueryValueEx returned some other error - weird?
  682. //
  683. DBGTRACE((DBGF_ERRORS,
  684. TEXT("PNPSIF: RegQueryValueEx failed for value '%s', ")
  685. TEXT("error == 0x%08lx\n"),
  686. pszValueName, result));
  687. }
  688. }
  689. EnumSubkeys:
  690. //
  691. // Perform special processing of the device instance subkeys.
  692. //
  693. if ((ulFlags & PNPSIF_RESTORE_TYPE_ENUM) &&
  694. (ARGUMENT_PRESENT(pContext)) &&
  695. (ulLevel == ENUM_LEVEL_INSTANCES)) {
  696. if (*((PBOOL)pContext)) {
  697. //
  698. // For root enumerated devices, check for the possibility that
  699. // redundant instance keys of a device might have been created
  700. // in the current registry, in which case we should ignore them
  701. // when migrating to the target registry.
  702. //
  703. //
  704. // The possibility of redundant root-enumerated device
  705. // instance keys exists when:
  706. //
  707. // - the device is root-enumerated
  708. //
  709. // - instances of this device already exist in the target hive
  710. //
  711. // a particular device instance is redundant if it is a newly
  712. // created instance in the target registry for a device where
  713. // other instances already exist.
  714. //
  715. DWORD dwTargetSubkeyCount = 0;
  716. if (RegQueryInfoKey(hTargetKey,
  717. NULL,
  718. NULL,
  719. NULL,
  720. &dwTargetSubkeyCount,
  721. NULL,
  722. NULL,
  723. NULL,
  724. NULL,
  725. NULL,
  726. NULL,
  727. NULL) != ERROR_SUCCESS) {
  728. dwTargetSubkeyCount = 0;
  729. }
  730. bPossibleRedundantInstanceKeys = (dwTargetSubkeyCount > 0);
  731. }
  732. }
  733. //
  734. // Enumerate all hSourceKey subkeys.
  735. //
  736. for (dwIndex = 0; dwIndex < dwSubkeyCount; dwIndex++) {
  737. HKEY hTargetSubkey = NULL, hSourceSubkey = NULL;
  738. DWORD dwSubkeyNameLength = dwMaxSubkeyNameLength;
  739. result = RegEnumKeyEx(hSourceKey,
  740. dwIndex,
  741. pszSubkeyName,
  742. &dwSubkeyNameLength,
  743. 0,
  744. NULL,
  745. NULL,
  746. NULL);
  747. if (result != ERROR_SUCCESS) {
  748. //
  749. // Error enumerating subkeys - whatchya gonna do?
  750. // There is nothing left to do, so just exit.
  751. //
  752. DBGTRACE((DBGF_ERRORS,
  753. TEXT("PNPSIF: RegEnumKeyEx returned error == 0x%08lx\n"),
  754. result));
  755. goto Clean0;
  756. }
  757. DBGTRACE((DBGF_INFO,
  758. TEXT("PNPSIF: enumerated subkey %d == '%s' on hSourceKey.\n"),
  759. dwIndex, pszSubkeyName));
  760. //
  761. // Perform special processing of the Enum subkeys.
  762. //
  763. if ((ulFlags & PNPSIF_RESTORE_TYPE_ENUM) &&
  764. (ARGUMENT_PRESENT(pContext)) &&
  765. (ulLevel == ENUM_LEVEL_ENUMERATORS)) {
  766. //
  767. // At the start of the recursive enumeration for each
  768. // enumerator subkey, reset our (global) BOOL context
  769. // variable to keep track of whether subsequent device
  770. // subkeys represent "ROOT" enumerated devices.
  771. //
  772. PBOOL pbIsRootEnumerated = (PBOOL)pContext;
  773. if (CompareString(LOCALE_INVARIANT,
  774. NORM_IGNORECASE,
  775. (LPTSTR)pszSubkeyName,
  776. -1,
  777. REGSTR_KEY_ROOTENUM,
  778. -1) == CSTR_EQUAL) {
  779. *pbIsRootEnumerated = TRUE;
  780. } else {
  781. *pbIsRootEnumerated = FALSE;
  782. }
  783. }
  784. //
  785. // Open this subkey in hSourceKey
  786. //
  787. result = RegOpenKeyEx(hSourceKey,
  788. pszSubkeyName,
  789. 0,
  790. KEY_READ,
  791. &hSourceSubkey);
  792. if (result == ERROR_SUCCESS) {
  793. //
  794. // Attempt to open this subkey in the hTargetKey
  795. //
  796. result = RegOpenKeyEx(hTargetKey,
  797. pszSubkeyName,
  798. 0,
  799. KEY_READ,
  800. &hTargetSubkey);
  801. if ((result != ERROR_SUCCESS) &&
  802. (bPossibleRedundantInstanceKeys) &&
  803. (IsString4DigitOrdinal(pszSubkeyName))) {
  804. //
  805. // The subkey is a root-enumerated instance key with a
  806. // 4-digit ordinal name (most-likely autogenerated), where
  807. // we may have redundant keys.
  808. //
  809. // If this key doesn't exist in the target registry, it was
  810. // likely a redundant, autogenerated instance created during
  811. // setup because a previous autogenerated key (i.e. one we
  812. // just migrated) already existed, we just won't migrate it.
  813. //
  814. ASSERT(ulFlags & PNPSIF_RESTORE_TYPE_ENUM);
  815. ASSERT(ulLevel == ENUM_LEVEL_INSTANCES);
  816. ASSERT((ARGUMENT_PRESENT(pContext)) && (*((PBOOL)pContext)));
  817. } else {
  818. if (result == ERROR_SUCCESS) {
  819. //
  820. // We successfully opened the target subkey above (we
  821. // just weren't supposed to delete it) so we should have
  822. // a valid handle.
  823. //
  824. ASSERT(hTargetSubkey != NULL);
  825. dwDisposition = REG_OPENED_EXISTING_KEY;
  826. } else {
  827. //
  828. // Create the target subkey.
  829. //
  830. ASSERT(hTargetSubkey == NULL);
  831. result = RegCreateKeyEx(hTargetKey,
  832. pszSubkeyName,
  833. 0,
  834. NULL,
  835. REG_OPTION_NON_VOLATILE,
  836. KEY_ALL_ACCESS,
  837. NULL,
  838. &hTargetSubkey,
  839. &dwDisposition);
  840. if (result == ERROR_SUCCESS) {
  841. ASSERT(dwDisposition == REG_CREATED_NEW_KEY);
  842. }
  843. }
  844. if (result == ERROR_SUCCESS) {
  845. //
  846. // Check if the key had already existed.
  847. //
  848. if (dwDisposition == REG_CREATED_NEW_KEY) {
  849. //
  850. // We just created this key in the target to replace it
  851. // with the corresponding key in the source.
  852. //
  853. if (!MyReplaceKey(hSourceSubkey, hTargetSubkey)) {
  854. DBGTRACE((DBGF_ERRORS,
  855. TEXT("PNPSIF: MyReplaceKey failed with error == 0x%08lx\n"),
  856. GetLastError()));
  857. }
  858. } else if ((ulFlags & PNPSIF_RESTORE_TYPE_ENUM) &&
  859. (ulLevel == ENUM_LEVEL_INSTANCES) &&
  860. (!IsDeviceConfigured(hTargetSubkey)) &&
  861. (IsDeviceConfigured(hSourceSubkey))) {
  862. //
  863. // If we are enumerating instances, check if the
  864. // instance keys in the target and source are
  865. // properly configured.
  866. //
  867. //
  868. // If it is not configured in the target, but is
  869. // configured in the source - then we DO want to
  870. // replace the target instance key, because it might
  871. // be some sort of critical device - like a boot
  872. // device. Even if it's not critical, if the device
  873. // has not been seen since before the last upgrade
  874. // *prior* to backup, we can't really have much
  875. // confidence in those settings anyways. If neither
  876. // the target or source are configured, we may as
  877. // well just keep the target, like we do for
  878. // everything else.
  879. //
  880. if (MyReplaceKey(hSourceSubkey, hTargetSubkey)) {
  881. //
  882. // If we successfully replaced the target instance
  883. // key for this device with the source, then also
  884. // replace the corresponding Class key for this
  885. // instance from the target with the Class key from
  886. // the source.
  887. //
  888. // NOTE: this works because we always restore the
  889. // Enum branch before retoring the Class branch, so
  890. // the Class keys haven't been touched yet.
  891. //
  892. if (!ReplaceClassKeyForInstance(hSourceSubkey,
  893. PnpSifRestoreSourceClassKeyHandle,
  894. PnpSifRestoreTargetClassKeyHandle)) {
  895. DBGTRACE((DBGF_ERRORS,
  896. TEXT("PNPSIF: ReplaceClassKeyForInstance failed, ")
  897. TEXT("error == 0x%08lx\n"),
  898. GetLastError()));
  899. }
  900. } else {
  901. DBGTRACE((DBGF_ERRORS,
  902. TEXT("PNPSIF: MyReplaceKey failed, error == 0x%08lx\n"),
  903. GetLastError()));
  904. }
  905. } else if ((ulLevel-1) != 0) {
  906. //
  907. // We're still above the replacement level, and the key
  908. // previously existed in the target, so we're not
  909. // supposed to replace it. Since the next level is NOT
  910. // the replacement level, just follow the keys down
  911. // recursively.
  912. //
  913. ASSERT(dwDisposition == REG_OPENED_EXISTING_KEY);
  914. if (!RestoreSpecialRegistryData(hSourceSubkey,
  915. hTargetSubkey,
  916. ulLevel-1,
  917. (PVOID)pContext,
  918. ulFlags)) {
  919. //
  920. // Error handling the subkeys - whatcha gonna do?
  921. //
  922. DBGTRACE((DBGF_ERRORS,
  923. TEXT("PNPSIF: RestoreSpecialRegistryData failed ")
  924. TEXT("for subkey %s at level %d, error == 0x%08lx\n"),
  925. pszSubkeyName, ulLevel-1,
  926. GetLastError()));
  927. }
  928. } else {
  929. //
  930. // The key already exists, so don't replace it. Also,
  931. // the next level is the replacement level, so don't
  932. // recurse either (else we'd replace it). Basically,
  933. // just do nothing here.
  934. //
  935. ASSERT(ulLevel == 1);
  936. ASSERT(dwDisposition == REG_OPENED_EXISTING_KEY);
  937. }
  938. //
  939. // Close the open target subkey.
  940. //
  941. RegCloseKey(hTargetSubkey);
  942. } else {
  943. //
  944. // could not open/create subkey in taget registry.
  945. //
  946. DBGTRACE((DBGF_ERRORS,
  947. TEXT("PNPSIF: RegCreateKey failed to create target subkey %s ")
  948. TEXT("with error == 0x%08lx\n"),
  949. pszSubkeyName, result));
  950. }
  951. }
  952. //
  953. // Close the enumerated hSourceKey subkey.
  954. //
  955. RegCloseKey(hSourceSubkey);
  956. } else {
  957. //
  958. // Could not open the enumerated hSourceKey subkey.
  959. //
  960. DBGTRACE((DBGF_ERRORS,
  961. TEXT("PNPSIF: RegOpenKey failed to open existing subkey %s, ")
  962. TEXT("error == 0x%08lx\n"),
  963. pszSubkeyName, result));
  964. }
  965. } // for (dwIndex = 0; dwIndex < dwSubkeyCount; dwIndex++)
  966. } // (ulLevel != 0)
  967. Clean0:
  968. //
  969. // Free any allocated buffers
  970. //
  971. if (pszSubkeyName != NULL) {
  972. MyFree(pszSubkeyName);
  973. }
  974. if (pszValueName != NULL) {
  975. MyFree(pszValueName);
  976. }
  977. if (pValueData != NULL) {
  978. MyFree(pValueData);
  979. }
  980. if (result != ERROR_SUCCESS) {
  981. SetLastError(result);
  982. }
  983. return (result == ERROR_SUCCESS);
  984. } // RestoreSpecialRegistryData
  985. BOOL
  986. MyReplaceKey(
  987. IN HKEY hSourceKey,
  988. IN HKEY hTargetKey
  989. )
  990. /*++
  991. Routine Description:
  992. This routine replaces the target registry key with the source registry key.
  993. This is done by performing a RegSaveKey on the source registry key to a
  994. temporary file, and restoring that file to the target registry key. All
  995. data contained below the target registry key is lost. The source registry
  996. key is not modified by this routine.
  997. Arguments:
  998. hSourceKey - Handle to the key that is the source of the restore operation.
  999. All values and subkeys of the source key will be restored on
  1000. top of the target key.
  1001. hTargetKey - Handle to the key that is the target of the restore operation.
  1002. All values and subkeys of the source key will be restored on
  1003. top of this key, and all existing values and data underneath
  1004. this key will be lost.
  1005. Return Value:
  1006. TRUE if successful, FALSE otherwise. Upon failure, additional information
  1007. can be retrieved by calling GetLastError().
  1008. Notes:
  1009. Since this routine uses the RegSaveKey and RegRestoreKey registry APIs, it
  1010. is expected that the calling thread/process have both the SE_BACKUP_NAME and
  1011. SE_RESTORE_NAME privileges.
  1012. --*/
  1013. {
  1014. LONG result = ERROR_SUCCESS;
  1015. HRESULT hr;
  1016. TCHAR szTempFilePath[MAX_PATH];
  1017. TCHAR szTempFileName[MAX_PATH];
  1018. DWORD dwTemp;
  1019. //
  1020. // Use the temporary directory to store the saved registry key.
  1021. //
  1022. dwTemp = GetTempPath(MAX_PATH, szTempFilePath);
  1023. if ((dwTemp == 0) || (dwTemp > MAX_PATH)) {
  1024. DBGTRACE((DBGF_ERRORS,
  1025. TEXT("PNPSIF: GetTempPath failed, ")
  1026. TEXT("current directory will be specified.\n")));
  1027. // current path specified with trailing '\', as GetTempPath would have.
  1028. hr = StringCchCopy(szTempFileName,
  1029. SIZECHARS(szTempFileName),
  1030. TEXT(".\\"));
  1031. if (FAILED(hr)) {
  1032. result = HRESULT_CODE(hr);
  1033. goto Clean0;
  1034. }
  1035. }
  1036. //
  1037. // Assign the saved registry key a temporary, unique filename.
  1038. //
  1039. if (!GetTempFileName(szTempFilePath,
  1040. TEXT("PNP"),
  1041. 0, // make sure it's unique
  1042. szTempFileName)) {
  1043. DBGTRACE((DBGF_ERRORS,
  1044. TEXT("PNPSIF: GetTempFileName failed with error == 0x%08lx, ")
  1045. TEXT("using hardcoded temp file name!\n"),
  1046. GetLastError()));
  1047. hr = StringCchCopy(szTempFileName,
  1048. SIZECHARS(szTempFileName),
  1049. szTempFilePath);
  1050. if (FAILED(hr)) {
  1051. result = HRESULT_CODE(hr);
  1052. goto Clean0;
  1053. }
  1054. hr = StringCchCat(szTempFileName,
  1055. SIZECHARS(szTempFileName),
  1056. TEXT("~pnpreg.tmp"));
  1057. if (FAILED(hr)) {
  1058. result = HRESULT_CODE(hr);
  1059. goto Clean0;
  1060. }
  1061. }
  1062. DBGTRACE((DBGF_INFO,
  1063. TEXT("PNPSIF: Using temporary filename: %s\n"),
  1064. szTempFileName));
  1065. //
  1066. // A side effect of requesting a "unique" filename from GetTempFileName is
  1067. // that it automatically creates the file. Unfortunately, RegSaveKey will
  1068. // fail if the specified file already exists, so delete the file now.
  1069. //
  1070. if(pSifUtilFileExists(szTempFileName,NULL)) {
  1071. DBGTRACE((DBGF_INFO,
  1072. TEXT("PNPSIF: Temporary file %s exists, deleting.\n"),
  1073. szTempFileName));
  1074. SetFileAttributes(szTempFileName, FILE_ATTRIBUTE_NORMAL);
  1075. DeleteFile(szTempFileName);
  1076. }
  1077. //
  1078. // Save the source key to a file using the temporary file name.
  1079. // (calling thread/process must have the SE_BACKUP_NAME privilege)
  1080. //
  1081. result = RegSaveKey(hSourceKey,
  1082. szTempFileName,
  1083. NULL);
  1084. if (result == ERROR_SUCCESS) {
  1085. //
  1086. // Restore the file to the target key.
  1087. // (calling thread/process must have the SE_RESTORE_NAME privilege)
  1088. //
  1089. result = RegRestoreKey(hTargetKey,
  1090. szTempFileName,
  1091. REG_FORCE_RESTORE);
  1092. if (result != ERROR_SUCCESS) {
  1093. //
  1094. // Failed to restore the file to the target key!
  1095. //
  1096. DBGTRACE((DBGF_ERRORS,
  1097. TEXT("PNPSIF: RegRestoreKey from %s failed, ")
  1098. TEXT("error == 0x%08lx\n"),
  1099. szTempFileName, result));
  1100. } else {
  1101. DBGTRACE((DBGF_INFO,
  1102. TEXT("PNPSIF: Key replacement successful.\n")));
  1103. }
  1104. //
  1105. // Delete the temporary file we created, now that we're done with it.
  1106. //
  1107. DBGTRACE((DBGF_INFO,
  1108. TEXT("PNPSIF: Deleting temporary file %s.\n"),
  1109. szTempFileName));
  1110. ASSERT(pSifUtilFileExists(szTempFileName,NULL));
  1111. SetFileAttributes(szTempFileName, FILE_ATTRIBUTE_NORMAL);
  1112. DeleteFile(szTempFileName);
  1113. } else {
  1114. //
  1115. // Failed to save the source key.
  1116. //
  1117. DBGTRACE((DBGF_ERRORS,
  1118. TEXT("PNPSIF: RegSaveKey to %s failed with error == 0x%08lx\n"),
  1119. szTempFileName, result));
  1120. }
  1121. Clean0:
  1122. if (result != ERROR_SUCCESS) {
  1123. SetLastError(result);
  1124. }
  1125. return (result == ERROR_SUCCESS);
  1126. } // MyReplaceKey
  1127. BOOL
  1128. IsString4DigitOrdinal(
  1129. IN LPTSTR pszSubkeyName
  1130. )
  1131. /*++
  1132. Routine Description:
  1133. This routine checks if a subkey name has the form of a 4-digit decimal
  1134. ordinal (e.g. "0000", "0001", ... , "9999"), that are typically given to
  1135. auto-generated root-enumerated device instance ids - i.e. TEXT("%04u").
  1136. Arguments:
  1137. pszSubkeyName - Subkey name to check.
  1138. Return Value:
  1139. TRUE if the string has the form of a 4-digit ordinal string, FALSE
  1140. otherwise.
  1141. --*/
  1142. {
  1143. LPTSTR p;
  1144. ULONG ulTotalLength = 0;
  1145. if ((!ARGUMENT_PRESENT(pszSubkeyName)) ||
  1146. (pszSubkeyName[0] == TEXT('\0'))) {
  1147. return FALSE;
  1148. }
  1149. for (p = pszSubkeyName; *p; p++) {
  1150. //
  1151. // Count the caharcters in the string, and make sure its not longer than
  1152. // 4 characters.
  1153. //
  1154. ulTotalLength++;
  1155. if (ulTotalLength > 4) {
  1156. return FALSE;
  1157. }
  1158. //
  1159. // Check if the character is non-numeric, non-decimal.
  1160. //
  1161. if ((*p < TEXT('0')) || (*p > TEXT('9'))) {
  1162. return FALSE;
  1163. }
  1164. }
  1165. if (ulTotalLength != 4) {
  1166. return FALSE;
  1167. }
  1168. return TRUE;
  1169. } // IsString4DigitOrdinal
  1170. BOOL
  1171. IsDeviceConfigured(
  1172. IN HKEY hInstanceKey
  1173. )
  1174. /*++
  1175. Routine Description:
  1176. This routine determines whether the device instance specified by the
  1177. supplied registry key is configured or not. If a device has configflags, and
  1178. CONFIGFLAG_REINSTALL or CONFIGFLAG_FAILEDINSTALL are not set then the
  1179. device is considered configured.
  1180. Arguments:
  1181. hInstanceKey - Handle to a device instance registry key.
  1182. Return Value:
  1183. TRUE if successful, FALSE otherwise. Upon failure, additional information
  1184. can be retrieved by calling GetLastError().
  1185. --*/
  1186. {
  1187. BOOL bDeviceConfigured = FALSE;
  1188. DWORD dwSize, dwType, dwConfigFlags;
  1189. if ((hInstanceKey == NULL) ||
  1190. (hInstanceKey == INVALID_HANDLE_VALUE)) {
  1191. return FALSE;
  1192. }
  1193. dwSize = sizeof(dwConfigFlags);
  1194. if ((RegQueryValueEx(hInstanceKey,
  1195. REGSTR_VAL_CONFIGFLAGS,
  1196. 0,
  1197. &dwType,
  1198. (LPBYTE)&dwConfigFlags,
  1199. &dwSize) == ERROR_SUCCESS) &&
  1200. (dwType == REG_DWORD) &&
  1201. !(dwConfigFlags & CONFIGFLAG_REINSTALL) &&
  1202. !(dwConfigFlags & CONFIGFLAG_FAILEDINSTALL)) {
  1203. bDeviceConfigured = TRUE;
  1204. }
  1205. return bDeviceConfigured;
  1206. } // IsDeviceConfigured
  1207. BOOL
  1208. ReplaceClassKeyForInstance(
  1209. IN HKEY hSourceInstanceKey,
  1210. IN HKEY hSourceRootClassKey,
  1211. IN HKEY hTargetRootClassKey
  1212. )
  1213. /*++
  1214. Routine Description:
  1215. This routine replaces the class key corresponding to the specified device
  1216. instance key (as specified by the "Driver" value in the instance key) in the
  1217. target hive with the class key from the source.
  1218. Arguments:
  1219. hSourceInstanceKey - Handle to a device instance registry key in the source
  1220. hive.
  1221. hSourceRootClassKey - Handle to the root of the Class branch in the source
  1222. hive - the same hive as the specified instance key.
  1223. hTargetRootClassKey - Handle to the root of the Class branch in the target
  1224. hive.
  1225. Return Value:
  1226. TRUE if successful, FALSE otherwise.
  1227. --*/
  1228. {
  1229. LONG result = ERROR_SUCCESS;
  1230. TCHAR szDriverKeyName[MAX_GUID_STRING_LEN + 5]; // "{ClassGUID}\XXXX"
  1231. DWORD dwSize, dwType, dwDisposition;
  1232. HKEY hSourceClassSubkey = NULL, hTargetClassSubkey = NULL;
  1233. if ((hSourceInstanceKey == NULL) ||
  1234. (hSourceInstanceKey == INVALID_HANDLE_VALUE) ||
  1235. (hSourceRootClassKey == NULL) ||
  1236. (hSourceRootClassKey == INVALID_HANDLE_VALUE) ||
  1237. (hTargetRootClassKey == NULL) ||
  1238. (hTargetRootClassKey == INVALID_HANDLE_VALUE)) {
  1239. return FALSE;
  1240. }
  1241. //
  1242. // Read the REGSTR_VAL_DRIVER REG_SZ "Driver" value from the instance key.
  1243. //
  1244. szDriverKeyName[0] = TEXT('\0');
  1245. dwSize = sizeof(szDriverKeyName);
  1246. result = RegQueryValueEx(hSourceInstanceKey,
  1247. REGSTR_VAL_DRIVER,
  1248. 0,
  1249. &dwType,
  1250. (LPBYTE)szDriverKeyName,
  1251. &dwSize);
  1252. if (result == ERROR_FILE_NOT_FOUND) {
  1253. //
  1254. // No "Driver" value, so there's no class key to migrate, which is fine.
  1255. //
  1256. result = ERROR_SUCCESS;
  1257. goto Clean0;
  1258. } else if ((result != ERROR_SUCCESS) ||
  1259. (dwType != REG_SZ)) {
  1260. //
  1261. // Any other error is a failure to read the value.
  1262. //
  1263. goto Clean0;
  1264. }
  1265. //
  1266. // Open the "Driver" key in the source Class branch.
  1267. //
  1268. result = RegOpenKeyEx(hSourceRootClassKey,
  1269. szDriverKeyName,
  1270. 0,
  1271. KEY_READ,
  1272. &hSourceClassSubkey);
  1273. if (result != ERROR_SUCCESS) {
  1274. //
  1275. // The instance key had a "Driver" value, so a corresponding key
  1276. // *should* exist. If we couldn't open it, it's from some failure
  1277. // besides that.
  1278. //
  1279. return FALSE;
  1280. }
  1281. //
  1282. // Open / create the corresponding key in the target Class branch.
  1283. //
  1284. result = RegCreateKeyEx(hTargetRootClassKey,
  1285. szDriverKeyName,
  1286. 0,
  1287. NULL,
  1288. REG_OPTION_NON_VOLATILE,
  1289. KEY_ALL_ACCESS,
  1290. NULL,
  1291. &hTargetClassSubkey,
  1292. &dwDisposition);
  1293. if (result != ERROR_SUCCESS) {
  1294. goto Clean0;
  1295. }
  1296. //
  1297. // Replace the target class subkey with the source.
  1298. //
  1299. if (!MyReplaceKey(hSourceClassSubkey, hTargetClassSubkey)) {
  1300. result = GetLastError();
  1301. DBGTRACE((DBGF_ERRORS,
  1302. TEXT("PNPSIF: MyReplaceKey failed with error == 0x%08lx\n"),
  1303. result));
  1304. }
  1305. Clean0:
  1306. if (hTargetClassSubkey) {
  1307. RegCloseKey(hTargetClassSubkey);
  1308. }
  1309. if (hSourceClassSubkey) {
  1310. RegCloseKey(hSourceClassSubkey);
  1311. }
  1312. if (result != ERROR_SUCCESS) {
  1313. SetLastError(result);
  1314. }
  1315. return (result == ERROR_SUCCESS);
  1316. } // ReplaceClassKeyForInstance
  1317.