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.

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