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.

974 lines
18 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. /* File: strclass.cpp
  3. Description: Typical class to handle strings.
  4. Revision History:
  5. Date Description Programmer
  6. -------- --------------------------------------------------- ----------
  7. 07/01/97 Initial creation. BrianAu
  8. */
  9. ///////////////////////////////////////////////////////////////////////////////
  10. #include "pch.h"
  11. #pragma hdrstop
  12. #include "strclass.h"
  13. #ifdef StrCpy
  14. # undef StrCpy
  15. #endif
  16. #ifdef StrCpyN
  17. # undef StrCpyN
  18. #endif
  19. #ifdef StrLen
  20. # undef StrLen
  21. #endif
  22. #ifdef UNICODE
  23. # define StrCpy StrCpyW
  24. # define StrCpyN StrCpyNW
  25. # define StrLen StrLenW
  26. #else
  27. # define StrCpy StrCpyA
  28. # define StrCpyN StrCpyNA
  29. # define StrLen StrLenA
  30. #endif // UNICODE
  31. const INT MAX_RESOURCE_STR_LEN = 4097;
  32. //
  33. // Disable "'operator ->' is not a UDT or reference to a UDT" warning.
  34. // This is caused when we create an autoptr to a non-UDT. It's meaningless
  35. // since there's no reason to call operator-> on a non-UDT autoptr.
  36. //
  37. #pragma warning (disable : 4284)
  38. CString::CString(
  39. VOID
  40. ) : m_pValue(new StringValue())
  41. {
  42. }
  43. CString::CString(
  44. INT cch
  45. ) : m_pValue(new StringValue(cch))
  46. {
  47. }
  48. CString::CString(
  49. LPCSTR pszA
  50. ) : m_pValue(new StringValue(pszA))
  51. {
  52. }
  53. CString::CString(
  54. LPCWSTR pszW
  55. ) : m_pValue(new StringValue(pszW))
  56. {
  57. }
  58. CString::CString(
  59. const CString& rhs
  60. ) : m_pValue(rhs.m_pValue)
  61. {
  62. InterlockedIncrement(&(m_pValue->m_cRef));
  63. }
  64. CString::CString(
  65. HINSTANCE hInst,
  66. INT idMsg,
  67. ...
  68. ) : m_pValue(NULL)
  69. {
  70. LPTSTR pszMsg = NULL;
  71. va_list args;
  72. va_start(args, idMsg);
  73. Format(hInst, idMsg, &args);
  74. va_end(args);
  75. }
  76. CString::~CString(
  77. VOID
  78. )
  79. {
  80. if (NULL != m_pValue)
  81. {
  82. ASSERT( 0 != m_pValue->m_cRef );
  83. if (0 == InterlockedDecrement(&(m_pValue->m_cRef)))
  84. {
  85. delete m_pValue;
  86. }
  87. }
  88. }
  89. //
  90. // Length of string, excluding nul terminator.
  91. //
  92. INT
  93. CString::Length(
  94. VOID
  95. ) const
  96. {
  97. return m_pValue->Length();
  98. }
  99. VOID
  100. CString::Empty(
  101. VOID
  102. )
  103. {
  104. ASSERT( 0 != m_pValue->m_cRef );
  105. if (0 == InterlockedDecrement(&(m_pValue->m_cRef)))
  106. {
  107. delete m_pValue;
  108. }
  109. m_pValue = NULL;
  110. m_pValue = new StringValue();
  111. }
  112. BOOL
  113. CString::IsEmpty(
  114. VOID
  115. ) const
  116. {
  117. return (NULL != m_pValue && 0 == m_pValue->Length());
  118. }
  119. CString&
  120. CString::operator = (
  121. const CString& rhs
  122. )
  123. {
  124. if (m_pValue != rhs.m_pValue) // Chk for assignment to *this.
  125. {
  126. ASSERT( 0 != m_pValue->m_cRef );
  127. if (0 == InterlockedDecrement(&(m_pValue->m_cRef)))
  128. {
  129. delete m_pValue;
  130. }
  131. m_pValue = rhs.m_pValue;
  132. InterlockedIncrement(&(m_pValue->m_cRef));
  133. }
  134. return *this;
  135. }
  136. CString&
  137. CString::operator = (
  138. LPCWSTR rhsW
  139. )
  140. {
  141. ASSERT( 0 != m_pValue->m_cRef );
  142. if (0 == InterlockedDecrement(&(m_pValue->m_cRef)))
  143. {
  144. delete m_pValue;
  145. }
  146. //
  147. // The StringValue ctor can throw an allocation exception.
  148. // Make sure the m_pValue variable is in a consistent state
  149. // beforehand.
  150. //
  151. m_pValue = NULL;
  152. m_pValue = new StringValue(rhsW);
  153. return *this;
  154. }
  155. CString&
  156. CString::operator = (
  157. LPCSTR rhsA
  158. )
  159. {
  160. ASSERT( 0 != m_pValue->m_cRef );
  161. if (0 == InterlockedDecrement(&(m_pValue->m_cRef)))
  162. {
  163. delete m_pValue;
  164. }
  165. //
  166. // The StringValue ctor can throw an allocation exception.
  167. // Make sure the m_pValue variable is in a consistent state
  168. // beforehand.
  169. //
  170. m_pValue = NULL;
  171. m_pValue = new StringValue(rhsA);
  172. return *this;
  173. }
  174. CString
  175. CString::operator + (
  176. const CString& rhs
  177. ) const
  178. {
  179. CString strNew;
  180. LPTSTR pszTemp = NULL;
  181. try
  182. {
  183. pszTemp = StringValue::Concat(m_pValue, rhs.m_pValue);
  184. strNew = pszTemp;
  185. }
  186. catch(...)
  187. {
  188. delete[] pszTemp;
  189. throw;
  190. }
  191. delete[] pszTemp;
  192. return strNew;
  193. }
  194. CString&
  195. CString::operator += (
  196. const CString& rhs
  197. )
  198. {
  199. LPTSTR pszTemp = NULL;
  200. try
  201. {
  202. pszTemp = StringValue::Concat(m_pValue, rhs.m_pValue);
  203. *this = pszTemp;
  204. }
  205. catch(...)
  206. {
  207. delete[] pszTemp;
  208. throw;
  209. }
  210. delete[] pszTemp;
  211. return *this;
  212. }
  213. BOOL
  214. CString::operator == (
  215. const CString& rhs
  216. ) const
  217. {
  218. return (0 == lstrcmp(m_pValue->m_psz, rhs.m_pValue->m_psz));
  219. }
  220. INT
  221. CString::Compare(
  222. LPCWSTR rhsW
  223. ) const
  224. {
  225. StringValue Value(rhsW);
  226. return lstrcmp(m_pValue->m_psz, Value.m_psz);
  227. }
  228. INT
  229. CString::Compare(
  230. LPCSTR rhsA
  231. ) const
  232. {
  233. StringValue Value(rhsA);
  234. return lstrcmp(m_pValue->m_psz, Value.m_psz);
  235. }
  236. INT
  237. CString::CompareNoCase(
  238. LPCWSTR rhsW
  239. ) const
  240. {
  241. StringValue Value(rhsW);
  242. return lstrcmpi(m_pValue->m_psz, Value.m_psz);
  243. }
  244. INT
  245. CString::CompareNoCase(
  246. LPCSTR rhsA
  247. ) const
  248. {
  249. StringValue Value(rhsA);
  250. return lstrcmpi(m_pValue->m_psz, Value.m_psz);
  251. }
  252. BOOL
  253. CString::operator < (
  254. const CString& rhs
  255. ) const
  256. {
  257. return (0 > lstrcmp(m_pValue->m_psz, rhs.m_pValue->m_psz));
  258. }
  259. TCHAR
  260. CString::operator[](
  261. INT index
  262. ) const
  263. {
  264. if (!ValidIndex(index))
  265. throw CMemoryException(CMemoryException::index);
  266. return m_pValue->m_psz[index];
  267. }
  268. TCHAR&
  269. CString::operator[](
  270. INT index
  271. )
  272. {
  273. if (!ValidIndex(index))
  274. throw CMemoryException(CMemoryException::index);
  275. CopyOnWrite();
  276. return m_pValue->m_psz[index];
  277. }
  278. INT
  279. CString::First(
  280. TCHAR ch
  281. ) const
  282. {
  283. LPCTSTR psz = m_pValue->m_psz;
  284. LPCTSTR pszLast = psz;
  285. INT i = 0;
  286. while(psz && *psz)
  287. {
  288. if (ch == *psz)
  289. return i;
  290. psz = CharNext(psz);
  291. i += (INT)(psz - pszLast);
  292. pszLast = psz;
  293. }
  294. return -1;
  295. }
  296. INT
  297. CString::Last(
  298. TCHAR ch
  299. ) const
  300. {
  301. INT iLast = -1;
  302. INT i = 0;
  303. LPCTSTR psz = m_pValue->m_psz;
  304. LPCTSTR pszPrev = psz;
  305. while(psz && *psz)
  306. {
  307. if (ch == *psz)
  308. iLast = i;
  309. psz = CharNext(psz);
  310. i += (INT)(psz - pszPrev);
  311. pszPrev = psz;
  312. }
  313. return iLast;
  314. }
  315. CString
  316. CString::SubString(
  317. INT iFirst,
  318. INT cch
  319. )
  320. {
  321. if (!ValidIndex(iFirst))
  322. throw CMemoryException(CMemoryException::index);
  323. INT cchToEnd = Length() - iFirst;
  324. if (-1 == cch || cch > cchToEnd)
  325. return CString(m_pValue->m_psz + iFirst);
  326. LPTSTR pszTemp = new TCHAR[cch + 1];
  327. if (NULL == pszTemp)
  328. throw CAllocException();
  329. CString::StrCpyN(pszTemp, m_pValue->m_psz + iFirst, cch + 1);
  330. CString strTemp(pszTemp);
  331. delete[] pszTemp;
  332. return strTemp;
  333. }
  334. VOID
  335. CString::ToUpper(
  336. INT iFirst,
  337. INT cch
  338. )
  339. {
  340. if (!ValidIndex(iFirst))
  341. throw CMemoryException(CMemoryException::index);
  342. CopyOnWrite();
  343. INT cchToEnd = Length() - iFirst;
  344. if (-1 == cch || cch > cchToEnd)
  345. cch = cchToEnd;
  346. CharUpperBuff(m_pValue->m_psz + iFirst, cch);
  347. }
  348. VOID
  349. CString::ToLower(
  350. INT iFirst,
  351. INT cch
  352. )
  353. {
  354. if (!ValidIndex(iFirst))
  355. throw CMemoryException(CMemoryException::index);
  356. CopyOnWrite();
  357. INT cchToEnd = Length() - iFirst;
  358. if (-1 == cch || cch > cchToEnd)
  359. cch = cchToEnd;
  360. CharLowerBuff(m_pValue->m_psz + iFirst, cch);
  361. }
  362. VOID
  363. CString::Size(
  364. INT cch
  365. )
  366. {
  367. StringValue *m_psv = new StringValue(cch + 1);
  368. CString::StrCpyN(m_psv->m_psz, m_pValue->m_psz, cch);
  369. ASSERT( 0 != m_pValue->m_cRef );
  370. if (0 == InterlockedDecrement(&m_pValue->m_cRef))
  371. {
  372. delete m_pValue;
  373. }
  374. m_pValue = m_psv;
  375. }
  376. VOID
  377. CString::CopyOnWrite(
  378. VOID
  379. )
  380. {
  381. //
  382. // Only need to copy if ref cnt > 1.
  383. //
  384. if (m_pValue->m_cRef > 1)
  385. {
  386. StringValue * pValue = new StringValue(m_pValue->m_psz);
  387. ASSERT( 0 != m_pValue->m_cRef );
  388. if (0 == InterlockedDecrement(&(m_pValue->m_cRef)))
  389. {
  390. delete m_pValue;
  391. }
  392. m_pValue = pValue;
  393. }
  394. }
  395. BOOL
  396. CString::Format(
  397. LPCTSTR pszFmt,
  398. ...
  399. )
  400. {
  401. BOOL bResult;
  402. va_list args;
  403. va_start(args, pszFmt);
  404. bResult = Format(pszFmt, &args);
  405. va_end(args);
  406. return bResult;
  407. }
  408. BOOL
  409. CString::Format(
  410. LPCTSTR pszFmt,
  411. va_list *pargs
  412. )
  413. {
  414. BOOL bResult = FALSE;
  415. TCHAR szBuffer[MAX_RESOURCE_STR_LEN];
  416. INT cchLoaded;
  417. cchLoaded = ::FormatMessage(FORMAT_MESSAGE_FROM_STRING,
  418. pszFmt,
  419. 0,
  420. 0,
  421. szBuffer,
  422. ARRAYSIZE(szBuffer),
  423. pargs);
  424. if (0 < cchLoaded)
  425. {
  426. ASSERT( NULL == m_pValue || 0 != m_pValue->m_cRef );
  427. if (NULL != m_pValue && 0 == InterlockedDecrement(&(m_pValue->m_cRef)))
  428. {
  429. delete m_pValue;
  430. }
  431. m_pValue = NULL;
  432. m_pValue = new StringValue(szBuffer);
  433. bResult = TRUE;
  434. }
  435. else
  436. {
  437. DWORD dwLastError = GetLastError();
  438. if (ERROR_SUCCESS != dwLastError)
  439. {
  440. DBGERROR((TEXT("CString::Format failed with error 0x%08X"), dwLastError));
  441. throw CResourceException(CResourceException::string, NULL, 0);
  442. }
  443. }
  444. return bResult;
  445. }
  446. BOOL
  447. CString::Format(
  448. HINSTANCE hInst,
  449. UINT idFmt,
  450. ...
  451. )
  452. {
  453. BOOL bResult;
  454. va_list args;
  455. va_start(args, idFmt);
  456. bResult = Format(hInst, idFmt, &args);
  457. va_end(args);
  458. return bResult;
  459. }
  460. BOOL
  461. CString::Format(
  462. HINSTANCE hInst,
  463. UINT idFmt,
  464. va_list *pargs
  465. )
  466. {
  467. BOOL bResult = FALSE;
  468. TCHAR szFmtStr[MAX_RESOURCE_STR_LEN]; // Buffer for format string (if needed).
  469. INT cchLoaded;
  470. //
  471. // Try to load the format string as a string resource.
  472. //
  473. cchLoaded = ::LoadString(hInst, idFmt, szFmtStr, ARRAYSIZE(szFmtStr));
  474. if (0 < cchLoaded)
  475. {
  476. //
  477. // The format string was in a string resource.
  478. // Now format it with the arg list.
  479. //
  480. bResult = Format(szFmtStr, pargs);
  481. }
  482. else
  483. {
  484. TCHAR szBuffer[MAX_RESOURCE_STR_LEN];
  485. //
  486. // The format string may be in a message resource.
  487. // Note that if it is, the resulting formatted string will
  488. // be automatically attached to m_psz by ::FormatMessage.
  489. //
  490. cchLoaded = ::FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
  491. FORMAT_MESSAGE_FROM_HMODULE,
  492. hInst,
  493. (DWORD)idFmt,
  494. LANGIDFROMLCID(GetThreadLocale()),
  495. (LPTSTR)szBuffer,
  496. ARRAYSIZE(szBuffer),
  497. pargs);
  498. if (0 < cchLoaded)
  499. {
  500. ASSERT( NULL == m_pValue || 0 != m_pValue->m_cRef );
  501. if (NULL != m_pValue && 0 == InterlockedDecrement(&(m_pValue->m_cRef)))
  502. {
  503. delete m_pValue;
  504. }
  505. m_pValue = NULL;
  506. m_pValue = new StringValue(szBuffer);
  507. bResult = TRUE;
  508. }
  509. else
  510. {
  511. DWORD dwLastError = GetLastError();
  512. if (ERROR_SUCCESS != dwLastError)
  513. {
  514. DBGERROR((TEXT("CString::Format failed with error 0x%08X"), dwLastError));
  515. throw CResourceException(CResourceException::string, hInst, idFmt);
  516. }
  517. }
  518. }
  519. return bResult;
  520. }
  521. LPTSTR
  522. CString::GetBuffer(
  523. INT cchMax
  524. )
  525. {
  526. if (-1 == cchMax)
  527. cchMax = m_pValue->m_cchAlloc;
  528. CopyOnWrite();
  529. if (cchMax > m_pValue->m_cchAlloc)
  530. {
  531. //
  532. // Extend the buffer, copying original contents to dest.
  533. //
  534. StringValue *pv = new StringValue(cchMax);
  535. StrCpyN(pv->m_psz, m_pValue->m_psz, cchMax);
  536. ASSERT( 0 != m_pValue->m_cRef );
  537. if (0 == InterlockedDecrement(&(m_pValue->m_cRef)))
  538. {
  539. delete m_pValue;
  540. }
  541. m_pValue = pv;
  542. LPTSTR pszEnd = m_pValue->m_psz + m_pValue->m_cchAlloc - 1;
  543. if (pszEnd >= m_pValue->m_psz && TEXT('\0') != *(pszEnd))
  544. {
  545. //
  546. // Ensure it's nul terminated.
  547. //
  548. *(pszEnd) = TEXT('\0');
  549. }
  550. }
  551. return m_pValue->m_psz;
  552. }
  553. VOID
  554. CString::ReleaseBuffer(
  555. void
  556. )
  557. {
  558. //
  559. // Update the string length member after client has had free access
  560. // to internal buffer.
  561. //
  562. m_pValue->m_cch = StrLen(m_pValue->m_psz);
  563. }
  564. void
  565. CString::Rtrim(
  566. void
  567. )
  568. {
  569. LPTSTR s = GetBuffer();
  570. int len = Length();
  571. while(0 < --len && IsWhiteSpace(s[len]))
  572. s[len] = TEXT('\0');
  573. ReleaseBuffer();
  574. }
  575. void
  576. CString::Ltrim(
  577. void
  578. )
  579. {
  580. LPTSTR s0;
  581. LPTSTR s = s0 = GetBuffer();
  582. while(*s && IsWhiteSpace(*s))
  583. s++;
  584. while(*s)
  585. *s0++ = *s++;
  586. *s0 = TEXT('\0');
  587. ReleaseBuffer();
  588. }
  589. VOID
  590. CString::ExpandEnvironmentStrings(
  591. VOID
  592. )
  593. {
  594. DWORD cchBuffer = 0; // Size of expansion buffer.
  595. DWORD cchPath = 0; // Count of actual chars in expanded buffer.
  596. CString strExpanded; // Expansion buffer.
  597. //
  598. // If necessary, keep increasing expansion buffer size until entire
  599. // expanded string fits.
  600. //
  601. do
  602. {
  603. cchBuffer += MAX_PATH;
  604. cchPath = ::ExpandEnvironmentStrings(*this,
  605. strExpanded.GetBuffer(cchBuffer),
  606. cchBuffer);
  607. }
  608. while(0 != cchPath && cchPath > cchBuffer);
  609. ReleaseBuffer();
  610. *this = strExpanded;
  611. }
  612. bool
  613. CString::GetDisplayRect(
  614. HDC hdc,
  615. LPRECT prc
  616. ) const
  617. {
  618. return (0 != DrawText(hdc, Cstr(), Length(), prc, DT_CALCRECT));
  619. }
  620. VOID
  621. CString::DebugOut(
  622. BOOL bNewline
  623. ) const
  624. {
  625. OutputDebugString(m_pValue->m_psz);
  626. if (bNewline)
  627. OutputDebugString(TEXT("\n\r"));
  628. }
  629. CString::StringValue::StringValue(
  630. VOID
  631. ) : m_psz(new TCHAR[1]),
  632. m_cchAlloc(1),
  633. m_cch(0),
  634. m_cRef(1)
  635. {
  636. *m_psz = TEXT('\0');
  637. }
  638. CString::StringValue::StringValue(
  639. LPCSTR pszA
  640. ) : m_psz(NULL),
  641. m_cchAlloc(0),
  642. m_cch(0),
  643. m_cRef(1)
  644. {
  645. #ifdef UNICODE
  646. m_psz = AnsiToWide(pszA, &m_cchAlloc);
  647. m_cch = StrLenW(m_psz);
  648. #else
  649. m_cch = CString::StrLenA(pszA);
  650. m_psz = Dup(pszA, m_cch + 1);
  651. m_cchAlloc = m_cch + 1;
  652. #endif
  653. }
  654. CString::StringValue::StringValue(
  655. LPCWSTR pszW
  656. ) : m_psz(NULL),
  657. m_cchAlloc(0),
  658. m_cch(0),
  659. m_cRef(1)
  660. {
  661. #ifdef UNICODE
  662. m_cch = CString::StrLenW(pszW);
  663. m_psz = Dup(pszW, m_cch + 1);
  664. m_cchAlloc = m_cch + 1;
  665. #else
  666. m_psz = WideToAnsi(pszW, &m_cchAlloc);
  667. m_cch = StrLenA(m_psz);
  668. #endif
  669. }
  670. CString::StringValue::StringValue(
  671. INT cch
  672. ) : m_psz(NULL),
  673. m_cchAlloc(0),
  674. m_cch(0),
  675. m_cRef(0)
  676. {
  677. m_psz = Dup(TEXT(""), cch);
  678. m_cRef = 1;
  679. m_cchAlloc = cch;
  680. }
  681. CString::StringValue::~StringValue(
  682. VOID
  683. )
  684. {
  685. delete[] m_psz;
  686. }
  687. LPWSTR
  688. CString::StringValue::AnsiToWide(
  689. LPCSTR pszA,
  690. INT *pcch
  691. )
  692. {
  693. INT cchW = 0;
  694. LPWSTR pszW = NULL;
  695. cchW = MultiByteToWideChar(CP_ACP,
  696. 0,
  697. pszA,
  698. -1,
  699. NULL,
  700. 0);
  701. pszW = new WCHAR[cchW];
  702. if (NULL == pszW)
  703. throw CAllocException();
  704. MultiByteToWideChar(CP_ACP,
  705. 0,
  706. pszA,
  707. -1,
  708. pszW,
  709. cchW);
  710. if (NULL != pcch)
  711. *pcch = cchW;
  712. return pszW;
  713. }
  714. LPSTR
  715. CString::StringValue::WideToAnsi(
  716. LPCWSTR pszW,
  717. INT *pcch
  718. )
  719. {
  720. INT cchA = 0;
  721. LPSTR pszA = NULL;
  722. cchA = WideCharToMultiByte(CP_ACP,
  723. 0,
  724. pszW,
  725. -1,
  726. NULL,
  727. 0,
  728. NULL,
  729. NULL);
  730. pszA = new CHAR[cchA];
  731. if (NULL == pszA)
  732. throw CAllocException();
  733. WideCharToMultiByte(CP_ACP,
  734. 0,
  735. pszW,
  736. -1,
  737. pszA,
  738. cchA,
  739. NULL,
  740. NULL);
  741. if (NULL != pcch)
  742. *pcch = cchA;
  743. return pszA;
  744. }
  745. INT
  746. CString::StringValue::Length(
  747. VOID
  748. ) const
  749. {
  750. if (0 == m_cch && NULL != m_psz)
  751. {
  752. m_cch = StrLen(m_psz);
  753. }
  754. return m_cch;
  755. }
  756. LPWSTR
  757. CString::StringValue::Dup(
  758. LPCWSTR pszW,
  759. INT cch
  760. )
  761. {
  762. if (0 == cch)
  763. cch = CString::StrLenW(pszW) + 1;
  764. LPWSTR pszNew = new WCHAR[cch];
  765. if (NULL == pszNew)
  766. throw CAllocException();
  767. lstrcpynW(pszNew, pszW, cch);
  768. return pszNew;
  769. }
  770. LPSTR
  771. CString::StringValue::Dup(
  772. LPCSTR pszA,
  773. INT cch
  774. )
  775. {
  776. if (0 == cch)
  777. cch = CString::StrLenA(pszA) + 1;
  778. LPSTR pszNew = new CHAR[cch];
  779. if (NULL == pszNew)
  780. throw CAllocException();
  781. lstrcpynA(pszNew, pszA, cch);
  782. return pszNew;
  783. }
  784. LPTSTR
  785. CString::StringValue::Concat(
  786. CString::StringValue *psv1,
  787. CString::StringValue *psv2
  788. )
  789. {
  790. LPTSTR pszTemp = NULL;
  791. INT len1 = psv1->Length();
  792. INT len2 = psv2->Length();
  793. const INT cch = len1 + len2 + 1;
  794. pszTemp = new TCHAR[cch];
  795. if (NULL == pszTemp)
  796. throw CAllocException();
  797. wnsprintf(pszTemp, cch, TEXT("%s%s"), psv1->m_psz, psv2->m_psz);
  798. return pszTemp;
  799. }
  800. #pragma warning (default : 4284)