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.

765 lines
21 KiB

  1. /*++
  2. Copyright (C) Microsoft Corporation, 1996 - 1997
  3. All rights reserved.
  4. Module Name:
  5. compinfo.hxx
  6. Abstract:
  7. Local and remote computer information detection.
  8. Author:
  9. 10/17/95 <adamk> created.
  10. Steve Kiraly (SteveKi) 21-Jan-1996 used for downlevel server detection
  11. Revision History:
  12. --*/
  13. #include "precomp.hxx"
  14. #pragma hdrstop
  15. #include "compinfo.hxx"
  16. #include "persist.hxx"
  17. TCHAR const PROCESSOR_ARCHITECTURE_NAME_INTEL[] = TEXT("Intel");
  18. TCHAR const PROCESSOR_ARCHITECTURE_NAME_UNKNOWN[] = TEXT("(unknown)");
  19. TCHAR const ENVIRONMENT_IA64[] = TEXT("Windows IA64");
  20. TCHAR const ENVIRONMENT_INTEL[] = TEXT("Windows NT x86");
  21. TCHAR const ENVIRONMENT_WINDOWS[] = TEXT("Windows 4.0");
  22. TCHAR const ENVIRONMENT_UNKNOWN[] = TEXT("(unknown)");
  23. TCHAR const ENVIRONMENT_NATIVE[] = TEXT("");
  24. TCHAR const c_szProductOptionsPath[] = TEXT( "System\\CurrentControlSet\\Control\\ProductOptions" );
  25. TCHAR const c_szProductOptions[] = TEXT( "ProductType" );
  26. TCHAR const c_szWorkstation[] = TEXT( "WINNT" );
  27. TCHAR const c_szServer1[] = TEXT( "SERVERNT" );
  28. TCHAR const c_szServer2[] = TEXT( "LANMANNT" );
  29. TCHAR const c_szNetApi32Dll[] = TEXT( "netapi32.dll" );
  30. CHAR const c_szNetServerGetInfo[] = "NetServerGetInfo";
  31. CHAR const c_szNetApiBufferFree[] = "NetApiBufferFree";
  32. CComputerInfo::
  33. CComputerInfo(
  34. IN LPCTSTR pComputerName
  35. ) : ComputerName( pComputerName),
  36. ProductOption( kNtUnknown ),
  37. OSIsDebugVersion( FALSE ),
  38. ProcessorArchitecture( 0 ),
  39. ProcessorCount( 0 )
  40. {
  41. DBGMSG( DBG_TRACE, ( "CComputerInfo::ctor\n" ) );
  42. memset( &OSInfo, 0, sizeof( OSInfo ) );
  43. }
  44. CComputerInfo::~CComputerInfo()
  45. {
  46. DBGMSG( DBG_TRACE, ( "CComputerInfo::dtor\n" ) );
  47. }
  48. LPCTSTR CComputerInfo::GetProcessorArchitectureName() const
  49. {
  50. SPLASSERT(IsInfoValid());
  51. switch (ProcessorArchitecture)
  52. {
  53. case PROCESSOR_ARCHITECTURE_INTEL:
  54. {
  55. return PROCESSOR_ARCHITECTURE_NAME_INTEL;
  56. }
  57. default:
  58. {
  59. return PROCESSOR_ARCHITECTURE_NAME_UNKNOWN;
  60. }
  61. }
  62. }
  63. LPCTSTR CComputerInfo::GetProcessorArchitectureDirectoryName() const
  64. {
  65. SPLASSERT(IsInfoValid());
  66. switch (ProcessorArchitecture)
  67. {
  68. case PROCESSOR_ARCHITECTURE_INTEL:
  69. {
  70. return TEXT("i386");
  71. }
  72. default:
  73. {
  74. return PROCESSOR_ARCHITECTURE_NAME_UNKNOWN;
  75. }
  76. }
  77. }
  78. LPCTSTR CComputerInfo::GetNativeEnvironment() const
  79. {
  80. SPLASSERT(IsInfoValid());
  81. switch (ProcessorArchitecture)
  82. {
  83. case PROCESSOR_ARCHITECTURE_INTEL:
  84. {
  85. if (IsRunningWindows95())
  86. {
  87. return ENVIRONMENT_WINDOWS;
  88. }
  89. else
  90. {
  91. return ENVIRONMENT_INTEL;
  92. }
  93. }
  94. default:
  95. {
  96. SPLASSERT(FALSE);
  97. return ENVIRONMENT_UNKNOWN;
  98. }
  99. }
  100. }
  101. BOOL CComputerInfo::IsInfoValid() const
  102. {
  103. // if OSInfo.dwOSVersionInfoSize is not zero, then the info has been retrieved
  104. return (BOOL) (OSInfo.dwOSVersionInfoSize != 0);
  105. }
  106. BOOL CComputerInfo::IsRunningWindowsNT() const
  107. {
  108. return (OSInfo.dwPlatformId & VER_PLATFORM_WIN32_NT);
  109. }
  110. BOOL CComputerInfo::IsRunningWindows95() const
  111. {
  112. return (OSInfo.dwPlatformId & VER_PLATFORM_WIN32_WINDOWS);
  113. }
  114. DWORD CComputerInfo::GetOSBuildNumber() const
  115. {
  116. // Build number is the low word of dwBuildNumber
  117. return (OSInfo.dwBuildNumber & 0xFFFF);
  118. }
  119. WORD CComputerInfo::GetProcessorArchitecture() const
  120. {
  121. return ProcessorArchitecture;
  122. }
  123. DWORD CComputerInfo::GetSpoolerVersion() const
  124. {
  125. DWORD BuildNumber = GetOSBuildNumber();
  126. DWORD SpoolerVersion;
  127. // Windows NT 4.0 (and beyond)
  128. if (BuildNumber > 1057)
  129. {
  130. SpoolerVersion = 2;
  131. }
  132. // Windows NT 3.5 and 3.51
  133. else if (BuildNumber > 511)
  134. {
  135. SpoolerVersion = 1;
  136. }
  137. // Windows NT 3.1
  138. else
  139. {
  140. SpoolerVersion = 0;
  141. }
  142. return SpoolerVersion;
  143. }
  144. BOOL CComputerInfo::GetInfo()
  145. {
  146. // NOTE: OSInfo.dwOSVersionInfoSize must be non-zero after the info is retrieved.
  147. DWORD ErrorCode = ERROR_SUCCESS;
  148. LPTSTR pCPUName = NULL;
  149. LPTSTR pBuildNumberText = NULL;
  150. LPTSTR pVersionText = NULL;
  151. LPTSTR pCSDVersionText = NULL;
  152. LPTSTR pOSTypeText = NULL;
  153. // set size of version info structure
  154. OSInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  155. if( ComputerName.bEmpty() )
  156. {
  157. SYSTEM_INFO SystemInfo;
  158. // get operating system info
  159. if (!GetVersionEx(&OSInfo))
  160. {
  161. ErrorCode = GetLastError();
  162. goto CleanUp;
  163. }
  164. // get hardware info
  165. GetSystemInfo(&SystemInfo);
  166. ProcessorArchitecture = SystemInfo.wProcessorArchitecture;
  167. ProcessorCount = SystemInfo.dwNumberOfProcessors;
  168. }
  169. else
  170. {
  171. REGISTRY_KEY_INFO RegistryKeyInfo;
  172. // determine operating system
  173. // if this key is found, then the OS is Windows NT
  174. // if this key cannot be found, then the OS is Windows 95
  175. // otherwise, it is an error
  176. pBuildNumberText = AllocateRegistryString(ComputerName, HKEY_LOCAL_MACHINE,
  177. TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"),
  178. TEXT("CurrentBuildNumber"));
  179. if (GetLastError() == ERROR_CANTOPEN)
  180. {
  181. // operating system is Windows 95
  182. OSInfo.dwPlatformId = VER_PLATFORM_WIN32_WINDOWS;
  183. // get OS version
  184. pVersionText = AllocateRegistryString(ComputerName, HKEY_LOCAL_MACHINE,
  185. TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion"),
  186. TEXT("Version"));
  187. if (GetLastError() != ERROR_SUCCESS)
  188. {
  189. ErrorCode = GetLastError();
  190. goto CleanUp;
  191. }
  192. // parse OS version
  193. OSInfo.dwMajorVersion = 0;
  194. OSInfo.dwMinorVersion = 0;
  195. OSInfo.dwBuildNumber = 0;
  196. //
  197. // The version string is of the form "X.X" The following
  198. // code is used to isolate the major and minor verison
  199. // to place in dwords respectivly.
  200. //
  201. LPTSTR p;
  202. OSInfo.dwMajorVersion = _tcstoul( pVersionText, &p, 10);
  203. //
  204. // We know the conversion will stop at the '.' if it did
  205. // skip past the '.' and do the minor version conversion.
  206. //
  207. if( *p == TEXT('.') )
  208. {
  209. p++;
  210. OSInfo.dwMinorVersion = _tcstoul(p, &p, 10);
  211. }
  212. //
  213. // We know the conversion will stop at the '.' if it did
  214. // skip past the '.' and do the build number conversion.
  215. //
  216. if( *p == TEXT('.') )
  217. {
  218. p++;
  219. OSInfo.dwBuildNumber = _tcstoul(p, NULL, 10);
  220. }
  221. // get CSD version
  222. OSInfo.szCSDVersion[0] = TEXT('\0');
  223. // processor must be Intel
  224. ProcessorArchitecture = PROCESSOR_ARCHITECTURE_INTEL;
  225. // processor count must be 1
  226. ProcessorCount = 1;
  227. }
  228. else if (GetLastError() == ERROR_SUCCESS)
  229. {
  230. // operating system is Windows NT
  231. OSInfo.dwPlatformId = VER_PLATFORM_WIN32_NT;
  232. // parse build number (which was just retrieved)
  233. OSInfo.dwBuildNumber = _tcstoul(pBuildNumberText, NULL, 10);
  234. // get OS version
  235. pVersionText = AllocateRegistryString(ComputerName, HKEY_LOCAL_MACHINE,
  236. TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"),
  237. TEXT("CurrentVersion"));
  238. if (GetLastError() != ERROR_SUCCESS)
  239. {
  240. ErrorCode = GetLastError();
  241. goto CleanUp;
  242. }
  243. //
  244. // parse OS version
  245. //
  246. OSInfo.dwMajorVersion = 0;
  247. OSInfo.dwMinorVersion = 0;
  248. //
  249. // The version string is of the form "X.X" The following
  250. // code is used to isolate the major and minor verison
  251. // to place in dwords respectivly.
  252. //
  253. LPTSTR p;
  254. OSInfo.dwMajorVersion = _tcstoul( pVersionText, &p, 10);
  255. //
  256. // We know the conversion will stop at the '.' if it did
  257. // skip past the '.' and do the minor version conversion.
  258. //
  259. if( *p == TEXT('.') )
  260. {
  261. p++;
  262. OSInfo.dwMinorVersion = _tcstoul(p, NULL, 10);
  263. }
  264. // get CSD version
  265. OSInfo.szCSDVersion[0] = TEXT('\0');
  266. pCSDVersionText = AllocateRegistryString(ComputerName, HKEY_LOCAL_MACHINE,
  267. TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"),
  268. TEXT("CSDVersion"));
  269. if (GetLastError() == ERROR_SUCCESS)
  270. {
  271. StringCchCopy(OSInfo.szCSDVersion, ARRAYSIZE(OSInfo.szCSDVersion), pCSDVersionText);
  272. }
  273. // get name of cpu
  274. pCPUName = AllocateRegistryString(ComputerName, HKEY_LOCAL_MACHINE,
  275. TEXT("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0"),
  276. TEXT("Identifier"));
  277. if (GetLastError() != ERROR_SUCCESS)
  278. {
  279. ErrorCode = GetLastError();
  280. goto CleanUp;
  281. }
  282. // determine processor architecture from cpu name
  283. if (!_tcsnicmp(pCPUName, TEXT("80386-"), 6)
  284. || !_tcsnicmp(pCPUName, TEXT("80486-"), 6)
  285. || !_tcsnicmp(pCPUName, TEXT("x86 "), 4))
  286. {
  287. ProcessorArchitecture = PROCESSOR_ARCHITECTURE_INTEL;
  288. }
  289. else if(!_tcsnicmp(pCPUName, TEXT("IA64"), 4))
  290. {
  291. ProcessorArchitecture = PROCESSOR_ARCHITECTURE_IA64;
  292. }
  293. else
  294. {
  295. SPLASSERT(FALSE);
  296. ProcessorArchitecture = 0;
  297. }
  298. // get processor count
  299. // On Windows NT, this can be determined by the number of subkeys of the following
  300. // registry key.
  301. if (!GetRegistryKeyInfo(ComputerName, HKEY_LOCAL_MACHINE,
  302. TEXT("HARDWARE\\DESCRIPTION\\System\\CentralProcessor"),
  303. &RegistryKeyInfo))
  304. {
  305. ErrorCode = GetLastError();
  306. goto CleanUp;
  307. }
  308. ProcessorCount = RegistryKeyInfo.NumSubKeys;
  309. }
  310. else
  311. {
  312. ErrorCode = GetLastError();
  313. goto CleanUp;
  314. }
  315. }
  316. // determine whether OS is retail or debug
  317. OSIsDebugVersion = FALSE;
  318. if (OSInfo.dwPlatformId == VER_PLATFORM_WIN32_NT)
  319. {
  320. // get OS type
  321. pOSTypeText = AllocateRegistryString(ComputerName,
  322. HKEY_LOCAL_MACHINE,
  323. TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"),
  324. TEXT("CurrentType"));
  325. if (GetLastError() != ERROR_SUCCESS)
  326. {
  327. ErrorCode = GetLastError();
  328. goto CleanUp;
  329. }
  330. // if the type text contains the word "Checked",
  331. // then it is a debug build
  332. OSIsDebugVersion = (_tcsstr(pOSTypeText, TEXT("Checked")) != NULL);
  333. }
  334. CleanUp:
  335. if (pCPUName)
  336. {
  337. GlobalFree(pCPUName);
  338. }
  339. if (pBuildNumberText)
  340. {
  341. GlobalFree(pBuildNumberText);
  342. }
  343. if (pVersionText)
  344. {
  345. GlobalFree(pVersionText);
  346. }
  347. if (pCSDVersionText)
  348. {
  349. GlobalFree(pCSDVersionText);
  350. }
  351. if (pOSTypeText)
  352. {
  353. GlobalFree(pOSTypeText);
  354. }
  355. if (ErrorCode)
  356. {
  357. SetLastError(ErrorCode);
  358. return FALSE;
  359. }
  360. else
  361. {
  362. return TRUE;
  363. }
  364. }
  365. BOOL
  366. CComputerInfo::
  367. GetProductInfo(
  368. VOID
  369. )
  370. {
  371. //
  372. // If the request is for the local machine, do not access the net.
  373. //
  374. if( ComputerName.bEmpty() )
  375. {
  376. ProductOption = GetLocalProductInfo();
  377. }
  378. else
  379. {
  380. ProductOption = GetRemoteProductInfo();
  381. }
  382. DBGMSG( DBG_TRACE, ("CComputerInfo::GetPoductInfo %d\n", ProductOption ) );
  383. return ProductOption != kNtUnknown;
  384. }
  385. BOOL
  386. CComputerInfo::
  387. IsRunningNtServer(
  388. VOID
  389. ) const
  390. {
  391. return ProductOption == kNtServer;
  392. }
  393. BOOL
  394. CComputerInfo::
  395. IsRunningNtWorkstation(
  396. VOID
  397. ) const
  398. {
  399. return ProductOption == kNtWorkstation;
  400. }
  401. CComputerInfo::ProductType
  402. CComputerInfo::
  403. GetLocalProductInfo(
  404. VOID
  405. )
  406. {
  407. TStatusB bStatus;
  408. TString strProduct;
  409. ProductType Option = kNtUnknown;
  410. TPersist Product( c_szProductOptionsPath, TPersist::kOpen|TPersist::kRead, HKEY_LOCAL_MACHINE );
  411. bStatus DBGCHK = VALID_OBJ( Product );
  412. if( bStatus )
  413. {
  414. bStatus DBGCHK = Product.bRead( c_szProductOptions, strProduct );
  415. if( bStatus )
  416. {
  417. if( !_tcsicmp( c_szWorkstation, strProduct ) )
  418. {
  419. Option = kNtWorkstation;
  420. }
  421. else if( !_tcsicmp( c_szServer1, strProduct ) || !_tcsicmp( c_szServer2, strProduct ) )
  422. {
  423. Option = kNtServer;
  424. }
  425. else
  426. {
  427. Option = kNtUnknown;
  428. }
  429. }
  430. }
  431. return Option;
  432. }
  433. CComputerInfo::ProductType
  434. CComputerInfo::
  435. GetRemoteProductInfo(
  436. VOID
  437. )
  438. {
  439. ProductType Option = kNtUnknown;
  440. TLibrary Lib( c_szNetApi32Dll );
  441. if( VALID_OBJ( Lib ) )
  442. {
  443. typedef NET_API_STATUS (*pf_NetServerGetInfo)(LPCTSTR servername,DWORD level,LPBYTE *bufptr);
  444. typedef NET_API_STATUS (*pf_NetApiBufferFree)(LPVOID Buffer);
  445. pf_NetServerGetInfo pfNetServerGetInfo = (pf_NetServerGetInfo)Lib.pfnGetProc( c_szNetServerGetInfo );
  446. pf_NetApiBufferFree pfNetApiBufferFree = (pf_NetApiBufferFree)Lib.pfnGetProc( c_szNetApiBufferFree );
  447. PSERVER_INFO_101 si101 = NULL;
  448. if( pfNetServerGetInfo && pfNetApiBufferFree )
  449. {
  450. //
  451. // Get the server info
  452. //
  453. if( pfNetServerGetInfo( ComputerName, 101, (LPBYTE *)&si101 ) == NERR_Success )
  454. {
  455. DBGMSG( DBG_TRACE, ("Server_Info_101.sv101_type %x\n", si101->sv101_type ) );
  456. DWORD dwType = si101->sv101_type;
  457. //
  458. // If the server type is NT and a server.
  459. //
  460. if( dwType & ( SV_TYPE_SERVER_NT | SV_TYPE_DOMAIN_CTRL | SV_TYPE_DOMAIN_BAKCTRL ) )
  461. {
  462. Option = kNtServer;
  463. }
  464. //
  465. // If the server type is NT and a workstation.
  466. //
  467. else if( (dwType & ( SV_TYPE_NT | SV_TYPE_WORKSTATION )) == ( SV_TYPE_NT | SV_TYPE_WORKSTATION ) )
  468. {
  469. Option = kNtWorkstation;
  470. }
  471. else
  472. {
  473. Option = kNtUnknown;
  474. }
  475. //
  476. // Release the server info structure.
  477. //
  478. pfNetApiBufferFree( si101 );
  479. }
  480. }
  481. }
  482. return Option;
  483. }
  484. ////////////////////////////////////////////////////////////////////////////////
  485. //
  486. // AllocateRegistryString returns a copy of the string value stored at the
  487. // specified registry key. The registry can be on either a remote machine or
  488. // the local machine.
  489. //
  490. // Parameter Description
  491. // -----------------------------------------------------------------------------
  492. // pServerName Name of server on which registry resides.
  493. // hRegistryRoot Registry root (i.e. HKEY_LOCAL_MACHINE). See RegConnectRegistry
  494. // for acceptable values.
  495. // pKeyName Name of registry key.
  496. // pValueName Name of registry value. The value must be of type REG_SZ.
  497. //
  498. // Returns:
  499. // If successful, the function returns a pointer to a copy of the string.
  500. // If the function fails, GetLastError() will return an error code other than
  501. // ERROR_SUCCESS, and NULL is returned from the function.
  502. // Revision History:
  503. // 10/17/95 <adamk> created.
  504. //
  505. LPTSTR
  506. CComputerInfo::
  507. AllocateRegistryString(
  508. LPCTSTR pServerName,
  509. HKEY hRegistryRoot,
  510. LPCTSTR pKeyName,
  511. LPCTSTR pValueName
  512. )
  513. {
  514. DWORD ErrorCode = 0;
  515. HKEY hRegistry = 0;
  516. HKEY hRegistryKey = 0;
  517. DWORD RegistryValueType = 0;
  518. DWORD cbSize = 0;
  519. DWORD cchSize = 0;
  520. LPTSTR pString = NULL;
  521. // connect to the registry of the specified machine
  522. if (ErrorCode = RegConnectRegistry((LPTSTR) pServerName, hRegistryRoot, &hRegistry))
  523. {
  524. goto CleanUp;
  525. }
  526. // open the registry key
  527. if (ErrorCode = RegOpenKeyEx(hRegistry, pKeyName, 0, KEY_QUERY_VALUE, &hRegistryKey))
  528. {
  529. goto CleanUp;
  530. }
  531. // query the value's type and size
  532. if(ErrorCode = RegQueryValueEx(hRegistryKey, pValueName, NULL, &RegistryValueType, NULL,
  533. &cbSize))
  534. {
  535. goto CleanUp;
  536. }
  537. // make sure the value is a string
  538. if (RegistryValueType != REG_SZ)
  539. {
  540. ErrorCode = ERROR_INVALID_PARAMETER;
  541. goto CleanUp;
  542. }
  543. //
  544. // cbSize is the requested size of the buffer in bytes.
  545. // allocate two more characters - one to make sure the
  546. // cbSize / sizeof(TCHAR) is rounded up properly and one
  547. // for a zero terminator to cover the case where the
  548. // data is not properly zero terminated when written.
  549. //
  550. cchSize = cbSize / sizeof(TCHAR) + 2;
  551. cbSize = cchSize * sizeof(TCHAR);
  552. if (!(pString = (LPTSTR) GlobalAlloc(GMEM_FIXED, cbSize)))
  553. {
  554. ErrorCode = GetLastError();
  555. goto CleanUp;
  556. }
  557. if (ErrorCode = RegQueryValueEx(hRegistryKey, pValueName, NULL, &RegistryValueType,
  558. (LPBYTE) pString, &cbSize))
  559. {
  560. ErrorCode = GetLastError();
  561. goto CleanUp;
  562. }
  563. else
  564. {
  565. //
  566. // RegQueryValueEx may not zero terminate the buffer if the
  567. // data in the registry is not zero terminated. Make sure it
  568. // is properly zero terminated.
  569. //
  570. pString[cchSize-1] = 0;
  571. }
  572. CleanUp:
  573. if (hRegistryKey)
  574. {
  575. RegCloseKey(hRegistryKey);
  576. }
  577. if (hRegistry)
  578. {
  579. RegCloseKey(hRegistry);
  580. }
  581. if (ErrorCode && pString)
  582. {
  583. GlobalFree(pString);
  584. pString = NULL;
  585. }
  586. SetLastError (ErrorCode);
  587. return pString;
  588. }
  589. ////////////////////////////////////////////////////////////////////////////////
  590. //
  591. // GetRegistrySubKeyCount returns the number of subkeys that the specified
  592. // registry key contains. The registry can be on either a remote machine or
  593. // the local machine.
  594. //
  595. // Parameter Description
  596. // -----------------------------------------------------------------------------
  597. // pServerName Name of server on which registry resides.
  598. // hRegistryRoot Registry root (i.e. HKEY_LOCAL_MACHINE). See RegConnectRegistry
  599. // for acceptable values.
  600. // pKeyName Name of registry key.
  601. //
  602. // Returns:
  603. // If successful, the function returns the number of subkeys and GetLastError()
  604. // will return ERROR_SUCCESS. Otherwise, GetLastError() return the error code
  605. // indicating the reason for the failure.
  606. // Revision History:
  607. // 10/23/95 <adamk> created.
  608. //
  609. BOOL
  610. CComputerInfo::
  611. GetRegistryKeyInfo(
  612. LPCTSTR pServerName,
  613. HKEY hRegistryRoot,
  614. LPCTSTR pKeyName,
  615. REGISTRY_KEY_INFO *pKeyInfo
  616. )
  617. {
  618. DWORD ErrorCode = 0;
  619. HKEY hRegistry = 0;
  620. HKEY hRegistryKey = 0;
  621. // connect to the registry of the specified machine
  622. if (ErrorCode = RegConnectRegistry((LPTSTR) pServerName, hRegistryRoot, &hRegistry))
  623. {
  624. goto CleanUp;
  625. }
  626. // open the registry key
  627. if (ErrorCode = RegOpenKeyEx(hRegistry, pKeyName, 0, KEY_QUERY_VALUE, &hRegistryKey))
  628. {
  629. goto CleanUp;
  630. }
  631. // get the key info
  632. if (ErrorCode = RegQueryInfoKey(hRegistryKey, NULL, 0, 0, &pKeyInfo->NumSubKeys,
  633. &pKeyInfo->MaxSubKeyLength, &pKeyInfo->MaxClassLength, &pKeyInfo->NumValues,
  634. &pKeyInfo->MaxValueNameLength, &pKeyInfo->MaxValueDataLength,
  635. &pKeyInfo->SecurityDescriptorLength, &pKeyInfo->LastWriteTime))
  636. {
  637. goto CleanUp;
  638. }
  639. CleanUp:
  640. if (hRegistryKey)
  641. {
  642. RegCloseKey(hRegistryKey);
  643. }
  644. if (hRegistry)
  645. {
  646. RegCloseKey(hRegistry);
  647. }
  648. if (ErrorCode)
  649. {
  650. SetLastError(ErrorCode);
  651. return FALSE;
  652. }
  653. else
  654. {
  655. return TRUE;
  656. }
  657. }