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.

645 lines
21 KiB

  1. /****************************** Module Header ******************************\
  2. * Module Name: chartran.c
  3. *
  4. * Copyright (c) 1985 - 1999, Microsoft Corporation
  5. *
  6. * This module contains the routines for translating ACP characters
  7. * to Unicode and translating Unicode characters to ACP characters.
  8. * NOTE: The ACP is the currently installed 8-bit code page.
  9. *
  10. *
  11. * History:
  12. * 08-01-91 GregoryW Created.
  13. * 05-14-92 GregoryW Modified to use the Rtl translation routines.
  14. \***************************************************************************/
  15. extern __declspec(dllimport) USHORT NlsAnsiCodePage;
  16. #define IS_ACP(cp) (((cp) == NlsAnsiCodePage) || ((cp) == CP_ACP))
  17. /***************************************************************************\
  18. * WCSToMBEx (API)
  19. *
  20. * Convert a wide-character (Unicode) string to MBCS (ANSI) string.
  21. *
  22. * nAnsiChar > 0 indicates the number of bytes to allocate to store the
  23. * ANSI string (if bAllocateMem == TRUE) or the size of the buffer
  24. * pointed to by *pAnsiString (bAllocateMem == FALSE).
  25. *
  26. * nAnsiChar == -1 indicates that the necessary number of bytes be allocated
  27. * to hold the translated string. bAllocateMem must be set to TRUE in
  28. * this case.
  29. *
  30. * Return value
  31. * Success: number of characters in the output string
  32. * If bAllocateMem was TRUE, then FreeAnsiString() may be
  33. * used to free the allocated memory at *ppAnsiString.
  34. * Failure: 0 means failure
  35. * (Any buffers allocated by this routine are freed)
  36. *
  37. * History:
  38. * 1992-??-?? GregoryW Created
  39. * 1993-01-07 IanJa fix memory leak on error case.
  40. \***************************************************************************/
  41. int
  42. WCSToMBEx(
  43. WORD wCodePage,
  44. LPCWSTR pUnicodeString,
  45. int cchUnicodeString,
  46. LPSTR *ppAnsiString,
  47. int nAnsiChar,
  48. BOOL bAllocateMem)
  49. {
  50. ULONG nCharsInAnsiString;
  51. #ifdef _USERK_
  52. INT iCharsInAnsiString;
  53. #endif // _USERK_
  54. if (nAnsiChar == 0 || cchUnicodeString == 0 || pUnicodeString == NULL) {
  55. return 0; // nothing to translate or nowhere to put it
  56. }
  57. /*
  58. * Adjust the cchUnicodeString value. If cchUnicodeString == -1 then the
  59. * string pointed to by pUnicodeString is NUL terminated so we
  60. * count the number of bytes. If cchUnicodeString < -1 this is an
  61. * illegal value so we return FALSE. Otherwise, cchUnicodeString is
  62. * set and requires no adjustment.
  63. */
  64. if (cchUnicodeString == -1) {
  65. cchUnicodeString = (wcslen(pUnicodeString) + 1);
  66. } else if (cchUnicodeString < -1) {
  67. return 0; // illegal value
  68. }
  69. /*
  70. * Adjust the nAnsiChar value. If nAnsiChar == -1 then we pick a
  71. * value based on cchUnicodeString to hold the converted string. If
  72. * nAnsiChar < -1 this is an illegal value so we return FALSE.
  73. * Otherwise, nAnsiChar is set and requires no adjustment.
  74. */
  75. if (nAnsiChar == -1) {
  76. if (bAllocateMem == FALSE) {
  77. return 0; // no destination
  78. }
  79. nAnsiChar = cchUnicodeString * DBCS_CHARSIZE;
  80. } else if (nAnsiChar < -1) {
  81. return 0; // illegal value
  82. }
  83. if (bAllocateMem) {
  84. /*
  85. * We need to allocate memory to hold the translated string.
  86. */
  87. *ppAnsiString = (LPSTR)UserRtlAllocMem(nAnsiChar);
  88. if (*ppAnsiString == NULL) {
  89. return 0;
  90. }
  91. }
  92. /*
  93. * translate Unicode string pointed to by pUnicodeString into
  94. * ANSI and store in location pointed to by pAnsiString. We
  95. * stop translating when we fill up the ANSI buffer or reach
  96. * the end of the Unicode string.
  97. */
  98. /*
  99. * if the target multibyte codepage is eqaul to ACP, Call faster Rtl function.
  100. */
  101. if (IS_ACP(wCodePage)) {
  102. NTSTATUS Status;
  103. Status = RtlUnicodeToMultiByteN(
  104. (PCH)*ppAnsiString,
  105. nAnsiChar,
  106. &nCharsInAnsiString,
  107. (PWCH)pUnicodeString,
  108. cchUnicodeString * sizeof(WCHAR));
  109. /*
  110. * If the ansi buffer is too small, RtlUnicodeToMultiByteN()
  111. * returns STATUS_BUFFER_OVERFLOW. In this case, the function
  112. * put as many ansi characters as specified in the buffer and
  113. * returns the number by chacacters(in bytes) written. We would
  114. * like to return the actual byte count written in the ansi
  115. * buffer rather than returnning 0 since callers of this function
  116. * don't expect to be returned 0 in most case.
  117. */
  118. if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) {
  119. if (bAllocateMem) {
  120. UserRtlFreeMem(*ppAnsiString);
  121. }
  122. return 0; // translation failed
  123. }
  124. return (int)nCharsInAnsiString;
  125. } else {
  126. #ifdef _USERK_
  127. /*
  128. * Call GRE to convert string to Unicode. (Kernel mode)
  129. */
  130. iCharsInAnsiString = EngWideCharToMultiByte(
  131. (UINT)wCodePage,
  132. (LPWSTR)pUnicodeString,
  133. cchUnicodeString * sizeof(WCHAR),
  134. (LPSTR)*ppAnsiString,
  135. nAnsiChar);
  136. nCharsInAnsiString = (iCharsInAnsiString == -1) ? 0 :
  137. (ULONG) iCharsInAnsiString;
  138. #else
  139. /*
  140. * Call NLS API (Kernel32) to convert string to Unicode. (User mode)
  141. */
  142. nCharsInAnsiString = WideCharToMultiByte(
  143. (UINT)wCodePage, 0,
  144. (LPCWSTR)pUnicodeString,
  145. cchUnicodeString,
  146. (LPSTR)*ppAnsiString,
  147. nAnsiChar,
  148. NULL, NULL);
  149. #endif // _USERK_
  150. if (nCharsInAnsiString == 0) {
  151. if (bAllocateMem) {
  152. UserRtlFreeMem(*ppAnsiString);
  153. }
  154. }
  155. return (int)nCharsInAnsiString;
  156. }
  157. }
  158. // Returns number of character converted
  159. int MBToWCSEx(
  160. WORD wCodePage,
  161. LPCSTR pAnsiString,
  162. int nAnsiChar,
  163. LPWSTR *ppUnicodeString,
  164. int cchUnicodeString,
  165. BOOL bAllocateMem)
  166. {
  167. ULONG nBytesInUnicodeString;
  168. if (nAnsiChar == 0 || cchUnicodeString == 0 || pAnsiString == NULL) {
  169. return 0; // nothing to translate or nowhere to put it
  170. }
  171. /*
  172. * Adjust the nAnsiChar value. If nAnsiChar == -1 then the
  173. * string pointed to by pAnsiString is NUL terminated so we
  174. * count the number of bytes. If nAnsiChar < -1 this is an
  175. * illegal value so we return FALSE. Otherwise, nAnsiChar is
  176. * set and requires no adjustment.
  177. */
  178. #ifdef _USERK_
  179. UserAssert(nAnsiChar >= USER_AWCONV_COUNTSTRINGSZ);
  180. #endif
  181. if (nAnsiChar < 0) {
  182. /*
  183. * Bug 268035 - joejo
  184. * Need to fail if the count is a negative number less than -2!
  185. */
  186. if (nAnsiChar < USER_AWCONV_COUNTSTRINGSZ) {
  187. return 0;
  188. }
  189. #if (USER_AWCONV_COUNTSTRING != -1 || USER_AWCONV_COUNTSTRINGSZ != -2)
  190. #error USER_AWCONV_COUNTSTRING or USER_AWCONV_COUNTSTRINGSZ has unexpected value.
  191. #endif
  192. /* HACK HACK HACK
  193. * If nAnsiChar is -1 (USER_AWCONV_COUNTSTRING), nAnsiChar length will be strlen() + 1,
  194. * to allocate the memory including trailing \0: this is compatible to the original code.
  195. * If nAnsiCahr is -2 (USER_AWCONV_COUNTSTRINGSZ), memory for trailing \0 will not be needed,
  196. * so memory allocation is optimized and the return value would be same as strlen().
  197. */
  198. nAnsiChar = strlen(pAnsiString) + 2 + nAnsiChar; // don't forget the NUL if nAnsiChar == -1
  199. if (nAnsiChar == 0) {
  200. return 0;
  201. }
  202. }
  203. /*
  204. * Adjust the cchUnicodeString value. If cchUnicodeString == -1 then we
  205. * pick a value based on nAnsiChar to hold the converted string. If
  206. * cchUnicodeString < -1 this is an illegal value so we return FALSE.
  207. * Otherwise, cchUnicodeString is set and requires no adjustment.
  208. */
  209. if (cchUnicodeString == -1) {
  210. if (bAllocateMem == FALSE) {
  211. return 0; // no destination
  212. }
  213. cchUnicodeString = nAnsiChar;
  214. } else if (cchUnicodeString < -1) {
  215. return 0; // illegal value
  216. }
  217. if (bAllocateMem) {
  218. *ppUnicodeString = (LPWSTR)UserRtlAllocMem(cchUnicodeString*sizeof(WCHAR));
  219. if (*ppUnicodeString == NULL) {
  220. return 0; // allocation failed
  221. }
  222. }
  223. /*
  224. * if codepage is CP_ACP, We will call faster RtlXXX function.
  225. */
  226. if (IS_ACP(wCodePage)) {
  227. /*
  228. * translate ANSI string pointed to by pAnsiString into Unicode
  229. * and store in location pointed to by pUnicodeString. We
  230. * stop translating when we fill up the Unicode buffer or reach
  231. * the end of the ANSI string.
  232. */
  233. if (!NT_SUCCESS(RtlMultiByteToUnicodeN(
  234. (PWCH)*ppUnicodeString,
  235. cchUnicodeString * sizeof(WCHAR),
  236. &nBytesInUnicodeString,
  237. (PCH)pAnsiString,
  238. nAnsiChar
  239. ))) {
  240. if (bAllocateMem) {
  241. UserRtlFreeMem(*ppUnicodeString);
  242. }
  243. return 0; // translation failed
  244. }
  245. return (int)(nBytesInUnicodeString / sizeof(WCHAR));
  246. } else {
  247. /*
  248. * if wCodePage is not ACP, Call NLS API.
  249. */
  250. ULONG nCharsInUnicodeString;
  251. #ifdef _USERK_
  252. /*
  253. * I believe we will never hit this code which is why I am
  254. * adding this assert. [gerritv] 5-21-96
  255. */
  256. #define SHOULD_NOT_REACH_HERE 0
  257. UserAssert(SHOULD_NOT_REACH_HERE);
  258. #undef SHOULD_NOT_REACH_HERE
  259. return 0;
  260. #if 0 // FYI: old code
  261. INT iCharsInUnicodeString;
  262. /*
  263. * Call GRE to convert string to Unicode. (Kernel mode)
  264. * I believe we will never hit this code which is why I am
  265. * adding this assert. [gerritv] 5-21-96
  266. */
  267. UserAssert(0);
  268. iCharsInUnicodeString = EngMultiByteToWideChar(
  269. (UINT)wCodePage,
  270. (LPWSTR)*ppUnicodeString,
  271. (int)cchUnicodeString * sizeof(WCHAR),
  272. (LPSTR)pAnsiString,
  273. (int)nAnsiChar);
  274. nCharsInUnicodeString = (iCharsInUnicodeString == -1) ? 0 :
  275. (ULONG) iCharsInUnicodeString;
  276. #endif
  277. #else
  278. /*
  279. * Call NLS API (Kernel32) to convert string to Unicode. (User mode)
  280. */
  281. nCharsInUnicodeString = MultiByteToWideChar(
  282. (UINT)wCodePage, 0,
  283. (LPCSTR)pAnsiString,
  284. (int)nAnsiChar,
  285. (LPWSTR)*ppUnicodeString,
  286. (int)cchUnicodeString);
  287. #endif // _USERK_
  288. if (nCharsInUnicodeString == 0) {
  289. if (bAllocateMem) {
  290. UserRtlFreeMem(*ppUnicodeString);
  291. }
  292. }
  293. return (int)nCharsInUnicodeString;
  294. }
  295. }
  296. /**************************************************************************\
  297. * RtlWCSMessageWParmCharToMB
  298. *
  299. * Converts a Wide Character to a Multibyte character; in place
  300. * Returns the number of characters converted or zero if failure
  301. *
  302. * 11-Feb-1992 JohnC Created
  303. \**************************************************************************/
  304. BOOL RtlWCSMessageWParamCharToMB(DWORD msg, WPARAM *pWParam)
  305. {
  306. DWORD dwAnsi;
  307. NTSTATUS Status;
  308. WORD CodePage;
  309. int nbWch;
  310. #ifdef FE_SB // RtlWCSMessageWParamCharToMB()
  311. //
  312. // Format of *pWParam here...
  313. //
  314. // LOWORD(*pWParam) = Unicode CodePoint...
  315. // HIWORD(*pWParam) = Has some information for DBCS messaging
  316. // (ex. WPARAM_IR_DBCSCHAR)
  317. //
  318. // Then we need to convert ONLY loword of wParam to Unicode...
  319. //
  320. #endif // FE_SB
  321. #ifndef FE_SB
  322. // NtBug #3135 (Closed 02/04/93)
  323. // Publisher Posts WM_CHAR messages with wParam > 0xFF (not a valid ANSI char)!
  324. //
  325. // It does this to disable TranslateAccelerator for that char.
  326. // MSPub's winproc must get the non-ANSI 'character' value, so PostMessage must
  327. // translate *two* characters of wParam for character messages, and PeekMessage
  328. // must translate *two* Unicode chars of wParam for ANSI app.
  329. #endif
  330. /*
  331. * Only these messages have CHARs: others are passed through
  332. */
  333. switch(msg) {
  334. #ifdef FE_IME // RtlWCSMessageWParamCharToMB()
  335. case WM_IME_CHAR:
  336. case WM_IME_COMPOSITION:
  337. #endif // FE_IME
  338. case WM_CHAR:
  339. case WM_CHARTOITEM:
  340. case EM_SETPASSWORDCHAR:
  341. case WM_DEADCHAR:
  342. case WM_SYSCHAR:
  343. case WM_SYSDEADCHAR:
  344. case WM_MENUCHAR:
  345. CodePage = THREAD_CODEPAGE();
  346. dwAnsi = 0;
  347. nbWch = IS_DBCS_ENABLED() ? 1 * sizeof(WCHAR) : 2 * sizeof(WCHAR);
  348. if (IS_ACP(CodePage)) {
  349. // HACK HACK HACK HACK (for NtBug #3135)
  350. // to allow applications that store data in high word of wParam
  351. // Jan/06/96 hiroyama
  352. Status = RtlUnicodeToMultiByteN((LPSTR)&dwAnsi, sizeof(dwAnsi),
  353. NULL, (LPWSTR)pWParam, nbWch);
  354. if (!NT_SUCCESS(Status)) {
  355. // LATER IanJa: returning FALSE makes GetMessage fail, which
  356. // terminates the app. We should use some default 'bad character'
  357. // I use 0x00 for now.
  358. *pWParam = 0x00;
  359. return TRUE;
  360. }
  361. } else {
  362. int cwch;
  363. // assuming little endian
  364. #ifdef _USERK_
  365. cwch = EngWideCharToMultiByte(CodePage,
  366. (LPWSTR)pWParam, nbWch,
  367. (LPSTR)&dwAnsi, sizeof(dwAnsi));
  368. #else
  369. cwch = WideCharToMultiByte(CodePage, 0,
  370. (LPCWSTR)pWParam, nbWch / sizeof(WCHAR),
  371. (LPSTR)&dwAnsi, sizeof(dwAnsi), NULL, NULL);
  372. #endif // _USERK_
  373. // KdPrint(("0x%04x -> 0x%02x (%d)\n", *pWParam, dwAnsi, CodePage));
  374. if (cwch == 0) {
  375. *pWParam = 0x00;
  376. return TRUE;
  377. }
  378. }
  379. if (IS_DBCS_ENABLED()) {
  380. WORD wAnsi = LOWORD(dwAnsi);
  381. //
  382. // From:
  383. // HIBYTE(wAnsi) = Dbcs TrailingByte.
  384. // LOBYTE(wAnsi) = Dbcs LeadingByte or Sbcs character.
  385. //
  386. // To:
  387. // HIWORD(*pWParam) = Original Data (information for DBCS messgaing).
  388. // HIBYTE(LOWORD(*pWParam)) = Dbcs LeadingByte Byte.
  389. // LOBYTE(LOWORD(*pWParam)) = Dbcs TrailingByte or Sbcs character.
  390. //
  391. if (IS_DBCS_MESSAGE(wAnsi)) {
  392. //
  393. // It's a DBCS character.
  394. //
  395. *pWParam = MAKEWPARAM(MAKEWORD(HIBYTE(wAnsi),LOBYTE(wAnsi)),HIWORD(*pWParam));
  396. } else {
  397. //
  398. // It's a SBCS character.
  399. //
  400. *pWParam = MAKEWPARAM(MAKEWORD(LOBYTE(wAnsi),0),0);
  401. }
  402. } else {
  403. #if DBG
  404. if ((dwAnsi == 0) || (dwAnsi > 0xFF)) {
  405. RIPMSG1(RIP_VERBOSE, "msgW -> msgA: char = 0x%.4lX\n", dwAnsi);
  406. }
  407. #endif
  408. *pWParam = dwAnsi;
  409. }
  410. break;
  411. }
  412. return TRUE;
  413. }
  414. /**************************************************************************\
  415. * RtlMBMessageCharToWCS
  416. *
  417. * Converts a Multibyte character to a Wide character; in place
  418. * Returns the number of characters converted or zero if failure
  419. *
  420. * 11-Feb-1992 JohnC Created
  421. * 13-Jan-1993 IanJa Translate 2 characters (Publisher posts these!)
  422. \**************************************************************************/
  423. BOOL RtlMBMessageWParamCharToWCS(DWORD msg, WPARAM *pWParam)
  424. {
  425. DWORD dwUni;
  426. NTSTATUS Status;
  427. // FE_SB (RtlMBMessageWParamCharToWCS)
  428. BOOL bWmCrIrDbcsChar = FALSE;
  429. WORD wAnsi = LOWORD(*pWParam);
  430. // end FE_SB (RtlMBMessageWParamCharToWCS)
  431. WORD CodePage = THREAD_CODEPAGE();
  432. /*
  433. * Only these messages have CHARs: others are passed through
  434. */
  435. switch(msg) {
  436. // FE_SB (RtlMBMessageWParamCharToWCS)
  437. case WM_CHAR:
  438. //
  439. // WM_CHAR's wParam format for WM_IME_REPORT:IR_DBCSCHAR
  440. //
  441. if (IS_DBCS_ENABLED() && (*pWParam & WMCR_IR_DBCSCHAR)) {
  442. //
  443. // Mark this message is sent as IR_DBCSCHAR format.
  444. //
  445. bWmCrIrDbcsChar = TRUE;
  446. }
  447. //
  448. // Fall through....
  449. //
  450. #ifdef FE_IME
  451. case WM_IME_CHAR:
  452. case WM_IME_COMPOSITION:
  453. //
  454. // We need to re-align for Unicode convertsion..
  455. // WM_CHAR/WM_IME_CHAR/WM_IME_COMPOSITION's wParam format :
  456. //
  457. // ReAlign IR_DBCS char format to regular sequence.
  458. //
  459. // From:
  460. //
  461. // HIWORD(wParam) = 0;
  462. // HIBYTE(LOWORD(wParam)) = DBCS LeadingByte.
  463. // LOBYTE(LOWORD(wParan)) = DBCS TrailingByte or SBCS character.
  464. //
  465. // To:
  466. // HIWORD(wParam) = 0;
  467. // HIBYTE(LOWORD(wParam)) = DBCS TrailingByte.
  468. // LOBYTE(LOWORD(wParam)) = DBCS LeadingByte or SBCS character.
  469. //
  470. if (IS_DBCS_ENABLED()) {
  471. *pWParam = MAKE_WPARAM_DBCSCHAR(wAnsi);
  472. }
  473. #endif
  474. //
  475. // Fall through...
  476. //
  477. // end FE_SB (RtlMBMessageWParamCharToWCS)
  478. case WM_CHARTOITEM:
  479. case EM_SETPASSWORDCHAR:
  480. case WM_DEADCHAR:
  481. case WM_SYSCHAR:
  482. case WM_SYSDEADCHAR:
  483. case WM_MENUCHAR:
  484. dwUni = 0;
  485. if (IS_ACP(CodePage)) {
  486. Status = RtlMultiByteToUnicodeN((LPWSTR)&dwUni, sizeof(dwUni),
  487. NULL, (LPSTR)pWParam, 2 * sizeof(CHAR));
  488. if (!NT_SUCCESS(Status))
  489. return FALSE;
  490. } else {
  491. int cwch;
  492. #ifdef _USERK_
  493. cwch = EngMultiByteToWideChar(CodePage,
  494. (LPWSTR)&dwUni, sizeof(dwUni),
  495. (LPSTR)pWParam, 2);
  496. #else
  497. cwch = MultiByteToWideChar(CodePage, 0,
  498. (LPSTR)pWParam, 2,
  499. (LPWSTR)&dwUni, sizeof(dwUni) / sizeof(WCHAR));
  500. #endif // _USERK_
  501. // KdPrint(("0x%02x -> 0x%04x (%d)\n", *pWParam, dwUni, CodePage));
  502. if (cwch == 0) {
  503. return FALSE;
  504. }
  505. }
  506. // FE_SB (RtlMBMessageWParamCharToWCS)
  507. //
  508. // if this character is sent for WM_IME_REPORT:IR_DBCSCHAR, we mark it.
  509. //
  510. if (bWmCrIrDbcsChar)
  511. dwUni |= WMCR_IR_DBCSCHAR;
  512. // else FE_SB (RtlMBMessageWParamCharToWCS)
  513. #if DBG
  514. if ((dwUni == 0) || (dwUni > 0xFF)) {
  515. RIPMSG1(RIP_VERBOSE, "msgA -> msgW: wchar = 0x%lX\n", dwUni);
  516. }
  517. #endif
  518. // end FE_SB
  519. *pWParam = dwUni;
  520. break;
  521. }
  522. return TRUE;
  523. }
  524. /**************************************************************************\
  525. * RtlInitLargeAnsiString
  526. *
  527. * Captures a large ANSI string in the same manner as
  528. * RtlInitAnsiString.
  529. *
  530. * 03-22-95 JimA Created.
  531. \**************************************************************************/
  532. VOID RtlInitLargeAnsiString(
  533. PLARGE_ANSI_STRING plstr,
  534. LPCSTR psz,
  535. UINT cchLimit)
  536. {
  537. ULONG Length;
  538. plstr->Buffer = (PSTR)psz;
  539. plstr->bAnsi = TRUE;
  540. if (ARGUMENT_PRESENT( psz )) {
  541. Length = strlen( psz );
  542. plstr->Length = min(Length, cchLimit);
  543. plstr->MaximumLength = min((Length + 1), cchLimit);
  544. } else {
  545. plstr->MaximumLength = 0;
  546. plstr->Length = 0;
  547. }
  548. }
  549. /**************************************************************************\
  550. * RtlInitLargeUnicodeString
  551. *
  552. * Captures a large unicode string in the same manner as
  553. * RtlInitUnicodeString.
  554. *
  555. * 03-22-95 JimA Created.
  556. \**************************************************************************/
  557. VOID RtlInitLargeUnicodeString(
  558. PLARGE_UNICODE_STRING plstr,
  559. LPCWSTR psz,
  560. UINT cchLimit)
  561. {
  562. ULONG Length;
  563. plstr->Buffer = (PWSTR)psz;
  564. plstr->bAnsi = FALSE;
  565. if (ARGUMENT_PRESENT( psz )) {
  566. Length = wcslen( psz ) * sizeof( WCHAR );
  567. plstr->Length = min(Length, cchLimit);
  568. plstr->MaximumLength = min((Length + sizeof(UNICODE_NULL)), cchLimit);
  569. } else {
  570. plstr->MaximumLength = 0;
  571. plstr->Length = 0;
  572. }
  573. }