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.

862 lines
24 KiB

  1. //#pragma title( "TReg.cpp - NT registry class" )
  2. /*
  3. Copyright (c) 1995-1998, Mission Critical Software, Inc. All rights reserved.
  4. ===============================================================================
  5. Module - TReg.cpp
  6. System - Common
  7. Author - Tom Bernhardt, Rich Denham
  8. Created - 1995-09-01
  9. Description - NT registry class.
  10. Updates -
  11. ===============================================================================
  12. */
  13. #ifdef USE_STDAFX
  14. # include "stdafx.h"
  15. #else
  16. # include <windows.h>
  17. #endif
  18. #include <stdlib.h>
  19. #include <stdio.h>
  20. #include <time.h>
  21. #include "Common.hpp"
  22. #include "UString.hpp"
  23. #include "Err.hpp"
  24. #include "TNode.hpp"
  25. #include "TReg.hpp"
  26. // Short term solution
  27. #define MAX_REG_NAMELEN 512
  28. #define MAX_REG_VALUELEN 2048
  29. // Destructor function was formerly inline.
  30. // It is here to facilitate handle leak tracing.
  31. TRegKey::~TRegKey()
  32. {
  33. Close();
  34. };
  35. // Close function was formerly inline.
  36. // It is here to facilitate handle leak tracing.
  37. void
  38. TRegKey::Close()
  39. {
  40. if ( hKey != INVALID_HANDLE_VALUE )
  41. {
  42. RegCloseKey( hKey );
  43. hKey = (HKEY) INVALID_HANDLE_VALUE;
  44. }
  45. };
  46. // open registry on remote computer
  47. DWORD
  48. TRegKey::Connect(
  49. HKEY hPreDefined ,// in -must be HKEY_LOCAL_MACHINE or HKEY_USERS
  50. TCHAR const * machineName // in -remote computer name
  51. )
  52. {
  53. LONG rc; // return code
  54. if ( hKey != INVALID_HANDLE_VALUE )
  55. {
  56. Close();
  57. }
  58. rc = RegConnectRegistry( const_cast<TCHAR *>(machineName), hPreDefined, &hKey );
  59. if ( rc )
  60. {
  61. hKey = (HKEY) INVALID_HANDLE_VALUE;
  62. }
  63. return (DWORD)rc;
  64. }
  65. // create new key
  66. DWORD
  67. TRegKey::Create(
  68. TCHAR const * keyname ,// in -name/path of key to create/open
  69. HKEY hParent ,// in -handle of parent key
  70. DWORD * pDisp ,// out-disposition of create
  71. DWORD access // in -security access mask for key
  72. )
  73. {
  74. DWORD disp;
  75. LONG rc;
  76. if ( hKey != INVALID_HANDLE_VALUE )
  77. {
  78. Close();
  79. }
  80. rc = RegCreateKeyEx( hParent,
  81. keyname,
  82. 0,
  83. NULL,
  84. REG_OPTION_NON_VOLATILE,
  85. access,
  86. NULL,
  87. &hKey,
  88. (pDisp!=NULL) ? pDisp : &disp );
  89. if ( rc )
  90. {
  91. hKey = (HKEY) INVALID_HANDLE_VALUE;
  92. }
  93. return (DWORD)rc;
  94. }
  95. // create new key (using backup/restore)
  96. DWORD
  97. TRegKey::CreateBR(
  98. TCHAR const * keyname ,// in -name/path of key to create/open
  99. HKEY hParent ,// in -handle of parent key
  100. DWORD * pDisp ,// out-disposition of create
  101. DWORD access // in -security access mask for key
  102. )
  103. {
  104. DWORD disp;
  105. LONG rc;
  106. if ( hKey != INVALID_HANDLE_VALUE )
  107. {
  108. Close();
  109. }
  110. rc = RegCreateKeyEx( hParent,
  111. keyname,
  112. 0,
  113. NULL,
  114. REG_OPTION_BACKUP_RESTORE,
  115. access,
  116. NULL,
  117. &hKey,
  118. (pDisp!=NULL) ? pDisp : &disp );
  119. if ( rc )
  120. {
  121. hKey = (HKEY) INVALID_HANDLE_VALUE;
  122. }
  123. return (DWORD)rc;
  124. }
  125. // open existing key
  126. DWORD
  127. TRegKey::Open(
  128. TCHAR const * keyname ,// in -name/path of key to create/open
  129. HKEY hParent ,// in -handle of parent key
  130. DWORD access // in -security access mask for key
  131. )
  132. {
  133. LONG rc;
  134. if ( hKey != INVALID_HANDLE_VALUE )
  135. {
  136. Close();
  137. }
  138. rc = RegOpenKeyEx( hParent,
  139. keyname,
  140. 0,
  141. access,
  142. &hKey );
  143. if ( rc )
  144. {
  145. hKey = (HKEY) INVALID_HANDLE_VALUE;
  146. }
  147. return (DWORD)rc;
  148. }
  149. // Recursively delete the subkey
  150. DWORD // ret-os return code
  151. TRegKey::SubKeyRecursiveDel(
  152. TCHAR const * keyname // in - the subkey name
  153. ) const
  154. {
  155. TRegKey subKey;
  156. DWORD retval;
  157. retval = subKey.Open(keyname, this);
  158. if (retval == ERROR_SUCCESS)
  159. {
  160. retval = subKey.HiveDel();
  161. subKey.Close();
  162. }
  163. if (retval == ERROR_SUCCESS)
  164. retval = SubKeyDel(keyname);
  165. return retval;
  166. }
  167. // Gets the subkey value of the specified index number
  168. DWORD // ret-os return code
  169. TRegKey::SubKeyEnum(
  170. DWORD n ,// in -ordinal number of subkey
  171. TCHAR * keyname ,// out-key name
  172. DWORD keylen // in -max size of key name in TCHARs
  173. ) const
  174. {
  175. LONG rc;
  176. DWORD keyLen = keylen;
  177. FILETIME lastWrite;
  178. rc = RegEnumKeyEx( hKey,
  179. n,
  180. keyname,
  181. &keyLen,
  182. 0,
  183. NULL,
  184. NULL,
  185. &lastWrite );
  186. return (DWORD)rc;
  187. }
  188. // Enumerate value
  189. DWORD // ret-0 or error code
  190. TRegKey::ValueEnum(
  191. DWORD index ,// in -ordinal number of subkey
  192. TCHAR * name ,// out-name
  193. DWORD namelen ,// in -name size in TCHARs
  194. void * value ,// out-value
  195. DWORD * valuelen ,// i/o-value size in BYTEs
  196. DWORD * type // out-value type code
  197. ) const
  198. {
  199. return (DWORD)RegEnumValue( hKey, index, name, &namelen, NULL, type, (BYTE *) value, valuelen );
  200. }
  201. // Get REG_DWORD value
  202. DWORD // ret-OS return code
  203. TRegKey::ValueGetDWORD(
  204. TCHAR const * name ,// in -value name
  205. DWORD * value // out-returned DWORD value
  206. ) const
  207. {
  208. LONG osRc; // OS return code
  209. DWORD type; // type of value
  210. DWORD len = sizeof *value; // value length
  211. osRc = RegQueryValueEx( hKey, name, NULL, &type, (BYTE *) value, &len );
  212. if ( !osRc && (type != REG_DWORD) )
  213. {
  214. osRc = ERROR_FILE_NOT_FOUND;
  215. }
  216. return (DWORD)osRc;
  217. }
  218. // Get REG_SZ value
  219. DWORD // ret-OS return code
  220. TRegKey::ValueGetStr(
  221. TCHAR const * name ,// in -value name
  222. TCHAR * value ,// out-value buffer
  223. DWORD maxlen // in -sizeof value buffer
  224. ) const
  225. {
  226. LONG osRc; // OS return code
  227. DWORD type; // type of value
  228. DWORD len; // value length
  229. // force maxlen to an integral number of TEXT characters
  230. maxlen = maxlen / (sizeof value[0]) * (sizeof value[0]);
  231. if ( !maxlen )
  232. {
  233. osRc = ERROR_FILE_NOT_FOUND;
  234. }
  235. else
  236. {
  237. len = maxlen;
  238. osRc = RegQueryValueEx( hKey, name, NULL, &type, (BYTE *) value, &len );
  239. len = len / (sizeof value[0]) * (sizeof value[0]);
  240. if ( !osRc && (type != REG_SZ) )
  241. {
  242. osRc = ERROR_FILE_NOT_FOUND;
  243. }
  244. if ( osRc )
  245. {
  246. value[0] = TEXT('\0');
  247. }
  248. else
  249. { // return of a null-terminated string is not guaranteed by API!
  250. // force null-terminated string, truncate string if necessary.
  251. if ( len >= maxlen )
  252. {
  253. len = maxlen - sizeof value[0];
  254. }
  255. value[len/(sizeof value[0])] = TEXT('\0');
  256. }
  257. }
  258. return (DWORD)osRc;
  259. }
  260. DWORD
  261. TRegKey::ValueGet(
  262. TCHAR const * name ,// in -name
  263. void * value ,// out-value
  264. DWORD * lenvalue ,// i/o-length of value
  265. DWORD * typevalue // out-type of value
  266. ) const
  267. {
  268. return (DWORD)RegQueryValueEx( hKey, name, 0, typevalue, (UCHAR *) value, lenvalue );
  269. }
  270. // Set REG_SZ value
  271. DWORD
  272. TRegKey::ValueSetStr(
  273. TCHAR const * name ,// in -value name
  274. TCHAR const * value ,// out-value
  275. DWORD type // in -value type
  276. ) const
  277. {
  278. return (DWORD)RegSetValueEx( hKey,
  279. name,
  280. NULL,
  281. type,
  282. (LPBYTE) value,
  283. (UStrLen(value) + 1) * sizeof value[0] );
  284. }
  285. DWORD
  286. TRegKey::ValueSet(
  287. TCHAR const * name ,// in -name
  288. void const * value ,// in -value
  289. DWORD lenvalue ,// in -length of value
  290. DWORD typevalue // in -type of value
  291. ) const
  292. {
  293. return (DWORD)RegSetValueEx( hKey,
  294. name,
  295. 0,
  296. typevalue,
  297. (UCHAR const *) value,
  298. lenvalue );
  299. }
  300. DWORD // ret-0 or error code
  301. TRegKey::ValueDel(
  302. TCHAR const * name // in -value name
  303. ) const
  304. {
  305. LONG rc;
  306. rc = (DWORD)RegDeleteValue(hKey, name);
  307. return rc;
  308. }
  309. DWORD // ret-OS return code
  310. TRegKey::HiveCopy(
  311. TRegKey const * source // in -source hive
  312. )
  313. {
  314. DWORD retval=0; // returned value
  315. DWORD index; // key/value index
  316. TCHAR name[MAX_REG_NAMELEN]; // key name
  317. TCHAR value[MAX_REG_VALUELEN]; // value name
  318. DWORD valuelen; // value length
  319. DWORD type; // value type
  320. TRegKey srcNest; // nested source registry
  321. TRegKey trgNest; // nested target registry
  322. // process values at this level
  323. for ( index = 0;
  324. !retval;
  325. index++ )
  326. {
  327. valuelen = sizeof value;
  328. retval = source->ValueEnum( index, name, MAX_REG_NAMELEN, value, &valuelen, &type );
  329. if ( !retval )
  330. {
  331. retval = this->ValueSet( name, value, valuelen, type );
  332. }
  333. else if ( retval == ERROR_MORE_DATA )
  334. {
  335. retval = 0;
  336. }
  337. }
  338. if ( retval == ERROR_NO_MORE_ITEMS )
  339. {
  340. retval = 0;
  341. }
  342. // process keys at this level; for each key make a recursive call
  343. for ( index = 0;
  344. !retval;
  345. index++ )
  346. {
  347. retval = source->SubKeyEnum( index, name, MAX_REG_NAMELEN );
  348. if ( !retval )
  349. {
  350. retval = srcNest.Open( name, source );
  351. if ( !retval )
  352. {
  353. retval = trgNest.Create( name, this );
  354. if ( !retval )
  355. {
  356. retval = trgNest.HiveCopy( &srcNest );
  357. trgNest.Close();
  358. }
  359. srcNest.Close();
  360. }
  361. }
  362. }
  363. if ( retval == ERROR_NO_MORE_ITEMS )
  364. {
  365. retval = 0;
  366. }
  367. return retval;
  368. }
  369. DWORD // ret-OS return code
  370. TRegKey::HiveDel()
  371. {
  372. DWORD retval = 0; // returned value
  373. DWORD index; // value/key index
  374. TCHAR name[MAX_REG_NAMELEN]; // name
  375. DWORD namelen; // name length
  376. BYTE value[MAX_REG_VALUELEN]; // value
  377. DWORD valuelen; // value length
  378. DWORD type; // value type code
  379. TRegKey trgNest; // nested target registry
  380. // delete values at this level
  381. for ( index = 0;
  382. !retval;
  383. /* index++ */ ) // note that index remains at zero
  384. {
  385. namelen = MAX_REG_NAMELEN;
  386. valuelen = sizeof value;
  387. retval = ValueEnum( index, name, namelen, value, &valuelen, &type );
  388. if ( retval == ERROR_MORE_DATA )
  389. {
  390. retval = 0;
  391. }
  392. if ( !retval )
  393. {
  394. retval = ValueDel( name );
  395. }
  396. }
  397. if ( retval == ERROR_NO_MORE_ITEMS )
  398. {
  399. retval = 0;
  400. }
  401. // process keys at this level; for each key make a recursive call
  402. for ( index = 0;
  403. !retval;
  404. /* index++ */ ) // note that index remains at zero
  405. {
  406. retval = SubKeyEnum( index, name, MAX_REG_NAMELEN );
  407. if ( !retval )
  408. {
  409. retval = trgNest.Open( name, this );
  410. if ( !retval )
  411. {
  412. retval = trgNest.HiveDel();
  413. trgNest.Close();
  414. }
  415. retval = SubKeyDel( name );
  416. }
  417. }
  418. if ( retval == ERROR_NO_MORE_ITEMS )
  419. {
  420. retval = 0;
  421. }
  422. return retval;
  423. }
  424. // These four classes are used only by TRegReplicate
  425. // Class to represent one registry key
  426. class RKey : public TNode
  427. {
  428. friend class RKeyList;
  429. private:
  430. TCHAR * name; // key name
  431. protected:
  432. public:
  433. RKey() { name = NULL; };
  434. ~RKey() { if ( name ) delete [] name; };
  435. BOOL New( TCHAR const * aname );
  436. TCHAR const * GetName() const { return name; };
  437. };
  438. BOOL
  439. RKey::New(
  440. TCHAR const * aname // in -key name
  441. )
  442. {
  443. name = new TCHAR[UStrLen(aname)+1];
  444. if ( name )
  445. {
  446. UStrCpy( name, aname );
  447. }
  448. return !!name;
  449. }
  450. // Class to represent the set of registry keys at one level
  451. class RKeyList : public TNodeListSortable
  452. {
  453. private:
  454. static TNodeCompare( Compare ) { return UStrICmp(
  455. ((RKey const *) v1)->name,
  456. ((RKey const *) v2)->name ); }
  457. protected:
  458. public:
  459. RKeyList() : TNodeListSortable( Compare ) {}
  460. ~RKeyList();
  461. };
  462. // RKeyList object destructor
  463. RKeyList::~RKeyList()
  464. {
  465. DeleteAllListItems( RKey );
  466. }
  467. // Class to represent one registry value
  468. class RValue : public TNode
  469. {
  470. friend class RValueList;
  471. private:
  472. TCHAR * name; // value's name
  473. BYTE * value; // value's value
  474. DWORD valuelen; // value's value length
  475. DWORD type; // value's type
  476. protected:
  477. public:
  478. RValue() { name = NULL; value = NULL; valuelen = type = 0; };
  479. ~RValue() { if ( name ) delete [] name;
  480. if ( value ) delete [] value; };
  481. BOOL New( TCHAR const * aname, BYTE const * avalue, DWORD valuelen, DWORD type );
  482. TCHAR const * GetName() const { return name; };
  483. BYTE const * GetValue() const { return value; };
  484. DWORD GetValueLen() const { return valuelen; };
  485. DWORD GetType() const { return type; };
  486. };
  487. BOOL
  488. RValue::New(
  489. TCHAR const * aname ,// in -value's name
  490. BYTE const * avalue ,// in -value's value
  491. DWORD avaluelen ,// in -value's value length
  492. DWORD atype // in -value's type
  493. )
  494. {
  495. name = new TCHAR[UStrLen(aname)+1];
  496. if ( name )
  497. {
  498. UStrCpy( name, aname );
  499. }
  500. value = new BYTE[avaluelen];
  501. if ( value )
  502. {
  503. memcpy( value, avalue, avaluelen );
  504. }
  505. valuelen = avaluelen;
  506. type = atype;
  507. return name && value;
  508. }
  509. // Class to represent the set of registry values at one level
  510. class RValueList : public TNodeListSortable
  511. {
  512. private:
  513. static TNodeCompare( Compare ) { return UStrICmp(
  514. ((RValue const *)v1)->name,
  515. ((RValue const *)v2)->name ); }
  516. protected:
  517. public:
  518. RValueList() : TNodeListSortable( Compare ) {}
  519. ~RValueList();
  520. };
  521. // RValueList object destructor
  522. RValueList::~RValueList()
  523. {
  524. DeleteAllListItems( RValue );
  525. }
  526. // Static subroutine used only by TRegReplicate
  527. // collect all values at one registry level into a RValueList
  528. DWORD static
  529. CollectValues(
  530. RValueList * pValueList ,// out-value list to be built
  531. TRegKey const * pRegKey // in -registry key
  532. )
  533. {
  534. DWORD retval=0; // returned value
  535. DWORD index; // value enum index
  536. TCHAR name[MAX_REG_NAMELEN]; // value name
  537. BYTE value[MAX_REG_VALUELEN]; // value value
  538. DWORD valuelen; // value length
  539. DWORD type; // value type
  540. RValue * pValue; // new value
  541. for ( index = 0;
  542. !retval;
  543. index++ )
  544. {
  545. valuelen = sizeof value;
  546. retval = pRegKey->ValueEnum( index, name, MAX_REG_NAMELEN, value, &valuelen, &type );
  547. if ( !retval )
  548. {
  549. pValue = new RValue;
  550. if ( pValue )
  551. {
  552. try
  553. {
  554. if ( pValue->New( name, value, valuelen, type ) )
  555. {
  556. pValueList->Insert( pValue );
  557. }
  558. else
  559. {
  560. delete pValue;
  561. pValue = NULL;
  562. }
  563. }
  564. catch(...)
  565. {
  566. delete pValue;
  567. pValue = NULL;
  568. throw;
  569. }
  570. }
  571. if ( !pValue )
  572. {
  573. retval = ERROR_NOT_ENOUGH_MEMORY;
  574. }
  575. }
  576. else if ( retval == ERROR_MORE_DATA )
  577. {
  578. retval = 0;
  579. }
  580. }
  581. if ( retval == ERROR_NO_MORE_ITEMS )
  582. {
  583. retval = 0;
  584. }
  585. return retval;
  586. }
  587. // Static subroutine used only by TRegReplicate
  588. // collect all keys at one registry level into a RKeyList
  589. DWORD static
  590. CollectKeys(
  591. RKeyList * pKeyList ,// out-key list to be built
  592. TRegKey const * pRegKey // in -registry key
  593. )
  594. {
  595. DWORD retval=0; // returned value
  596. DWORD index; // key enum index
  597. TCHAR name[MAX_REG_NAMELEN]; // key name
  598. RKey * pKey; // new key object
  599. for ( index = 0;
  600. !retval;
  601. index++ )
  602. {
  603. retval = pRegKey->SubKeyEnum( index, name, MAX_REG_NAMELEN );
  604. if ( !retval )
  605. {
  606. pKey = new RKey;
  607. if ( pKey )
  608. {
  609. try
  610. {
  611. if ( pKey->New( name ) )
  612. {
  613. pKeyList->Insert( pKey );
  614. }
  615. else
  616. {
  617. delete pKey;
  618. pKey = NULL;
  619. }
  620. }
  621. catch(...)
  622. {
  623. delete pKey;
  624. pKey = NULL;
  625. throw;
  626. }
  627. }
  628. if ( !pKey )
  629. {
  630. retval = ERROR_NOT_ENOUGH_MEMORY;
  631. }
  632. }
  633. }
  634. if ( retval == ERROR_NO_MORE_ITEMS )
  635. {
  636. retval = 0;
  637. }
  638. return retval;
  639. }
  640. // Replicate registry hive
  641. DWORD // ret-OS return code
  642. TRegKey::HiveReplicate(
  643. TRegKey const * source // in -source hive
  644. )
  645. {
  646. DWORD retval=0; // returned value
  647. RValueList srcValues; // source values
  648. RValueList trgValues; // target values
  649. TNodeListEnum eSrcValue; // enumerate source values
  650. RValue const * pSrcValue; // source value
  651. TNodeListEnum eTrgValue; // enumerate target values
  652. RValue const * pTrgValue; // target value
  653. RKeyList srcKeys; // source keys
  654. RKeyList trgKeys; // target keys
  655. TNodeListEnum eSrcKey; // enumerate source keys
  656. RKey const * pSrcKey; // source key
  657. TNodeListEnum eTrgKey; // enumerate target keys
  658. RKey const * pTrgKey; // target key
  659. int cmpRc; // compare return code
  660. TRegKey srcNest; // nested source registry
  661. TRegKey trgNest; // nested target registry
  662. // handle replication of values at this level
  663. CollectValues( &srcValues, source );
  664. CollectValues( &trgValues, this );
  665. // now merge the values
  666. pSrcValue = (RValue const *) eSrcValue.OpenFirst( &srcValues );
  667. pTrgValue = (RValue const *) eTrgValue.OpenFirst( &trgValues );
  668. while ( !retval && (pSrcValue || pTrgValue) )
  669. {
  670. if ( !pTrgValue )
  671. {
  672. cmpRc = -1;
  673. }
  674. else if ( !pSrcValue )
  675. {
  676. cmpRc = 1;
  677. }
  678. else
  679. {
  680. cmpRc = UStrICmp( pSrcValue->GetName(), pTrgValue->GetName() );
  681. }
  682. if ( cmpRc < 0 )
  683. { // source value only (copy)
  684. retval = this->ValueSet( pSrcValue->GetName(), pSrcValue->GetValue(),
  685. pSrcValue->GetValueLen(), pSrcValue->GetType() );
  686. pSrcValue = (RValue const *) eSrcValue.Next();
  687. }
  688. else if ( cmpRc > 0 )
  689. { // target value only (delete)
  690. retval = this->ValueDel( pTrgValue->GetName() );
  691. pTrgValue = (RValue const *) eTrgValue.Next();
  692. }
  693. else /* if ( cmpRc == 0 ) */
  694. { // identical value names (replicate)
  695. retval = this->ValueSet( pSrcValue->GetName(), pSrcValue->GetValue(),
  696. pSrcValue->GetValueLen(), pSrcValue->GetType() );
  697. pSrcValue = (RValue const *) eSrcValue.Next();
  698. pTrgValue = (RValue const *) eTrgValue.Next();
  699. }
  700. }
  701. eSrcValue.Close();
  702. eTrgValue.Close();
  703. // handle replication of keys at this level
  704. CollectKeys( &srcKeys, source );
  705. CollectKeys( &trgKeys, this );
  706. // now merge the values
  707. pSrcKey = (RKey const *) eSrcKey.OpenFirst( &srcKeys );
  708. pTrgKey = (RKey const *) eTrgKey.OpenFirst( &trgKeys );
  709. while ( !retval && (pSrcKey || pTrgKey) )
  710. {
  711. if ( !pTrgKey )
  712. {
  713. cmpRc = -1;
  714. }
  715. else if ( !pSrcKey )
  716. {
  717. cmpRc = 1;
  718. }
  719. else
  720. {
  721. cmpRc = UStrICmp( pSrcKey->GetName(), pTrgKey->GetName() );
  722. }
  723. if ( cmpRc < 0 )
  724. { // source key only (copy hive)
  725. retval = srcNest.Open( pSrcKey->GetName(), source );
  726. if ( !retval )
  727. {
  728. retval = trgNest.Create( pSrcKey->GetName(), this );
  729. if ( !retval )
  730. {
  731. retval = trgNest.HiveCopy( &srcNest );
  732. trgNest.Close();
  733. }
  734. srcNest.Close();
  735. }
  736. pSrcKey = (RKey const *) eSrcKey.Next();
  737. }
  738. else if ( cmpRc > 0 )
  739. { // target key only (delete hive)
  740. retval = trgNest.Open( pTrgKey->GetName(), this );
  741. if ( !retval )
  742. {
  743. retval = trgNest.HiveDel();
  744. trgNest.Close();
  745. }
  746. retval = SubKeyDel( pTrgKey->GetName() );
  747. pTrgKey = (RKey const *) eTrgKey.Next();
  748. }
  749. else /* if ( cmpRc == 0 ) */
  750. { // identical keys (replicate hive)
  751. retval = srcNest.Open( pSrcKey->GetName(), source );
  752. if ( !retval )
  753. {
  754. retval = trgNest.Open( pSrcKey->GetName(), this );
  755. if ( !retval )
  756. {
  757. retval = trgNest.HiveReplicate( &srcNest );
  758. trgNest.Close();
  759. }
  760. srcNest.Close();
  761. }
  762. pSrcKey = (RKey const *) eSrcKey.Next();
  763. pTrgKey = (RKey const *) eTrgKey.Next();
  764. }
  765. }
  766. eSrcKey.Close();
  767. eTrgKey.Close();
  768. return retval;
  769. }
  770. // TReg.cpp - end of file