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.

751 lines
23 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. Regqkey.c
  5. Abstract:
  6. This module contains the server side implementation for the Win32
  7. Registry query key API. That is:
  8. - BaseRegQueryInfoKey
  9. Author:
  10. David J. Gilman (davegi) 27-Nov-1991
  11. Notes:
  12. See the Notes in Regkey.c.
  13. --*/
  14. #include <rpc.h>
  15. #include "regrpc.h"
  16. #include "localreg.h"
  17. #include "regclass.h"
  18. #include "regecls.h"
  19. #include "regvcls.h"
  20. #include <malloc.h>
  21. #define DEFAULT_CLASS_SIZE 128
  22. //
  23. // Internal prototypes
  24. //
  25. NTSTATUS QueryKeyInfo(
  26. HKEY hKey,
  27. KEY_INFORMATION_CLASS KeyInformationClass,
  28. PVOID *ppKeyInfo,
  29. ULONG BufferLength,
  30. BOOL fClass,
  31. USHORT MaxClassLength);
  32. void CombineKeyInfo(
  33. PVOID KeyInfo,
  34. PVOID MachineClassKeyInfo,
  35. KEY_INFORMATION_CLASS KeyInformationClass,
  36. DWORD dwTotalSubKeys,
  37. DWORD dwTotalValues);
  38. error_status_t
  39. BaseRegQueryInfoKey(
  40. IN HKEY hKey,
  41. OUT PUNICODE_STRING lpClass,
  42. OUT LPDWORD lpcSubKeys,
  43. OUT LPDWORD lpcbMaxSubKeyLen,
  44. OUT LPDWORD lpcbMaxClassLen,
  45. OUT LPDWORD lpcValues,
  46. OUT LPDWORD lpcbMaxValueNameLen,
  47. OUT LPDWORD lpcbMaxValueLen,
  48. OUT LPDWORD lpcbSecurityDescriptor,
  49. OUT PFILETIME lpftLastWriteTime
  50. )
  51. /*++
  52. Routine Description:
  53. RegQueryInfoKey returns pertinent information about the key
  54. corresponding to a given key handle.
  55. Arguments:
  56. hKey - A handle to an open key.
  57. lpClass - Returns the Class string for the key.
  58. lpcSubKeys - Returns the number of subkeys for this key .
  59. lpcbMaxSubKeyLen - Returns the length of the longest subkey name.
  60. lpcbMaxClassLen - Returns length of longest subkey class string.
  61. lpcValues - Returns the number of ValueNames for this key.
  62. lpcbMaxValueNameLen - Returns the length of the longest ValueName.
  63. lpcbMaxValueLen - Returns the length of the longest value entry's data
  64. field.
  65. lpcbSecurityDescriptor - Returns the length of this key's
  66. SECURITY_DESCRIPTOR.
  67. lpftLastWriteTime - Returns the last time that the key or any of its
  68. value entries was modified.
  69. Return Value:
  70. Returns ERROR_SUCCESS (0) for success; error-code for failure.
  71. --*/
  72. {
  73. NTSTATUS Status;
  74. ULONG BufferLength;
  75. PVOID KeyInfo;
  76. PVOID ClassKeyInfo;
  77. KEY_INFORMATION_CLASS KeyInformationClass;
  78. SECURITY_DESCRIPTOR SecurityDescriptor;
  79. ULONG SecurityDescriptorLength;
  80. LONG Error;
  81. PDWORD pCbMaxClassLen = NULL;
  82. PDWORD pCbSecurityDescriptor = NULL;
  83. BYTE PrivateKeyFullInfo[ sizeof( KEY_FULL_INFORMATION ) +
  84. DEFAULT_CLASS_SIZE ];
  85. BYTE PrivateClassKeyInfo[ sizeof( KEY_FULL_INFORMATION ) +
  86. DEFAULT_CLASS_SIZE ];
  87. ASSERT( lpClass != NULL );
  88. ASSERT( lpcSubKeys != NULL );
  89. ASSERT( lpcbMaxSubKeyLen != NULL );
  90. ASSERT( lpcValues != NULL );
  91. ASSERT( lpcbMaxValueNameLen != NULL );
  92. ASSERT( lpcbMaxValueLen != NULL );
  93. ASSERT( lpftLastWriteTime != NULL );
  94. ASSERT( sizeof(KEY_FULL_INFORMATION) >= sizeof(KEY_CACHED_INFORMATION) );
  95. if( lpcbMaxClassLen != NULL ) {
  96. pCbMaxClassLen = lpcbMaxClassLen;
  97. }
  98. if( lpcbSecurityDescriptor != NULL ) {
  99. pCbSecurityDescriptor = lpcbSecurityDescriptor;
  100. }
  101. //
  102. // Call out to Perflib if the HKEY is HKEY_PERFORMANCE_DATA.
  103. //
  104. if(( hKey == HKEY_PERFORMANCE_DATA ) ||
  105. ( hKey == HKEY_PERFORMANCE_TEXT ) ||
  106. ( hKey == HKEY_PERFORMANCE_NLSTEXT )) {
  107. DWORD cbMaxClassLen;
  108. DWORD cbSecurityDescriptor;
  109. //
  110. // Impersonate the client.
  111. //
  112. RPC_IMPERSONATE_CLIENT( NULL );
  113. //
  114. // don't mess with Perf stuff
  115. //
  116. if( pCbMaxClassLen == NULL ) {
  117. pCbMaxClassLen = &cbMaxClassLen;
  118. }
  119. if( pCbSecurityDescriptor == NULL ) {
  120. pCbSecurityDescriptor = &cbSecurityDescriptor;
  121. }
  122. Error = PerfRegQueryInfoKey (
  123. hKey,
  124. lpClass,
  125. NULL,
  126. lpcSubKeys,
  127. lpcbMaxSubKeyLen,
  128. pCbMaxClassLen,
  129. lpcValues,
  130. lpcbMaxValueNameLen,
  131. lpcbMaxValueLen,
  132. pCbSecurityDescriptor,
  133. lpftLastWriteTime
  134. );
  135. RPC_REVERT_TO_SELF();
  136. return (error_status_t)Error;
  137. }
  138. ASSERT( IsPredefinedRegistryHandle( hKey ) == FALSE );
  139. //
  140. // First we assume that the information we want will fit on
  141. // PrivateKeyFullInformattion
  142. //
  143. if( (lpClass->Buffer == NULL) && (pCbMaxClassLen == NULL) ) {
  144. KeyInformationClass = KeyCachedInformation;
  145. } else {
  146. KeyInformationClass = KeyFullInformation;
  147. }
  148. ClassKeyInfo = (PVOID)PrivateClassKeyInfo;
  149. KeyInfo = (PVOID)PrivateKeyFullInfo;
  150. BufferLength = sizeof( PrivateKeyFullInfo );
  151. //
  152. // Ask Nt for all the meta information about this key.
  153. //
  154. Status = QueryKeyInfo(
  155. hKey,
  156. KeyInformationClass,
  157. &KeyInfo,
  158. BufferLength,
  159. lpClass->Buffer ? TRUE : FALSE,
  160. lpClass->MaximumLength
  161. );
  162. if( (NT_SUCCESS( Status ) ||
  163. ( Status == STATUS_BUFFER_OVERFLOW ) ) &&
  164. (KeyInformationClass == KeyFullInformation)
  165. ) {
  166. lpClass->Length = ( USHORT )
  167. ( (( PKEY_FULL_INFORMATION )KeyInfo)->ClassLength
  168. + sizeof( UNICODE_NULL )
  169. );
  170. }
  171. if ( NT_SUCCESS( Status )) {
  172. #ifdef LOCAL
  173. //
  174. // For special keys in HKCR, we can't just take the information
  175. // from the kernel -- these keys have properties that come from
  176. // both the user and machine versions of their keys. To find out
  177. // if it's a special key, we get more information below
  178. //
  179. if (REG_CLASS_IS_SPECIAL_KEY(hKey)) {
  180. {
  181. HKEY hkMachineClass;
  182. HKEY hkUserClass;
  183. BufferLength = sizeof( PrivateClassKeyInfo );
  184. //
  185. // we will now need information from both the user
  186. // and machine locations to find the number of
  187. // subkeys under this special key -- the machine
  188. // key is not open yet, so we open it below
  189. //
  190. //
  191. // Open the other key
  192. //
  193. Status = BaseRegGetUserAndMachineClass(
  194. NULL,
  195. hKey,
  196. MAXIMUM_ALLOWED,
  197. &hkMachineClass,
  198. &hkUserClass);
  199. if (NT_SUCCESS(Status) && (hkUserClass && hkMachineClass)) {
  200. DWORD dwTotalSubKeys;
  201. HKEY hkQuery;
  202. if (hkUserClass == hKey) {
  203. hkQuery = hkMachineClass;
  204. } else {
  205. hkQuery = hkUserClass;
  206. }
  207. //
  208. // Still need to do this query to find out
  209. // the largest subkey in the machine part
  210. // as well as other information about the
  211. // key such as its largest subkey
  212. //
  213. Status = QueryKeyInfo(
  214. hkQuery,
  215. KeyInformationClass,
  216. &ClassKeyInfo,
  217. BufferLength,
  218. FALSE,
  219. lpClass->MaximumLength);
  220. //
  221. // Now we will count the keys
  222. //
  223. if (NT_SUCCESS(Status)) {
  224. Status = ClassKeyCountSubKeys(
  225. hKey,
  226. hkUserClass,
  227. hkMachineClass,
  228. 0,
  229. &dwTotalSubKeys);
  230. }
  231. NtClose(hkQuery);
  232. //
  233. // Do not let inability to query information for
  234. // machine key cause a complete failure -- we'll
  235. // just use the user key's information
  236. //
  237. if (!NT_SUCCESS(Status)) {
  238. //
  239. // this key may not exist in the machine hive
  240. //
  241. if (STATUS_OBJECT_NAME_NOT_FOUND == Status) {
  242. Status = STATUS_SUCCESS;
  243. }
  244. if (STATUS_BUFFER_OVERFLOW == Status) {
  245. Status = STATUS_SUCCESS;
  246. }
  247. } else {
  248. ValueState* pValState;
  249. //
  250. // Find out how many values we have
  251. //
  252. Status = KeyStateGetValueState(
  253. hKey,
  254. &pValState);
  255. if (NT_SUCCESS(Status)) {
  256. //
  257. // Combine the information from the two
  258. // trees
  259. //
  260. CombineKeyInfo(
  261. KeyInfo,
  262. ClassKeyInfo,
  263. KeyInformationClass,
  264. dwTotalSubKeys,
  265. pValState ? pValState->cValues : 0);
  266. ValStateRelease(pValState);
  267. }
  268. }
  269. }
  270. }
  271. }
  272. #endif // LOCAL
  273. }
  274. if( NT_SUCCESS( Status )) {
  275. //
  276. // don't bother if the caller is not interested in this
  277. //
  278. if( pCbSecurityDescriptor != NULL ) {
  279. ASSERT( lpcbSecurityDescriptor != NULL );
  280. //
  281. // Get the size of the key's SECURITY_DESCRIPTOR for OWNER, GROUP
  282. // and DACL. These three are always accessible (or inaccesible)
  283. // as a set.
  284. //
  285. Status = NtQuerySecurityObject(
  286. hKey,
  287. OWNER_SECURITY_INFORMATION
  288. | GROUP_SECURITY_INFORMATION
  289. | DACL_SECURITY_INFORMATION,
  290. &SecurityDescriptor,
  291. 0,
  292. lpcbSecurityDescriptor
  293. );
  294. //
  295. // If getting the size of the SECURITY_DESCRIPTOR failed (probably
  296. // due to the lack of READ_CONTROL access) return zero.
  297. //
  298. if( Status != STATUS_BUFFER_TOO_SMALL ) {
  299. *lpcbSecurityDescriptor = 0;
  300. } else {
  301. //
  302. // Try again to get the size of the key's SECURITY_DESCRIPTOR,
  303. // this time asking for SACL as well. This should normally
  304. // fail but may succeed if the caller has SACL access.
  305. //
  306. Status = NtQuerySecurityObject(
  307. hKey,
  308. OWNER_SECURITY_INFORMATION
  309. | GROUP_SECURITY_INFORMATION
  310. | DACL_SECURITY_INFORMATION
  311. | SACL_SECURITY_INFORMATION,
  312. &SecurityDescriptor,
  313. 0,
  314. &SecurityDescriptorLength
  315. );
  316. if( Status == STATUS_BUFFER_TOO_SMALL ) {
  317. //
  318. // The caller had SACL access so update the returned
  319. // length.
  320. //
  321. *lpcbSecurityDescriptor = SecurityDescriptorLength;
  322. }
  323. }
  324. }
  325. if( KeyInformationClass == KeyCachedInformation ) {
  326. ASSERT( pCbMaxClassLen == NULL );
  327. *lpcSubKeys = ((PKEY_CACHED_INFORMATION)KeyInfo)->SubKeys;
  328. *lpcbMaxSubKeyLen = ((PKEY_CACHED_INFORMATION)KeyInfo)->MaxNameLen;
  329. *lpcValues = ((PKEY_CACHED_INFORMATION)KeyInfo)->Values;
  330. *lpcbMaxValueNameLen = ((PKEY_CACHED_INFORMATION)KeyInfo)->MaxValueNameLen;
  331. *lpcbMaxValueLen = ((PKEY_CACHED_INFORMATION)KeyInfo)->MaxValueDataLen;
  332. *lpftLastWriteTime = *( PFILETIME ) &((PKEY_CACHED_INFORMATION)KeyInfo)->LastWriteTime;
  333. Error = ERROR_SUCCESS;
  334. } else {
  335. *lpcSubKeys = ((PKEY_FULL_INFORMATION)KeyInfo)->SubKeys;
  336. if( pCbMaxClassLen != NULL ) {
  337. *lpcbMaxClassLen = ((PKEY_FULL_INFORMATION)KeyInfo)->MaxClassLen;
  338. ASSERT( pCbMaxClassLen == lpcbMaxClassLen );
  339. }
  340. *lpcbMaxSubKeyLen = ((PKEY_FULL_INFORMATION)KeyInfo)->MaxNameLen;
  341. *lpcValues = ((PKEY_FULL_INFORMATION)KeyInfo)->Values;
  342. *lpcbMaxValueNameLen = ((PKEY_FULL_INFORMATION)KeyInfo)->MaxValueNameLen;
  343. *lpcbMaxValueLen = ((PKEY_FULL_INFORMATION)KeyInfo)->MaxValueDataLen;
  344. *lpftLastWriteTime = *( PFILETIME ) &((PKEY_FULL_INFORMATION)KeyInfo)->LastWriteTime;
  345. //
  346. // Copy/assign remaining output parameters.
  347. //
  348. if ( lpClass->Length > lpClass->MaximumLength ) {
  349. if( lpClass->Buffer != NULL ) {
  350. lpClass->Buffer = NULL;
  351. Error = (error_status_t)RtlNtStatusToDosError( STATUS_BUFFER_TOO_SMALL );
  352. } else {
  353. //
  354. // Caller is not iterest in Class, so return its size only.
  355. //
  356. Error = ERROR_SUCCESS;
  357. }
  358. } else {
  359. if( ((PKEY_FULL_INFORMATION)KeyInfo)->ClassLength != 0 ) {
  360. ASSERT( lpClass->Buffer != NULL );
  361. RtlMoveMemory(
  362. lpClass->Buffer,
  363. ((PKEY_FULL_INFORMATION)KeyInfo)->Class,
  364. ((PKEY_FULL_INFORMATION)KeyInfo)->ClassLength
  365. );
  366. }
  367. //
  368. // NUL terminate the class name.
  369. //
  370. lpClass->Buffer[ ((PKEY_FULL_INFORMATION)KeyInfo)->ClassLength >> 1 ] = UNICODE_NULL;
  371. Error = ERROR_SUCCESS;
  372. }
  373. }
  374. } else if( Status == STATUS_BUFFER_OVERFLOW ) {
  375. //
  376. // A return value of STATUS_BUFFER_OVERFLOW means that the user did
  377. // not supply enough space for the class. The required space has
  378. // already been assigned above.
  379. //
  380. lpClass->Buffer = NULL;
  381. Error = ERROR_INVALID_PARAMETER;
  382. } else {
  383. //
  384. // Some other error occurred.
  385. //
  386. Error = RtlNtStatusToDosError( Status );
  387. }
  388. if( KeyInfo != ( PVOID )PrivateKeyFullInfo ) {
  389. //
  390. // Free the buffer and return the Registry return value.
  391. //
  392. RtlFreeHeap( RtlProcessHeap( ), 0, KeyInfo );
  393. }
  394. if( ClassKeyInfo != ( PVOID )PrivateClassKeyInfo ) {
  395. //
  396. // Free the buffer and return the Registry return value.
  397. //
  398. RtlFreeHeap( RtlProcessHeap( ), 0, ClassKeyInfo );
  399. }
  400. return (error_status_t)Error;
  401. }
  402. NTSTATUS QueryKeyInfo(
  403. HKEY hKey,
  404. KEY_INFORMATION_CLASS KeyInformationClass,
  405. PVOID *ppKeyInfo,
  406. ULONG BufferLength,
  407. BOOL fClass,
  408. USHORT MaxClassLength)
  409. /*++
  410. Routine Description:
  411. Queries the kernel for key information.
  412. Arguments:
  413. hKey - handle of key for which to query info
  414. KeyInformationClass - type of info required from the kernel
  415. KeyInfo - pointer to address of
  416. buffer for information about key
  417. BufferLength - size of KeyFullInfo buffer
  418. fClass - flag set to TRUE if the class for this
  419. key should be rerieved
  420. MaxClassLength - maximum size for class data that a caller
  421. is willing to support. The ppKeyFullInfo buffer may
  422. point to the address of a buffer that can handle the
  423. class size of the key, but the caller may want the class
  424. to fit in some smaller buffer later, so this param lets
  425. the caller limit that size. It is ignored if fClass
  426. is FALSE.
  427. Return Value:
  428. Returns NT_SUCCESS (0) for success; error-code for failure.
  429. Notes:
  430. --*/
  431. {
  432. NTSTATUS Status;
  433. ULONG Result;
  434. ASSERT( KeyInformationClass == KeyFullInformation ||
  435. KeyInformationClass == KeyCachedInformation );
  436. Status = NtQueryKey(
  437. hKey,
  438. KeyInformationClass,
  439. *ppKeyInfo,
  440. BufferLength,
  441. &Result);
  442. //
  443. // A return value of STATUS_BUFFER_TOO_SMALL would mean that there
  444. // was not enough room for even the fixed portion of the structure.
  445. //
  446. ASSERT( Status != STATUS_BUFFER_TOO_SMALL );
  447. if ( Status == STATUS_BUFFER_OVERFLOW ) {
  448. // we cannot hit this on a CachedInfo fixed structure
  449. ASSERT( KeyInformationClass == KeyFullInformation );
  450. //
  451. // The buffer defined in the stack wasn't big enough to hold
  452. // the Key Information.
  453. //
  454. // If the fClass flag is not set, then the caller does not do the
  455. // check for the caller specified maximum class length below
  456. // and requeries happily. If the flag is set, we do the check, and
  457. // if the class size is bigger than what the caller specified as
  458. // the max, we return STATUS_BUFFER_OVERFLOW.
  459. //
  460. if ( !fClass || ((ULONG)(MaxClassLength) >=
  461. (( PKEY_FULL_INFORMATION )*ppKeyInfo )->ClassLength + sizeof( UNICODE_NULL )) ) {
  462. //
  463. // Required length is stored in Result -- set our length
  464. // to the required length and allocate memory for it.
  465. //
  466. BufferLength = Result;
  467. *ppKeyInfo = RtlAllocateHeap( RtlProcessHeap( ), 0,
  468. BufferLength );
  469. //
  470. // If the memory allocation fails, return a Registry error.
  471. //
  472. if( ! *ppKeyInfo ) {
  473. return STATUS_NO_MEMORY;
  474. }
  475. //
  476. // Query for the necessary information about the supplied key.
  477. //
  478. Status = NtQueryKey( hKey,
  479. KeyFullInformation,
  480. *ppKeyInfo,
  481. BufferLength,
  482. &Result
  483. );
  484. }
  485. }
  486. return Status;
  487. }
  488. void CombineKeyInfo(
  489. PVOID KeyInfo,
  490. PVOID MachineClassKeyInfo,
  491. KEY_INFORMATION_CLASS KeyInformationClass,
  492. DWORD dwTotalSubKeys,
  493. DWORD dwTotalValues)
  494. /*++
  495. Routine Description:
  496. Combine the information from the user and machine hives
  497. for a special key
  498. Arguments:
  499. Status -
  500. KeyInfo - buffer for information about user key
  501. MachineClassKeyInfo - buffer for information about machine key
  502. KeyClassInformation - type of information present in buffers
  503. dwTotalSubKeys - total number of subkeys for the two
  504. in each hive
  505. Return Value:
  506. Returns NT_SUCCESS (0) for success; error-code for failure.
  507. Notes:
  508. --*/
  509. {
  510. ASSERT( KeyInformationClass == KeyCachedInformation ||
  511. KeyInformationClass == KeyFullInformation );
  512. if( KeyInformationClass == KeyCachedInformation ) {
  513. //
  514. // Set the number of keys to be the total between the
  515. // two versions in each hive
  516. //
  517. ((PKEY_CACHED_INFORMATION)KeyInfo)->SubKeys = dwTotalSubKeys;
  518. ((PKEY_CACHED_INFORMATION)KeyInfo)->Values = dwTotalValues;
  519. //
  520. // Set our max namelen to the namelen of whichever is biggest
  521. // between the two hives. Same for class.
  522. //
  523. if (((PKEY_CACHED_INFORMATION)MachineClassKeyInfo)->MaxNameLen > ((PKEY_CACHED_INFORMATION)KeyInfo)->MaxNameLen) {
  524. ((PKEY_CACHED_INFORMATION)KeyInfo)->MaxNameLen = ((PKEY_CACHED_INFORMATION)MachineClassKeyInfo)->MaxNameLen;
  525. }
  526. //
  527. // Since we also merge values, we must set the value information as well
  528. //
  529. if (((PKEY_CACHED_INFORMATION)MachineClassKeyInfo)->MaxValueNameLen > ((PKEY_CACHED_INFORMATION)KeyInfo)->MaxValueNameLen) {
  530. ((PKEY_CACHED_INFORMATION)KeyInfo)->MaxValueNameLen = ((PKEY_CACHED_INFORMATION)MachineClassKeyInfo)->MaxValueNameLen;
  531. }
  532. if (((PKEY_CACHED_INFORMATION)MachineClassKeyInfo)->MaxValueDataLen > ((PKEY_CACHED_INFORMATION)KeyInfo)->MaxValueDataLen) {
  533. ((PKEY_CACHED_INFORMATION)KeyInfo)->MaxValueDataLen = ((PKEY_CACHED_INFORMATION)MachineClassKeyInfo)->MaxValueDataLen;
  534. }
  535. } else {
  536. //
  537. // Set the number of keys to be the total between the
  538. // two versions in each hive
  539. //
  540. ((PKEY_FULL_INFORMATION)KeyInfo)->SubKeys = dwTotalSubKeys;
  541. ((PKEY_FULL_INFORMATION)KeyInfo)->Values = dwTotalValues;
  542. //
  543. // Set our max namelen to the namelen of whichever is biggest
  544. // between the two hives. Same for class.
  545. //
  546. if (((PKEY_FULL_INFORMATION)MachineClassKeyInfo)->MaxNameLen > ((PKEY_FULL_INFORMATION)KeyInfo)->MaxNameLen) {
  547. ((PKEY_FULL_INFORMATION)KeyInfo)->MaxNameLen = ((PKEY_FULL_INFORMATION)MachineClassKeyInfo)->MaxNameLen;
  548. }
  549. if (((PKEY_FULL_INFORMATION)MachineClassKeyInfo)->MaxClassLen > ((PKEY_FULL_INFORMATION)KeyInfo)->MaxClassLen) {
  550. ((PKEY_FULL_INFORMATION)KeyInfo)->MaxClassLen = ((PKEY_FULL_INFORMATION)MachineClassKeyInfo)->MaxClassLen;
  551. }
  552. //
  553. // Since we also merge values, we must set the value information as well
  554. //
  555. if (((PKEY_FULL_INFORMATION)MachineClassKeyInfo)->MaxValueNameLen > ((PKEY_FULL_INFORMATION)KeyInfo)->MaxValueNameLen) {
  556. ((PKEY_FULL_INFORMATION)KeyInfo)->MaxValueNameLen = ((PKEY_FULL_INFORMATION)MachineClassKeyInfo)->MaxValueNameLen;
  557. }
  558. if (((PKEY_FULL_INFORMATION)MachineClassKeyInfo)->MaxValueDataLen > ((PKEY_FULL_INFORMATION)KeyInfo)->MaxValueDataLen) {
  559. ((PKEY_FULL_INFORMATION)KeyInfo)->MaxValueDataLen = ((PKEY_FULL_INFORMATION)MachineClassKeyInfo)->MaxValueDataLen;
  560. }
  561. }
  562. }