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.

823 lines
21 KiB

  1. /*++
  2. Copyright (c) 1992 Microsoft Corporation
  3. Module Name:
  4. Regqval.c
  5. Abstract:
  6. This module contains the client side wrappers for the Win32 Registry
  7. query value APIs. That is:
  8. - RegQueryValueA
  9. - RegQueryValueW
  10. - RegQueryValueExA
  11. - RegQueryValueExW
  12. Author:
  13. David J. Gilman (davegi) 18-Mar-1992
  14. Notes:
  15. See the notes in server\regqval.c.
  16. --*/
  17. #include <rpc.h>
  18. #include "regrpc.h"
  19. #include "client.h"
  20. LONG
  21. RegQueryValueA (
  22. HKEY hKey,
  23. LPCSTR lpSubKey,
  24. LPSTR lpData,
  25. PLONG lpcbData
  26. )
  27. /*++
  28. Routine Description:
  29. Win 3.1 ANSI RPC wrapper for querying a value.
  30. --*/
  31. {
  32. HKEY ChildKey;
  33. LONG Error;
  34. DWORD ValueType;
  35. LONG InitialCbData;
  36. HKEY TempHandle = NULL;
  37. #if DBG
  38. if ( BreakPointOnEntry ) {
  39. DbgBreakPoint();
  40. }
  41. #endif
  42. //
  43. // Limit the capabilities associated with HKEY_PERFORMANCE_DATA.
  44. //
  45. if( hKey == HKEY_PERFORMANCE_DATA ) {
  46. return ERROR_INVALID_HANDLE;
  47. }
  48. hKey = MapPredefinedHandle( hKey, &TempHandle );
  49. if( hKey == NULL ) {
  50. Error = ERROR_INVALID_HANDLE;
  51. goto ExitCleanup;
  52. }
  53. //
  54. // If the sub-key is NULL or points to an empty string then the value is
  55. // to be queried from this key (i.e. hKey) otherwise the sub-key needs
  56. // to be opened.
  57. //
  58. if(( lpSubKey == NULL ) || ( *lpSubKey == '\0' )) {
  59. ChildKey = hKey;
  60. } else {
  61. //
  62. // The sub-key was supplied so impersonate the
  63. // client and attempt to open it.
  64. //
  65. Error = RegOpenKeyExA(
  66. hKey,
  67. lpSubKey,
  68. 0,
  69. KEY_QUERY_VALUE,
  70. &ChildKey
  71. );
  72. if( Error != ERROR_SUCCESS ) {
  73. goto ExitCleanup;
  74. }
  75. }
  76. InitialCbData = ARGUMENT_PRESENT(lpcbData) ? (*lpcbData) : 0;
  77. //
  78. // ChildKey contains an HKEY which may be the one supplied (hKey) or
  79. // returned from RegOpenKeyExA. Query the value using the special value
  80. // name NULL.
  81. //
  82. Error = RegQueryValueExA(
  83. ChildKey,
  84. NULL,
  85. NULL,
  86. &ValueType,
  87. lpData,
  88. lpcbData
  89. );
  90. //
  91. // If the sub key was opened, close it.
  92. //
  93. if( ChildKey != hKey ) {
  94. if( IsLocalHandle( ChildKey )) {
  95. LocalBaseRegCloseKey( &ChildKey );
  96. } else {
  97. ChildKey = DereferenceRemoteHandle( ChildKey );
  98. BaseRegCloseKey( &ChildKey );
  99. }
  100. }
  101. //
  102. // If the type of the value is not a null terminate string, then return
  103. // an error. (Win 3.1 compatibility)
  104. //
  105. if (!Error && ((ValueType != REG_SZ) && (ValueType != REG_EXPAND_SZ))) {
  106. Error = ERROR_INVALID_DATA;
  107. }
  108. //
  109. // If value doesn't exist, return ERROR_SUCCESS and an empty string.
  110. // (Win 3.1 compatibility)
  111. //
  112. if( Error == ERROR_FILE_NOT_FOUND ) {
  113. if( ARGUMENT_PRESENT( lpcbData ) ) {
  114. *lpcbData = sizeof( CHAR );
  115. }
  116. if( ARGUMENT_PRESENT( lpData ) ) {
  117. *lpData = '\0';
  118. }
  119. Error = ERROR_SUCCESS;
  120. }
  121. //
  122. // Expand if necessary (VB compatibility)
  123. //
  124. if (!Error && (ValueType == REG_EXPAND_SZ)) {
  125. if ( (!ARGUMENT_PRESENT(lpcbData)) || (!ARGUMENT_PRESENT(lpData)) ) {
  126. Error = ERROR_INVALID_DATA;
  127. } else {
  128. LPSTR ExpandBuffer;
  129. LONG ExpandedSize;
  130. LONG BufferSize = (InitialCbData>*lpcbData)?InitialCbData:*lpcbData;
  131. //
  132. // if InitialCbData was 0, allocate a buffer of the real size
  133. //
  134. ExpandBuffer = RtlAllocateHeap( RtlProcessHeap(), 0, BufferSize);
  135. if (ExpandBuffer == NULL) {
  136. Error = ERROR_NOT_ENOUGH_MEMORY;
  137. } else {
  138. RtlCopyMemory(ExpandBuffer, lpData, *lpcbData);
  139. ExpandedSize = ExpandEnvironmentStringsA(ExpandBuffer, lpData, BufferSize);
  140. if (ExpandedSize > InitialCbData) {
  141. Error = ERROR_MORE_DATA;
  142. }
  143. *lpcbData = ExpandedSize;
  144. RtlFreeHeap( RtlProcessHeap(), 0, ExpandBuffer );
  145. }
  146. }
  147. }
  148. //
  149. // Return the results of querying the value.
  150. //
  151. ExitCleanup:
  152. CLOSE_LOCAL_HANDLE(TempHandle);
  153. return Error;
  154. }
  155. LONG
  156. RegQueryValueW (
  157. HKEY hKey,
  158. LPCWSTR lpSubKey,
  159. LPWSTR lpData,
  160. PLONG lpcbData
  161. )
  162. /*++
  163. Routine Description:
  164. Win 3.1 Unicode RPC wrapper for querying a value.
  165. --*/
  166. {
  167. HKEY ChildKey;
  168. LONG Error;
  169. DWORD ValueType;
  170. LONG InitialCbData;
  171. HKEY TempHandle = NULL;
  172. #if DBG
  173. if ( BreakPointOnEntry ) {
  174. DbgBreakPoint();
  175. }
  176. #endif
  177. //
  178. // Limit the capabilities associated with HKEY_PERFORMANCE_DATA.
  179. //
  180. if( hKey == HKEY_PERFORMANCE_DATA ) {
  181. return ERROR_INVALID_HANDLE;
  182. }
  183. hKey = MapPredefinedHandle( hKey, &TempHandle );
  184. if( hKey == NULL ) {
  185. Error = ERROR_INVALID_HANDLE;
  186. goto ExitCleanup;
  187. }
  188. //
  189. // If the sub-key is NULL or points to an empty string then the value is
  190. // to be queried from this key (i.e. hKey) otherwise the sub-key needs
  191. // to be opened.
  192. //
  193. if(( lpSubKey == NULL ) || ( *lpSubKey == '\0' )) {
  194. ChildKey = hKey;
  195. } else {
  196. //
  197. // The sub-key was supplied so attempt to open it.
  198. //
  199. Error = RegOpenKeyExW(
  200. hKey,
  201. lpSubKey,
  202. 0,
  203. KEY_QUERY_VALUE,
  204. &ChildKey
  205. );
  206. if( Error != ERROR_SUCCESS ) {
  207. goto ExitCleanup;
  208. }
  209. }
  210. InitialCbData = ARGUMENT_PRESENT(lpcbData) ? (*lpcbData) : 0;
  211. //
  212. // ChildKey contains an HKEY which may be the one supplied (hKey) or
  213. // returned from RegOpenKeyExA. Query the value using the special value
  214. // name NULL.
  215. //
  216. Error = RegQueryValueExW(
  217. ChildKey,
  218. NULL,
  219. NULL,
  220. &ValueType,
  221. ( LPBYTE )lpData,
  222. lpcbData
  223. );
  224. //
  225. // If the sub key was opened, close it.
  226. //
  227. if( ChildKey != hKey ) {
  228. if( IsLocalHandle( ChildKey )) {
  229. LocalBaseRegCloseKey( &ChildKey );
  230. } else {
  231. ChildKey = DereferenceRemoteHandle( ChildKey );
  232. BaseRegCloseKey( &ChildKey );
  233. }
  234. }
  235. //
  236. // If the type of the value is not a null terminate string, then return
  237. // an error. (Win 3.1 compatibility)
  238. //
  239. if (!Error && ((ValueType != REG_SZ) && (ValueType != REG_EXPAND_SZ))) {
  240. Error = ERROR_INVALID_DATA;
  241. }
  242. //
  243. // If value doesn't exist, return ERROR_SUCCESS and an empty string.
  244. // (Win 3.1 compatibility)
  245. //
  246. if( Error == ERROR_FILE_NOT_FOUND ) {
  247. if( ARGUMENT_PRESENT( lpcbData ) ) {
  248. *lpcbData = sizeof( WCHAR );
  249. }
  250. if( ARGUMENT_PRESENT( lpData ) ) {
  251. *lpData = ( WCHAR )'\0';
  252. }
  253. Error = ERROR_SUCCESS;
  254. }
  255. //
  256. // Expand if necessary (VB compatibility)
  257. //
  258. if (!Error && (ValueType == REG_EXPAND_SZ)) {
  259. if ( (!ARGUMENT_PRESENT(lpcbData)) || (!ARGUMENT_PRESENT(lpData)) ) {
  260. Error = ERROR_INVALID_DATA;
  261. } else {
  262. LPWSTR ExpandBuffer;
  263. LONG ExpandedSize;
  264. LONG BufferSize = (InitialCbData>*lpcbData)?InitialCbData:*lpcbData;
  265. //
  266. // if InitialCbData was 0, allocate a buffer of the real size
  267. //
  268. ExpandBuffer = RtlAllocateHeap( RtlProcessHeap(), 0, BufferSize);
  269. if (ExpandBuffer == NULL) {
  270. Error = ERROR_NOT_ENOUGH_MEMORY;
  271. } else {
  272. RtlCopyMemory(ExpandBuffer, lpData, *lpcbData);
  273. ExpandedSize = ExpandEnvironmentStringsW(ExpandBuffer, lpData, BufferSize / sizeof(WCHAR));
  274. if (ExpandedSize > (LONG)(InitialCbData / sizeof(WCHAR))) {
  275. Error = ERROR_MORE_DATA;
  276. }
  277. *lpcbData = ExpandedSize;
  278. RtlFreeHeap( RtlProcessHeap(), 0, ExpandBuffer );
  279. }
  280. }
  281. }
  282. //
  283. // Return the results of querying the value.
  284. //
  285. ExitCleanup:
  286. CLOSE_LOCAL_HANDLE(TempHandle);
  287. return Error;
  288. }
  289. LONG
  290. APIENTRY
  291. RegQueryValueExA (
  292. HKEY hKey,
  293. LPCSTR lpValueName,
  294. LPDWORD lpReserved,
  295. LPDWORD lpdwType,
  296. LPBYTE lpData,
  297. LPDWORD lpcbData
  298. )
  299. /*++
  300. Routine Description:
  301. Win32 ANSI RPC wrapper for querying a value.
  302. RegQueryValueExA converts the lpValueName argument to a counted Unicode
  303. string and then calls BaseRegQueryValue.
  304. --*/
  305. {
  306. PUNICODE_STRING ValueName;
  307. UNICODE_STRING StubValueName;
  308. DWORD ValueType;
  309. ANSI_STRING AnsiString;
  310. NTSTATUS Status;
  311. LONG Error;
  312. DWORD ValueLength;
  313. DWORD InputLength;
  314. PWSTR UnicodeValueBuffer;
  315. ULONG UnicodeValueLength;
  316. PSTR AnsiValueBuffer;
  317. ULONG AnsiValueLength;
  318. ULONG Index;
  319. ULONG cbAnsi = 0;
  320. HKEY TempHandle = NULL;
  321. #if DBG
  322. if ( BreakPointOnEntry ) {
  323. DbgBreakPoint();
  324. }
  325. #endif
  326. //
  327. // Validate dependency between lpData and lpcbData parameters.
  328. //
  329. if( ARGUMENT_PRESENT( lpReserved ) ||
  330. (ARGUMENT_PRESENT( lpData ) && ( ! ARGUMENT_PRESENT( lpcbData )))) {
  331. return ERROR_INVALID_PARAMETER;
  332. }
  333. hKey = MapPredefinedHandle( hKey, &TempHandle );
  334. if( hKey == NULL ) {
  335. Error = ERROR_INVALID_HANDLE;
  336. goto ExitCleanup;
  337. }
  338. //
  339. // Convert the value name to a counted Unicode string using the static
  340. // Unicode string in the TEB.
  341. //
  342. StubValueName.Buffer = NULL;
  343. ValueName = &NtCurrentTeb( )->StaticUnicodeString;
  344. ASSERT( ValueName != NULL );
  345. Status = RtlInitAnsiStringEx( &AnsiString, lpValueName );
  346. if( ! NT_SUCCESS( Status )) {
  347. Error = RtlNtStatusToDosError( Status );
  348. goto ExitCleanup;
  349. }
  350. Status = RtlAnsiStringToUnicodeString(
  351. ValueName,
  352. &AnsiString,
  353. FALSE
  354. );
  355. if( ! NT_SUCCESS( Status )) {
  356. //
  357. // The StaticUnicodeString is not long enough; Try to allocate a bigger one
  358. //
  359. Status = RtlAnsiStringToUnicodeString(
  360. &StubValueName,
  361. &AnsiString,
  362. TRUE
  363. );
  364. if( ! NT_SUCCESS( Status )) {
  365. Error = RtlNtStatusToDosError( Status );
  366. goto ExitCleanup;
  367. }
  368. ValueName = &StubValueName;
  369. }
  370. //
  371. // Add the terminating NULL to the Length so that RPC transmits
  372. // it.
  373. //
  374. ValueName->Length += sizeof( UNICODE_NULL );
  375. //
  376. // Call the Base API, passing it the supplied parameters and the
  377. // counted Unicode strings. Note that zero bytes are transmitted (i.e.
  378. // InputLength = 0) for the data.
  379. //
  380. ValueLength = ARGUMENT_PRESENT( lpcbData )? *lpcbData : 0;
  381. InputLength = 0;
  382. if( IsLocalHandle( hKey )) {
  383. Error = (LONG)LocalBaseRegQueryValue (
  384. hKey,
  385. ValueName,
  386. &ValueType,
  387. lpData,
  388. &ValueLength,
  389. &InputLength
  390. );
  391. //
  392. // Make sure that the local side didn't destroy the Buffer in
  393. // the StaticUnicodeString
  394. //
  395. ASSERT( ValueName->Buffer );
  396. } else {
  397. Error = (LONG)BaseRegQueryValue (
  398. DereferenceRemoteHandle( hKey ),
  399. ValueName,
  400. &ValueType,
  401. lpData,
  402. &ValueLength,
  403. &InputLength
  404. );
  405. }
  406. //
  407. // If no error or callers buffer too small, and type is one of the null
  408. // terminated string types, then do the UNICODE to ANSI translation.
  409. // We handle the buffer too small case, because the callers buffer may
  410. // be big enough for the ANSI representation, but not the UNICODE one.
  411. // In this case, we need to allocate a buffer big enough, do the query
  412. // again and then the translation into the callers buffer. We only do
  413. // this if the caller actually wants the value data (lpData != NULL)
  414. //
  415. if ((Error == ERROR_SUCCESS || Error == ERROR_MORE_DATA) &&
  416. (ARGUMENT_PRESENT( lpData ) || ARGUMENT_PRESENT( lpcbData ))&&
  417. (ValueType == REG_SZ ||
  418. ValueType == REG_EXPAND_SZ ||
  419. ValueType == REG_MULTI_SZ)
  420. ) {
  421. UnicodeValueLength = ValueLength;
  422. AnsiValueBuffer = lpData;
  423. AnsiValueLength = ARGUMENT_PRESENT( lpcbData )?
  424. *lpcbData : 0;
  425. //
  426. // Allocate a buffer for the UNICODE value and reissue the query.
  427. //
  428. UnicodeValueBuffer = RtlAllocateHeap( RtlProcessHeap(), 0,
  429. UnicodeValueLength
  430. );
  431. if (UnicodeValueBuffer == NULL) {
  432. Error = ERROR_NOT_ENOUGH_MEMORY;
  433. } else {
  434. InputLength = 0;
  435. if( IsLocalHandle( hKey )) {
  436. //
  437. // Add the terminating NULL to the Length
  438. // (remember that in the local case, ValueName->Length
  439. // was decremented by sizeof( UNICODE_NULL ) in the first
  440. // call to LocalBaseRegQueryValue).
  441. // This won't happen in the remote case, since the
  442. // server side will decrement ValueName->Length on
  443. // the transmitted structure (a copy of ValueName), and
  444. // the new Valuename->Length won't be transmitted back to
  445. // the client.
  446. //
  447. ValueName->Length += sizeof( UNICODE_NULL );
  448. Error = (LONG)LocalBaseRegQueryValue (
  449. hKey,
  450. ValueName,
  451. &ValueType,
  452. (LPBYTE)UnicodeValueBuffer,
  453. &ValueLength,
  454. &InputLength
  455. );
  456. //
  457. // Make sure that the local side didn't destroy the
  458. // Buffer in the StaticUnicodeString
  459. //
  460. ASSERT(ValueName->Buffer);
  461. } else {
  462. Error = (LONG)BaseRegQueryValue (
  463. DereferenceRemoteHandle( hKey ),
  464. ValueName,
  465. &ValueType,
  466. (LPBYTE)UnicodeValueBuffer,
  467. &ValueLength,
  468. &InputLength
  469. );
  470. }
  471. if( Error == ERROR_SUCCESS ) {
  472. // Compute needed buffer size , cbAnsi will keeps the byte
  473. // counts to keep MBCS string after following step.
  474. RtlUnicodeToMultiByteSize( &cbAnsi ,
  475. UnicodeValueBuffer ,
  476. ValueLength );
  477. // If we could not store all MBCS string to buffer that
  478. // Apps gives me. We set ERROR_MORE_DATA to Error
  479. if( ARGUMENT_PRESENT( lpcbData ) ) {
  480. if( cbAnsi > *lpcbData && lpData != NULL ) {
  481. Error = ERROR_MORE_DATA;
  482. }
  483. }
  484. } else {
  485. // to be used below
  486. cbAnsi = ValueLength;
  487. }
  488. }
  489. if ((Error == ERROR_SUCCESS) && (AnsiValueBuffer != NULL) ) {
  490. //
  491. // We have a UNICODE value, so translate it to ANSI in the callers
  492. // buffer. In the case where the caller's buffer was big enough
  493. // for the UNICODE version, we do the conversion in place, which
  494. // works since the ANSI version is smaller than the UNICODE version.
  495. //
  496. Index = 0;
  497. Status = RtlUnicodeToMultiByteN( AnsiValueBuffer,
  498. AnsiValueLength,
  499. &Index,
  500. UnicodeValueBuffer,
  501. UnicodeValueLength
  502. );
  503. if (!NT_SUCCESS( Status )) {
  504. Error = RtlNtStatusToDosError( Status );
  505. }
  506. // Now Index keeps Byte counts of MBCS string in AnsiValueBuffer
  507. cbAnsi = Index;
  508. }
  509. //
  510. // Free the buffer if it was successfully allocated
  511. //
  512. if (UnicodeValueBuffer != NULL) {
  513. RtlFreeHeap( RtlProcessHeap(), 0, UnicodeValueBuffer );
  514. }
  515. //
  516. // Return the length of the ANSI version to the caller.
  517. //
  518. ValueLength = cbAnsi;
  519. //
  520. // Special hack to help out all the people who
  521. // believe the length of a NULL terminated string is
  522. // strlen(foo) instead of strlen(foo) + 1.
  523. // If the last character of the buffer is not a NULL
  524. // and there is enough space left in the caller's buffer,
  525. // slap a NULL in there to prevent him from going nuts
  526. // trying to do a strlen().
  527. //
  528. if (ARGUMENT_PRESENT( lpData ) &&
  529. (*lpcbData > ValueLength) &&
  530. (ValueLength > 0) &&
  531. (lpData[ValueLength-1] != '\0')) {
  532. lpData[ValueLength] = '\0';
  533. }
  534. }
  535. //
  536. // Stored the returned length in the caller specified location and
  537. // return the error code.
  538. //
  539. if (lpdwType != NULL) {
  540. *lpdwType = ValueType;
  541. }
  542. if( ARGUMENT_PRESENT( lpcbData ) ) {
  543. *lpcbData = ValueLength;
  544. }
  545. //
  546. // Free the temporary Unicode string stub allocated for the ValueName
  547. //
  548. RtlFreeUnicodeString(&StubValueName);
  549. ExitCleanup:
  550. CLOSE_LOCAL_HANDLE(TempHandle);
  551. return Error;
  552. }
  553. LONG
  554. APIENTRY
  555. RegQueryValueExW (
  556. HKEY hKey,
  557. LPCWSTR lpValueName,
  558. LPDWORD lpReserved,
  559. LPDWORD lpdwType,
  560. LPBYTE lpData,
  561. LPDWORD lpcbData
  562. )
  563. /*++
  564. Routine Description:
  565. Win32 Unicode RPC wrapper for querying a value.
  566. RegQueryValueExW converts the lpValueName argument to a counted Unicode
  567. string and then calls BaseRegQueryValue.
  568. --*/
  569. {
  570. UNICODE_STRING ValueName;
  571. DWORD InputLength;
  572. DWORD ValueLength;
  573. DWORD ValueType;
  574. LONG Error;
  575. HKEY TempHandle = NULL;
  576. NTSTATUS Status;
  577. #if DBG
  578. if ( BreakPointOnEntry ) {
  579. DbgBreakPoint();
  580. }
  581. #endif
  582. //
  583. // Validate dependency between lpData and lpcbData parameters.
  584. //
  585. if( ARGUMENT_PRESENT( lpReserved ) ||
  586. (ARGUMENT_PRESENT( lpData ) && ( ! ARGUMENT_PRESENT( lpcbData )))) {
  587. return ERROR_INVALID_PARAMETER;
  588. }
  589. hKey = MapPredefinedHandle( hKey, &TempHandle );
  590. if( hKey == NULL ) {
  591. Error = ERROR_INVALID_HANDLE;
  592. goto ExitCleanup;
  593. }
  594. //
  595. // Convert the value name to a counted Unicode string.
  596. //
  597. Status = RtlInitUnicodeStringEx(&ValueName, lpValueName);
  598. if( !NT_SUCCESS(Status) ) {
  599. Error = RtlNtStatusToDosError( Status );
  600. goto ExitCleanup;
  601. }
  602. //
  603. // Add the terminating NULL to the Length so that RPC transmits it.
  604. //
  605. ValueName.Length += sizeof( UNICODE_NULL );
  606. //
  607. // Call the Base API, passing it the supplied parameters and the
  608. // counted Unicode strings. Note that zero bytes are transmitted (i.e.
  609. // InputLength = 0) for the data.
  610. //
  611. InputLength = 0;
  612. ValueLength = ( ARGUMENT_PRESENT( lpcbData ) )? *lpcbData : 0;
  613. if( IsLocalHandle( hKey )) {
  614. Error = (LONG)LocalBaseRegQueryValue (
  615. hKey,
  616. &ValueName,
  617. &ValueType,
  618. lpData,
  619. &ValueLength,
  620. &InputLength
  621. );
  622. } else {
  623. Error = (LONG)BaseRegQueryValue (
  624. DereferenceRemoteHandle( hKey ),
  625. &ValueName,
  626. &ValueType,
  627. lpData,
  628. &ValueLength,
  629. &InputLength
  630. );
  631. }
  632. //
  633. // Special hack to help out all the people who
  634. // believe the length of a NULL terminated string is
  635. // strlen(foo) instead of strlen(foo) + 1.
  636. // If the last character of the buffer is not a NULL
  637. // and there is enough space left in the caller's buffer,
  638. // slap a NULL in there to prevent him from going nuts
  639. // trying to do a strlen().
  640. //
  641. if ( (Error == ERROR_SUCCESS) &&
  642. ARGUMENT_PRESENT( lpData ) &&
  643. ( (ValueType == REG_SZ) ||
  644. (ValueType == REG_EXPAND_SZ) ||
  645. (ValueType==REG_MULTI_SZ)) &&
  646. ( ValueLength > sizeof(WCHAR))) {
  647. UNALIGNED WCHAR *String = (UNALIGNED WCHAR *)lpData;
  648. DWORD Length = ValueLength/sizeof(WCHAR);
  649. if ((String[Length-1] != UNICODE_NULL) &&
  650. (ValueLength+sizeof(WCHAR) <= *lpcbData)) {
  651. String[Length] = UNICODE_NULL;
  652. }
  653. }
  654. if( ARGUMENT_PRESENT( lpcbData ) ) {
  655. *lpcbData = ValueLength;
  656. }
  657. if ( ARGUMENT_PRESENT( lpdwType )) {
  658. *lpdwType = ValueType;
  659. }
  660. ExitCleanup:
  661. CLOSE_LOCAL_HANDLE(TempHandle);
  662. return Error;
  663. }