Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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