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.

886 lines
20 KiB

  1. /*++
  2. Copyright (c) 1994 Microsoft Corporation
  3. Module Name:
  4. rpcsupp.cxx
  5. Abstract:
  6. This module contains the server side RPC admin APIs
  7. Author:
  8. John Ludeman (johnl) 02-Dec-1994
  9. Project:
  10. Internet Servers Common Server DLL
  11. --*/
  12. //
  13. // Include Headers
  14. //
  15. #include <tcpdllp.hxx>
  16. #include <tsunami.hxx>
  17. extern "C" {
  18. #include <info_srv.h>
  19. };
  20. #include <atq.h>
  21. #include "inetreg.h"
  22. #include <iistypes.hxx>
  23. #include <iiscnfg.h>
  24. #include <imd.h>
  25. #include <inetreg.h>
  26. #include <mb.hxx>
  27. //
  28. // number of capabilities DWORD
  29. //
  30. #define NUM_CAPABILITIES_FLAGS 1
  31. NET_API_STATUS
  32. NET_API_FUNCTION
  33. R_InetInfoGetVersion(
  34. IN LPWSTR pszServer OPTIONAL,
  35. IN DWORD dwReserved,
  36. OUT DWORD * pdwVersion
  37. )
  38. /*++
  39. Description
  40. Returns the version of the TCP server package. Primarily intended to
  41. detect downlevel servers for future versions of the admin tool.
  42. Arguments:
  43. pszServer - unused
  44. dwReserved - unused (may eventually indicate an individual server)
  45. pdwVersion - Receives the major version in the hi-word and the minor
  46. version in the low word
  47. Note:
  48. --*/
  49. {
  50. *pdwVersion = MAKELONG( IIS_VERSION_MAJOR, IIS_VERSION_MINOR );
  51. return NO_ERROR;
  52. } // R_InetInfoGetVersion
  53. NET_API_STATUS
  54. NET_API_FUNCTION
  55. R_InetInfoGetServerCapabilities(
  56. IN LPWSTR pszServer OPTIONAL,
  57. IN DWORD dwReserved,
  58. OUT LPINET_INFO_CAPABILITIES_STRUCT *ppCap
  59. )
  60. /*++
  61. Description
  62. Returns the information about the server and its capabilities.
  63. Arguments:
  64. pszServer - unused
  65. dwReserved - unused (may eventually indicate an individual server)
  66. ppCap - Receives the INET_INFO_CAPABILITIES structure
  67. --*/
  68. {
  69. DWORD err = NO_ERROR;
  70. LPINET_INFO_CAPABILITIES_STRUCT pCap;
  71. IF_DEBUG( DLL_RPC) {
  72. DBGPRINTF( ( DBG_CONTEXT,
  73. " Entering R_InetInfoGetServerCapabilities()\n" ));
  74. }
  75. if ( ( err = TsApiAccessCheck( TCP_QUERY_ADMIN_INFORMATION)) != NO_ERROR) {
  76. IF_DEBUG( DLL_RPC) {
  77. DBGPRINTF( ( DBG_CONTEXT,
  78. " TsApiAccessCheck() Failed. Error = %u\n", err));
  79. }
  80. } else {
  81. OSVERSIONINFO verInfo;
  82. DWORD bufSize =
  83. sizeof(INET_INFO_CAPABILITIES_STRUCT) +
  84. NUM_CAPABILITIES_FLAGS * sizeof(INET_INFO_CAP_FLAGS);
  85. pCap = (LPINET_INFO_CAPABILITIES_STRUCT) MIDL_user_allocate( bufSize );
  86. *ppCap = pCap;
  87. if ( pCap == NULL ) {
  88. return ERROR_NOT_ENOUGH_MEMORY;
  89. }
  90. ZeroMemory(pCap, bufSize);
  91. pCap->CapFlags = (LPINET_INFO_CAP_FLAGS)
  92. ((PCHAR)pCap + sizeof(INET_INFO_CAPABILITIES));
  93. //
  94. // Fill in the version and product type
  95. //
  96. pCap->CapVersion = 1;
  97. switch (IISGetPlatformType()) {
  98. case PtNtServer:
  99. pCap->ProductType = INET_INFO_PRODUCT_NTSERVER;
  100. break;
  101. case PtNtWorkstation:
  102. pCap->ProductType = INET_INFO_PRODUCT_NTWKSTA;
  103. break;
  104. default:
  105. pCap->ProductType = INET_INFO_PRODUCT_UNKNOWN;
  106. }
  107. //
  108. // Fill in GetVersionEx information
  109. //
  110. verInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  111. if ( GetVersionEx( &verInfo ) ) {
  112. pCap->BuildNumber = verInfo.dwBuildNumber;
  113. } else {
  114. pCap->BuildNumber = 0;
  115. }
  116. pCap->MajorVersion = IIS_VERSION_MAJOR;
  117. pCap->MinorVersion = IIS_VERSION_MINOR;
  118. //
  119. // Fill in the capabilities
  120. //
  121. pCap->NumCapFlags = NUM_CAPABILITIES_FLAGS;
  122. pCap->CapFlags[0].Mask = IIS_CAP1_ALL;
  123. if ( pCap->ProductType == INET_INFO_PRODUCT_NTSERVER ) {
  124. //
  125. // For downlevel purposes, we take out the multi-instance and virtual
  126. // sever support since the downlevel version of the RPC api
  127. // doesn't support those concepts.
  128. //
  129. pCap->CapFlags[0].Flag = (IIS_CAP1_NTS &
  130. ~(IIS_CAP1_MULTIPLE_INSTANCE | IIS_CAP1_VIRTUAL_SERVER));
  131. } else {
  132. pCap->CapFlags[0].Flag = IIS_CAP1_NTW;
  133. }
  134. }
  135. return ( err );
  136. } // R_InetInfoGetServerCapabilities
  137. NET_API_STATUS
  138. NET_API_FUNCTION
  139. R_InetInfoSetGlobalAdminInformation(
  140. IN LPWSTR pszServer OPTIONAL,
  141. IN DWORD dwReserved,
  142. IN INETA_GLOBAL_CONFIG_INFO * pConfig
  143. )
  144. /*++
  145. Description
  146. Sets the global service admin information
  147. Arguments:
  148. pszServer - unused
  149. dwReserved
  150. pConfig - Admin information to set
  151. Note:
  152. --*/
  153. {
  154. DWORD err;
  155. HKEY hkey = NULL;
  156. HKEY CacheKey = NULL;
  157. HKEY FilterKey = NULL;
  158. DWORD dwDummy;
  159. if ( ( err = TsApiAccessCheck( TCP_SET_ADMIN_INFORMATION)) != NO_ERROR) {
  160. IF_DEBUG( DLL_RPC) {
  161. DBGPRINTF( ( DBG_CONTEXT,
  162. " TsApiAccessCheck() Failed. Error = %u\n", err));
  163. }
  164. return err;
  165. }
  166. err = ERROR_NOT_SUPPORTED;
  167. return err;
  168. } // R_InetInfoSetGlobalAdminInformation
  169. NET_API_STATUS
  170. NET_API_FUNCTION
  171. R_InetInfoGetGlobalAdminInformation(
  172. IN LPWSTR pszServer OPTIONAL,
  173. IN DWORD dwReserved,
  174. OUT LPINETA_GLOBAL_CONFIG_INFO * ppConfig
  175. )
  176. /*++
  177. Description
  178. Gets the global service admin information
  179. Arguments:
  180. pszServer - unused
  181. dwReserved
  182. ppConfig - Receives current operating values of the server
  183. Note:
  184. --*/
  185. {
  186. DWORD err = NO_ERROR;
  187. INETA_GLOBAL_CONFIG_INFO * pConfig;
  188. IF_DEBUG( DLL_RPC) {
  189. DBGPRINTF( ( DBG_CONTEXT,
  190. " Entering R_InetInfoGetGlobalAdminInformation()\n" ));
  191. }
  192. if ( ( err = TsApiAccessCheck( TCP_QUERY_ADMIN_INFORMATION)) != NO_ERROR) {
  193. IF_DEBUG( DLL_RPC) {
  194. DBGPRINTF( ( DBG_CONTEXT,
  195. " TsApiAccessCheck() Failed. Error = %u\n", err));
  196. }
  197. } else {
  198. *ppConfig = (INETA_GLOBAL_CONFIG_INFO *) MIDL_user_allocate(
  199. sizeof( INET_INFO_GLOBAL_CONFIG_INFO ));
  200. if ( !*ppConfig ) {
  201. return ERROR_NOT_ENOUGH_MEMORY;
  202. }
  203. pConfig = *ppConfig;
  204. memset( pConfig, 0, sizeof( *pConfig ));
  205. pConfig->FieldControl = FC_GINET_INFO_ALL;
  206. pConfig->cbMemoryCacheSize = 0;
  207. pConfig->BandwidthLevel = (DWORD)AtqGetInfo( AtqBandwidthThrottle);
  208. if( err != NO_ERROR ) {
  209. //
  210. // clean up the allocated memory
  211. //
  212. MIDL_user_free( pConfig );
  213. } else {
  214. *ppConfig = pConfig;
  215. }
  216. }
  217. IF_DEBUG( DLL_RPC) {
  218. DBGPRINTF(( DBG_CONTEXT,
  219. "R_InetInfoGetGlobalAdminInformation() returns Error = %u \n",
  220. err ));
  221. }
  222. return ( err );
  223. } // R_InetInfoGetGlobalAdminInformation()
  224. NET_API_STATUS
  225. NET_API_FUNCTION
  226. R_InetInfoSetAdminInformation(
  227. IN LPWSTR pszServer OPTIONAL,
  228. IN DWORD dwServerMask,
  229. IN INETA_CONFIG_INFO * pConfig
  230. )
  231. /*++
  232. Description
  233. Sets the common service admin information for the servers specified
  234. in dwServerMask.
  235. Arguments:
  236. pszServer - unused
  237. dwServerMask - Bitfield of servers to set the information for
  238. pConfig - Admin information to set
  239. Note:
  240. --*/
  241. {
  242. DWORD err;
  243. LPINET_INFO_VIRTUAL_ROOT_LIST rootList = NULL;
  244. IF_DEBUG( DLL_RPC) {
  245. DBGPRINTF( ( DBG_CONTEXT,
  246. " Entering R_InetInfoSetAdminInformation. Mask %x\n",
  247. dwServerMask));
  248. }
  249. //
  250. // Do we have permissions?
  251. //
  252. if ( (err = TsApiAccessCheck( TCP_SET_ADMIN_INFORMATION )) != NO_ERROR) {
  253. IF_DEBUG( DLL_RPC) {
  254. DBGPRINTF( ( DBG_CONTEXT,
  255. " TsApiAccessCheck() Failed. Error = %u\n", err));
  256. }
  257. return(err);
  258. }
  259. //
  260. // Loop through the services and set the information for each one
  261. //
  262. if ( !IIS_SERVICE::SetServiceAdminInfo(
  263. 1,
  264. dwServerMask,
  265. 1, // Instance - may be overidden with downlevel instance
  266. TRUE, // common config
  267. pConfig
  268. )) {
  269. err = GetLastError();
  270. IF_DEBUG( DLL_RPC) {
  271. DBGPRINTF( ( DBG_CONTEXT,
  272. "SetServiceAdminInfo failed. Error = %u\n",
  273. err));
  274. }
  275. }
  276. IF_DEBUG( DLL_RPC) {
  277. DBGPRINTF( ( DBG_CONTEXT,
  278. " Leaving R_InetInfoSetAdminInformation. Err = %d\n",
  279. err ));
  280. }
  281. return(err);
  282. } // R_InetInfoSetAdminInformation
  283. NET_API_STATUS
  284. NET_API_FUNCTION
  285. R_InetInfoGetAdminInformation(
  286. IN LPWSTR pszServer OPTIONAL,
  287. IN DWORD dwServerMask,
  288. OUT LPINETA_CONFIG_INFO * ppConfig
  289. )
  290. /*++
  291. Description
  292. Gets the common service admin information for the specified
  293. server in dwServerMask.
  294. Arguments:
  295. pszServer - unused
  296. dwServerMask - Bitfield of server to get the information for
  297. pConfig - Receives current operating values of the server
  298. Note:
  299. --*/
  300. {
  301. DWORD err = NO_ERROR;
  302. DWORD nEntries = 0;
  303. PCHAR buffer = NULL;
  304. DWORD nRead = 0;
  305. IF_DEBUG( DLL_RPC) {
  306. DBGPRINTF( ( DBG_CONTEXT,
  307. " Entering R_InetInfoGetAdminInformation.\n"));
  308. }
  309. *ppConfig = NULL;
  310. //
  311. // Call the new API
  312. //
  313. IF_DEBUG( DLL_RPC) {
  314. DBGPRINTF( ( DBG_CONTEXT,
  315. " Entering R_InetInfoGetAdminInformation. Mask %x\n",
  316. dwServerMask));
  317. }
  318. if ( !IIS_SERVICE::GetServiceAdminInfo(
  319. 1,
  320. dwServerMask,
  321. 1, // Instance - my get overidden by downlevel instance
  322. TRUE, // common config
  323. &nRead,
  324. ppConfig
  325. )) {
  326. DBG_ASSERT(buffer == NULL);
  327. DBG_ASSERT(nRead == 0);
  328. err = GetLastError();
  329. DBG_ASSERT(err != NO_ERROR);
  330. IF_DEBUG( DLL_RPC) {
  331. DBGPRINTF( ( DBG_CONTEXT,
  332. "GetServiceAdminInfo failed. Error = %u\n",
  333. err));
  334. }
  335. } else {
  336. DBG_ASSERT(nRead == 1);
  337. }
  338. IF_DEBUG( DLL_RPC) {
  339. DBGPRINTF( ( DBG_CONTEXT,
  340. " Leaving R_InetInfoGetAdminInformation. Err = %d\n",
  341. err ));
  342. }
  343. return(err);
  344. } // R_InetInfoGetAdminInformation()
  345. NET_API_STATUS
  346. NET_API_FUNCTION
  347. R_InetInfoGetSites(
  348. IN LPWSTR pszServer OPTIONAL,
  349. IN DWORD dwServerMask,
  350. OUT LPINET_INFO_SITE_LIST * ppSites
  351. )
  352. /*++
  353. Description
  354. Gets the list of instances for the specified
  355. server in dwServerMask.
  356. Arguments:
  357. pszServer - unused
  358. dwServerMask - Bitfield of server to get the information for
  359. ppSites - Receives current site list
  360. Note:
  361. --*/
  362. {
  363. BOOL fRet = FALSE;
  364. DWORD err = NO_ERROR;
  365. IF_DEBUG( DLL_RPC) {
  366. DBGPRINTF( ( DBG_CONTEXT,
  367. " Entering R_InetInfoGetSites.\n"));
  368. }
  369. *ppSites = NULL;
  370. //
  371. // Call the new API
  372. //
  373. IF_DEBUG( DLL_RPC) {
  374. DBGPRINTF( ( DBG_CONTEXT,
  375. " Entering R_InetInfoGetSites. Mask %x\n",
  376. dwServerMask));
  377. }
  378. fRet = IIS_SERVICE::GetServiceSiteInfo (
  379. dwServerMask,
  380. ppSites
  381. );
  382. if (!fRet) {
  383. err = GetLastError();
  384. DBG_ASSERT(err != NO_ERROR);
  385. IF_DEBUG( DLL_RPC) {
  386. DBGPRINTF( ( DBG_CONTEXT,
  387. "GetServiceSiteInfo failed. Error = %u\n",
  388. err));
  389. }
  390. }
  391. IF_DEBUG( DLL_RPC) {
  392. DBGPRINTF( ( DBG_CONTEXT,
  393. " Leaving R_InetInfoGetSiteInformation. Err = %d\n",
  394. err ));
  395. }
  396. return(err);
  397. } // R_InetInfoGetSiteInformation()
  398. NET_API_STATUS
  399. NET_API_FUNCTION
  400. R_InetInfoQueryStatistics(
  401. IN LPWSTR pszServer OPTIONAL,
  402. IN DWORD Level,
  403. IN DWORD dwServerMask,
  404. LPINET_INFO_STATISTICS_INFO StatsInfo
  405. )
  406. {
  407. DWORD err;
  408. err = TsApiAccessCheck( TCP_QUERY_STATISTICS );
  409. if ( err ) {
  410. return err;
  411. }
  412. switch ( Level ) {
  413. case 0:
  414. {
  415. INET_INFO_STATISTICS_0 * pstats0;
  416. ATQ_STATISTICS atqStats;
  417. pstats0 = (INET_INFO_STATISTICS_0 *) MIDL_user_allocate(
  418. sizeof( INET_INFO_STATISTICS_0 ));
  419. if ( !pstats0 ) {
  420. return ERROR_NOT_ENOUGH_MEMORY;
  421. }
  422. #ifndef NO_AUX_PERF
  423. // init count of counters that are valid
  424. pstats0->nAuxCounters = 0;
  425. //
  426. // IF THERE ARE VALID UNNAMED COUNTERS THAT WE WISH TO TRACK
  427. // WE SHOULD DO SO HERE........
  428. // For Future Additions, this comment is added.
  429. // MuraliK 20-Sept-1995
  430. //
  431. #endif // NO_AUX_PERF
  432. if ( !TsCacheQueryStatistics( Level,
  433. dwServerMask,
  434. &pstats0->CacheCtrs ) ||
  435. !AtqGetStatistics( &atqStats))
  436. {
  437. MIDL_user_free( pstats0 );
  438. err = GetLastError();
  439. } else {
  440. // copy Atq Statistics to stats
  441. INETA_ATQ_STATISTICS * pAtqStats = &pstats0->AtqCtrs;
  442. pAtqStats->TotalBlockedRequests = atqStats.cBlockedRequests;
  443. pAtqStats->TotalAllowedRequests = atqStats.cAllowedRequests;
  444. pAtqStats->TotalRejectedRequests = atqStats.cRejectedRequests;
  445. pAtqStats->CurrentBlockedRequests=
  446. atqStats.cCurrentBlockedRequests;
  447. pAtqStats->MeasuredBandwidth = atqStats.MeasuredBandwidth;
  448. StatsInfo->InetStats0 = pstats0;
  449. }
  450. }
  451. break;
  452. default:
  453. err = ERROR_INVALID_LEVEL;
  454. break;
  455. }
  456. return err;
  457. }
  458. NET_API_STATUS
  459. NET_API_FUNCTION
  460. R_InetInfoClearStatistics(
  461. IN LPWSTR pszServer OPTIONAL,
  462. IN DWORD dwServerMask
  463. )
  464. {
  465. DWORD err;
  466. err = TsApiAccessCheck( TCP_SET_ADMIN_INFORMATION );
  467. if ( err == NO_ERROR) {
  468. if (!TsCacheClearStatistics( dwServerMask ) ||
  469. !AtqClearStatistics()) {
  470. err = GetLastError();
  471. }
  472. }
  473. return err;
  474. } // R_InetInfoClearStatistics
  475. NET_API_STATUS
  476. NET_API_FUNCTION
  477. R_InetInfoFlushMemoryCache(
  478. IN LPWSTR pszServer OPTIONAL,
  479. IN DWORD dwServerMask
  480. )
  481. {
  482. DWORD err;
  483. err = TsApiAccessCheck( TCP_SET_ADMIN_INFORMATION );
  484. if ( err ) {
  485. return err;
  486. }
  487. if ( !TsCacheFlush( dwServerMask )) {
  488. return GetLastError();
  489. }
  490. return NO_ERROR;
  491. }
  492. BOOL
  493. ReadRegString(
  494. HKEY hkey,
  495. CHAR * * ppchstr,
  496. LPCSTR pchValue,
  497. LPCSTR pchDefault
  498. )
  499. /*++
  500. Description
  501. Gets the specified string from the registry. If *ppchstr is not NULL,
  502. then the value is freed. If the registry call fails, *ppchstr is
  503. restored to its previous value.
  504. Arguments:
  505. hkey - Handle to open key
  506. ppchstr - Receives pointer of allocated memory of the new value of the
  507. string
  508. pchValue - Which registry value to retrieve
  509. pchDefault - Default string if value isn't found
  510. --*/
  511. {
  512. CHAR * pch = *ppchstr;
  513. *ppchstr = ReadRegistryString( hkey,
  514. pchValue,
  515. pchDefault,
  516. TRUE );
  517. if ( !*ppchstr ) {
  518. *ppchstr = pch;
  519. return FALSE;
  520. }
  521. if ( pch ) {
  522. TCP_FREE( pch );
  523. }
  524. return TRUE;
  525. } // ReadRegString
  526. BOOL
  527. ConvertStringToRpc(
  528. WCHAR * * ppwch,
  529. LPCSTR pch
  530. )
  531. /*++
  532. Description
  533. Allocates, copies and converts pch to *ppwch
  534. Arguments:
  535. ppwch - Receives allocated destination string
  536. pch - ANSI string to copy from
  537. --*/
  538. {
  539. int cch;
  540. int iRet;
  541. if ( !pch ) {
  542. *ppwch = NULL;
  543. return TRUE;
  544. }
  545. cch = strlen( pch );
  546. if ( !(*ppwch = (WCHAR *) MIDL_user_allocate( (cch + 1) * sizeof(WCHAR))) )
  547. {
  548. SetLastError( ERROR_NOT_ENOUGH_MEMORY );
  549. return FALSE;
  550. }
  551. iRet = MultiByteToWideChar( CP_ACP,
  552. MB_PRECOMPOSED,
  553. pch,
  554. cch + 1,
  555. *ppwch,
  556. cch + 1 );
  557. if ( !iRet ) {
  558. MIDL_user_free( *ppwch );
  559. return FALSE;
  560. }
  561. return TRUE;
  562. } // ConvertStringToRpc
  563. VOID
  564. FreeRpcString(
  565. WCHAR * pwch
  566. )
  567. {
  568. if ( pwch ) {
  569. MIDL_user_free( pwch );
  570. }
  571. } // FreeRpcString
  572. DWORD
  573. InitGlobalConfigFromReg(
  574. VOID
  575. )
  576. /*++
  577. Loads the global configuration parameters from registry.
  578. Should be called after Atq Module is initialized.
  579. Returns:
  580. Win32 error code. NO_ERROR on success
  581. --*/
  582. {
  583. DWORD dwError;
  584. HKEY hkey = NULL;
  585. DWORD dwVal;
  586. MB mb( (IMDCOM*) IIS_SERVICE::QueryMDObject() );
  587. dwError = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  588. INET_INFO_PARAMETERS_KEY,
  589. 0,
  590. KEY_ALL_ACCESS,
  591. &hkey);
  592. if ( dwError == NO_ERROR) {
  593. DWORD dwChangeNumber;
  594. // See if we need to migrate the bandwidth to the
  595. // metabase.
  596. if (!mb.GetSystemChangeNumber(&dwChangeNumber) ||
  597. dwChangeNumber == 0)
  598. {
  599. if (!mb.Open( "/lm",
  600. METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE ))
  601. {
  602. return GetLastError();
  603. }
  604. dwVal = ReadRegistryDword( hkey,
  605. INETA_BANDWIDTH_LEVEL,
  606. INETA_DEF_BANDWIDTH_LEVEL);
  607. mb.SetDword("", MD_MAX_BANDWIDTH, IIS_MD_UT_SERVER, dwVal);
  608. mb.Close();
  609. }
  610. }
  611. if (mb.Open("/lm", METADATA_PERMISSION_READ))
  612. {
  613. if ( mb.GetDword("", MD_MAX_BANDWIDTH_BLOCKED, IIS_MD_UT_SERVER, &dwVal ))
  614. {
  615. AtqSetInfo( AtqBandwidthThrottleMaxBlocked, (ULONG_PTR)dwVal );
  616. }
  617. if (!mb.GetDword("", MD_MAX_BANDWIDTH, IIS_MD_UT_SERVER, &dwVal))
  618. {
  619. DBGPRINTF( ( DBG_CONTEXT, "Could not read MD_MAX_BANDWIDTH\n" ) );
  620. dwVal = INETA_DEF_BANDWIDTH_LEVEL;
  621. }
  622. }
  623. else
  624. {
  625. DBGPRINTF( ( DBG_CONTEXT, "Couldn't open; error=%d\n", GetLastError() ) );
  626. dwVal = INETA_DEF_BANDWIDTH_LEVEL;
  627. }
  628. DBGPRINTF( ( DBG_CONTEXT,
  629. " Setting Global throttle value to %d\n", dwVal ));
  630. AtqSetInfo( AtqBandwidthThrottle, (ULONG_PTR)dwVal);
  631. if ( hkey ) {
  632. RegCloseKey( hkey );
  633. }
  634. return NO_ERROR;
  635. } // InitGlobalConfigFromReg()