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.

2235 lines
61 KiB

  1. /*++
  2. Copyright (c) 2001-2002 Microsoft Corporation
  3. Module Name:
  4. CString.cpp
  5. Abstract:
  6. A CString class, pure UNICODE internally.
  7. This code was ripped from MFC Strcore.cpp and Strex.cpp
  8. History:
  9. 05/11/2001 robkenny Added this header
  10. 05/11/2001 robkenny Fixed SplitPath.
  11. 05/11/2001 robkenny Do not Truncate(0) GetShortPathNameW,
  12. GetLongPathNameW and GetFullPathNameW if
  13. the API does not succeed.
  14. 08/14/2001 robkenny Moved code inside the ShimLib namespace.
  15. 02/28/2002 robkenny Security review.
  16. --*/
  17. #include "ShimHook.h"
  18. #include "StrSafe.h"
  19. #include "Win9xPath.h"
  20. namespace ShimLib
  21. {
  22. typedef WCHAR _TUCHAR;
  23. struct _AFX_DOUBLE { BYTE doubleBits[sizeof(double)]; };
  24. #ifdef USE_SEH
  25. const ULONG_PTR CString::m_CStringExceptionValue = CString::eCStringExceptionValue;
  26. // Exception filter for CString __try/__except blocks
  27. // Return EXCEPTION_EXECUTE_HANDLER if this is a CString exception
  28. // otherwise return EXCEPTION_CONTINUE_SEARCH
  29. int CString::ExceptionFilter(PEXCEPTION_POINTERS pexi)
  30. {
  31. if (pexi->ExceptionRecord->ExceptionCode == CString::eCStringNoMemoryException &&
  32. pexi->ExceptionRecord->NumberParameters == 1 &&
  33. pexi->ExceptionRecord->ExceptionInformation[0] == CString::m_CStringExceptionValue
  34. )
  35. {
  36. // This is a CString exception, handle it
  37. return EXCEPTION_EXECUTE_HANDLER;
  38. }
  39. // Not our error
  40. return EXCEPTION_CONTINUE_SEARCH;
  41. }
  42. #endif
  43. // The original code was written using a memcpy that incorrectly handled
  44. // overlapping buffers, despite the documentation.
  45. // Replace memcpy with memmove, which correctly handles overlapping buffers
  46. #define memcpy memmove
  47. const WCHAR * wcsinc(const WCHAR * s1)
  48. {
  49. return (s1) + 1;
  50. }
  51. LPWSTR wcsinc(LPWSTR s1)
  52. {
  53. return (s1) + 1;
  54. }
  55. // WCS routines that are only available in MSVCRT
  56. wchar_t * __cdecl _wcsrev (
  57. wchar_t * string
  58. )
  59. {
  60. wchar_t *start = string;
  61. wchar_t *left = string;
  62. wchar_t ch;
  63. while (*string++) /* find end of string */
  64. ;
  65. string -= 2;
  66. while (left < string)
  67. {
  68. ch = *left;
  69. *left++ = *string;
  70. *string-- = ch;
  71. }
  72. return(start);
  73. }
  74. void __cdecl _wsplitpath (
  75. register const WCHAR *path,
  76. WCHAR *drive,
  77. WCHAR *dir,
  78. WCHAR *fname,
  79. WCHAR *ext
  80. )
  81. {
  82. register WCHAR *p;
  83. WCHAR *last_slash = NULL, *dot = NULL;
  84. unsigned len;
  85. /* we assume that the path argument has the following form, where any
  86. * or all of the components may be missing.
  87. *
  88. * <drive><dir><fname><ext>
  89. *
  90. * and each of the components has the following expected form(s)
  91. *
  92. * drive:
  93. * 0 to _MAX_DRIVE-1 characters, the last of which, if any, is a
  94. * ':'
  95. * dir:
  96. * 0 to _MAX_DIR-1 characters in the form of an absolute path
  97. * (leading '/' or '\') or relative path, the last of which, if
  98. * any, must be a '/' or '\'. E.g -
  99. * absolute path:
  100. * \top\next\last\ ; or
  101. * /top/next/last/
  102. * relative path:
  103. * top\next\last\ ; or
  104. * top/next/last/
  105. * Mixed use of '/' and '\' within a path is also tolerated
  106. * fname:
  107. * 0 to _MAX_FNAME-1 characters not including the '.' character
  108. * ext:
  109. * 0 to _MAX_EXT-1 characters where, if any, the first must be a
  110. * '.'
  111. *
  112. */
  113. /* extract drive letter and :, if any */
  114. if ((wcslen(path) >= (_MAX_DRIVE - 2)) && (*(path + _MAX_DRIVE - 2) == L':')) {
  115. if (drive) {
  116. wcsncpy(drive, path, _MAX_DRIVE - 1);
  117. *(drive + _MAX_DRIVE-1) = L'\0';
  118. }
  119. path += _MAX_DRIVE - 1;
  120. }
  121. else if (drive) {
  122. *drive = L'\0';
  123. }
  124. /* extract path string, if any. Path now points to the first character
  125. * of the path, if any, or the filename or extension, if no path was
  126. * specified. Scan ahead for the last occurence, if any, of a '/' or
  127. * '\' path separator character. If none is found, there is no path.
  128. * We will also note the last '.' character found, if any, to aid in
  129. * handling the extension.
  130. */
  131. for (last_slash = NULL, p = (WCHAR *)path; *p; p++) {
  132. if (*p == L'/' || *p == L'\\')
  133. /* point to one beyond for later copy */
  134. last_slash = p + 1;
  135. else if (*p == L'.')
  136. dot = p;
  137. }
  138. if (last_slash) {
  139. /* found a path - copy up through last_slash or max. characters
  140. * allowed, whichever is smaller
  141. */
  142. if (dir) {
  143. len = __min((unsigned)(((char *)last_slash - (char *)path) / sizeof(WCHAR)),
  144. (_MAX_DIR - 1));
  145. wcsncpy(dir, path, len);
  146. *(dir + len) = L'\0';
  147. }
  148. path = last_slash;
  149. }
  150. else if (dir) {
  151. /* no path found */
  152. *dir = L'\0';
  153. }
  154. /* extract file name and extension, if any. Path now points to the
  155. * first character of the file name, if any, or the extension if no
  156. * file name was given. Dot points to the '.' beginning the extension,
  157. * if any.
  158. */
  159. if (dot && (dot >= path)) {
  160. /* found the marker for an extension - copy the file name up to
  161. * the '.'.
  162. */
  163. if (fname) {
  164. len = __min((unsigned)(((char *)dot - (char *)path) / sizeof(WCHAR)),
  165. (_MAX_FNAME - 1));
  166. wcsncpy(fname, path, len);
  167. *(fname + len) = L'\0';
  168. }
  169. /* now we can get the extension - remember that p still points
  170. * to the terminating nul character of path.
  171. */
  172. if (ext) {
  173. len = __min((unsigned)(((char *)p - (char *)dot) / sizeof(WCHAR)),
  174. (_MAX_EXT - 1));
  175. wcsncpy(ext, dot, len);
  176. *(ext + len) = L'\0';
  177. }
  178. }
  179. else {
  180. /* found no extension, give empty extension and copy rest of
  181. * string into fname.
  182. */
  183. if (fname) {
  184. len = __min((unsigned)(((char *)p - (char *)path) / sizeof(WCHAR)),
  185. (_MAX_FNAME - 1));
  186. wcsncpy(fname, path, len);
  187. *(fname + len) = L'\0';
  188. }
  189. if (ext) {
  190. *ext = L'\0';
  191. }
  192. }
  193. }
  194. // conversion helpers
  195. int AFX_CDECL _mbstowcsz(wchar_t* wcstr, const char* mbstr, size_t count);
  196. // AfxIsValidString() returns TRUE if the passed pointer
  197. // references a string of at least the given length in characters.
  198. // A length of -1 (the default parameter) means that the string
  199. // buffer's minimum length isn't known, and the function will
  200. // return TRUE no matter how long the string is. The memory
  201. // used by the string can be read-only.
  202. BOOL AFXAPI AfxIsValidString(LPCWSTR lpsz, int nLength /* = -1 */)
  203. {
  204. if (lpsz == NULL)
  205. return FALSE;
  206. return ::IsBadStringPtrW(lpsz, nLength) == 0;
  207. }
  208. // AfxIsValidAddress() returns TRUE if the passed parameter points
  209. // to at least nBytes of accessible memory. If bReadWrite is TRUE,
  210. // the memory must be writeable; if bReadWrite is FALSE, the memory
  211. // may be const.
  212. BOOL AFXAPI AfxIsValidAddress(const void* lp, UINT nBytes, BOOL bReadWrite /* = TRUE */)
  213. {
  214. // simple version using Win-32 APIs for pointer validation.
  215. return (lp != NULL && !IsBadReadPtr(lp, nBytes) &&
  216. (!bReadWrite || !IsBadWritePtr((LPVOID)lp, nBytes)));
  217. }
  218. /////////////////////////////////////////////////////////////////////////////
  219. // static class data, special inlines
  220. WCHAR CString::ChNil = L'\0';
  221. // For an empty string, m_pchData will point here
  222. // (note: avoids special case of checking for NULL m_pchData)
  223. // empty string data (and locked)
  224. int CString::_afxInitData[] = { -1, 0, 0, 0 };
  225. CStringData<WCHAR> * CString::_afxDataNil = (CStringData<WCHAR>*)&_afxInitData;
  226. const WCHAR * CString::_afxPchNil = (const WCHAR *)(((BYTE*)&_afxInitData)+sizeof(CStringData<WCHAR>));
  227. // special function to make afxEmptyString work even during initialization
  228. //const CString& AFXAPI AfxGetEmptyString()
  229. // { return *(CString*)&CString::_afxPchNil; }
  230. //////////////////////////////////////////////////////////////////////////////
  231. // Construction/Destruction
  232. CString::CString(const CString& stringSrc)
  233. {
  234. ASSERT(stringSrc.GetData()->nRefs != 0, "CString::CString(const CString& stringSrc)");
  235. if (stringSrc.GetData()->nRefs >= 0)
  236. {
  237. ASSERT(stringSrc.GetData() != _afxDataNil, "CString::CString(const CString& stringSrc)");
  238. // robkenny: increment before copy is safer
  239. InterlockedIncrement(&stringSrc.GetData()->nRefs);
  240. m_pchData = stringSrc.m_pchData;
  241. m_pchDataAnsi = NULL;
  242. }
  243. else
  244. {
  245. Init();
  246. *this = stringSrc.m_pchData;
  247. }
  248. }
  249. inline DWORD Round4(DWORD x)
  250. {
  251. return (x + 3) & ~3;
  252. }
  253. inline DWORD RoundBin(int x)
  254. {
  255. return Round4( ((DWORD)x) * sizeof(WCHAR) + sizeof(CStringData<WCHAR>) );
  256. }
  257. void CString::AllocBuffer(int nLen)
  258. // always allocate one extra character for '\0' termination
  259. // assumes [optimistically] that data length will equal allocation length
  260. {
  261. ASSERT(nLen >= 0, "CString::AllocBuffer");
  262. ASSERT(nLen <= INT_MAX-1, "CString::AllocBuffer"); // max size (enough room for 1 extra)
  263. if (nLen == 0)
  264. {
  265. Init();
  266. }
  267. else
  268. {
  269. int cchAllocSize = nLen;
  270. if (nLen < 64)
  271. {
  272. cchAllocSize = 64;
  273. }
  274. else if (nLen < 128)
  275. {
  276. cchAllocSize = 128;
  277. }
  278. else if (nLen < MAX_PATH)
  279. {
  280. cchAllocSize = MAX_PATH;
  281. }
  282. else if (nLen < 512)
  283. {
  284. cchAllocSize = 512;
  285. }
  286. // ------------------------------------------------------------------
  287. // Note: We allocate an extra byte that is not added to nAllocLength
  288. // this is so that whenever the code checks to determine if the buffer
  289. // is large enough it doesn't have to remember to add one for the
  290. // null character.
  291. // This is how the original CString was written.
  292. // ------------------------------------------------------------------
  293. // Calculate the number of bytes necessary for the CStringData thingy.
  294. DWORD ccbAllocSize = RoundBin(cchAllocSize + 1);
  295. // Check for overflow:
  296. // If they pass in a negative number, throw the exception
  297. // If ccbAllocSize is unsigned: the only way it can be smaller
  298. // than cchAllocSize would be if RoundBin overflowed.
  299. if ((cchAllocSize < 0) || (ccbAllocSize < (DWORD)cchAllocSize))
  300. {
  301. CSTRING_THROW_EXCEPTION
  302. }
  303. CStringData<WCHAR>* pData = (CStringData<WCHAR>*) new BYTE[ ccbAllocSize ];
  304. if (pData)
  305. {
  306. pData->nAllocLength = cchAllocSize;
  307. pData->nRefs = 1;
  308. pData->data()[nLen] = '\0';
  309. pData->nDataLength = nLen;
  310. m_pchData = pData->data();
  311. }
  312. else
  313. {
  314. CSTRING_THROW_EXCEPTION
  315. }
  316. }
  317. }
  318. void CString::Release()
  319. {
  320. if (GetData() != _afxDataNil)
  321. {
  322. ASSERT(GetData()->nRefs != 0, "CString::Release()");
  323. if (InterlockedDecrement(&GetData()->nRefs) <= 0)
  324. FreeData(GetData());
  325. Init();
  326. }
  327. }
  328. void CString::Release(CStringData<WCHAR>* pData)
  329. {
  330. if (pData != _afxDataNil)
  331. {
  332. ASSERT(pData->nRefs != 0, "CString::Release(CStringData<WCHAR>* pData)");
  333. if (InterlockedDecrement(&pData->nRefs) <= 0)
  334. FreeData(pData);
  335. }
  336. }
  337. void CString::Empty()
  338. {
  339. if (GetData()->nDataLength == 0)
  340. return;
  341. if (GetData()->nRefs >= 0)
  342. Release();
  343. else
  344. *this = &ChNil;
  345. ASSERT(GetData()->nDataLength == 0, "CString::Empty()");
  346. ASSERT(GetData()->nRefs < 0 || GetData()->nAllocLength == 0, "CString::Empty()");
  347. }
  348. void CString::CopyBeforeWrite()
  349. {
  350. if (GetData()->nRefs > 1)
  351. {
  352. CStringData<WCHAR>* pData = GetData();
  353. Release();
  354. AllocBuffer(pData->nDataLength);
  355. memcpy(m_pchData, pData->data(), (pData->nDataLength+1)*sizeof(WCHAR));
  356. }
  357. ASSERT(GetData()->nRefs <= 1, "CString::CopyBeforeWrite()");
  358. }
  359. void CString::AllocBeforeWrite(int nLen)
  360. {
  361. if (GetData()->nRefs > 1 || nLen > GetData()->nAllocLength)
  362. {
  363. Release();
  364. AllocBuffer(nLen);
  365. }
  366. ASSERT(GetData()->nRefs <= 1, "CString::AllocBeforeWrite(int nLen)");
  367. }
  368. CString::~CString()
  369. // free any attached data
  370. {
  371. if (GetData() != _afxDataNil)
  372. {
  373. if (InterlockedDecrement(&GetData()->nRefs) <= 0)
  374. FreeData(GetData());
  375. }
  376. if (m_pchDataAnsi)
  377. {
  378. free(m_pchDataAnsi);
  379. }
  380. }
  381. //////////////////////////////////////////////////////////////////////////////
  382. // Helpers for the rest of the implementation
  383. void CString::AllocCopy(CString& dest, int nCopyLen, int nCopyIndex,
  384. int nExtraLen) const
  385. {
  386. // Copy nCopyIndex to nCopyIndex+nCopyLen into dest
  387. // Make sure dest has nExtraLen chars left over in the dest string
  388. int nNewLen = nCopyLen + nExtraLen;
  389. if (nNewLen == 0)
  390. {
  391. dest.Init();
  392. }
  393. else
  394. {
  395. WCHAR * lpszDestBuffer = dest.GetBuffer(nNewLen);
  396. memcpy(lpszDestBuffer, m_pchData+nCopyIndex, nCopyLen*sizeof(WCHAR));
  397. dest.ReleaseBuffer(nCopyLen);
  398. }
  399. }
  400. ///////////////////////////////////////////////////////////////////////////////
  401. // CString conversion helpers (these use the current system locale)
  402. int AFX_CDECL _mbstowcsz(wchar_t* wcstr, const char* mbstr, size_t count)
  403. {
  404. if (count == 0 && wcstr != NULL)
  405. return 0;
  406. int result = ::MultiByteToWideChar(CP_ACP, 0, mbstr, -1,
  407. wcstr, count);
  408. ASSERT(wcstr == NULL || result <= (int)count, "CString::_mbstowcsz");
  409. if (result > 0)
  410. wcstr[result-1] = 0;
  411. return result;
  412. }
  413. //////////////////////////////////////////////////////////////////////////////
  414. // More sophisticated construction
  415. CString::CString(LPCWSTR lpsz)
  416. {
  417. Init();
  418. {
  419. int nLen = SafeStrlen(lpsz);
  420. if (nLen != 0)
  421. {
  422. AllocBuffer(nLen);
  423. memcpy(m_pchData, lpsz, nLen*sizeof(WCHAR));
  424. }
  425. }
  426. }
  427. /////////////////////////////////////////////////////////////////////////////
  428. // Special conversion constructors
  429. CString::CString(LPCSTR lpsz)
  430. {
  431. Init();
  432. int nSrcLen = lpsz != NULL ? strlenChars(lpsz) : 0;
  433. if (nSrcLen != 0)
  434. {
  435. AllocBuffer(nSrcLen);
  436. _mbstowcsz(m_pchData, lpsz, nSrcLen+1);
  437. ReleaseBuffer();
  438. }
  439. }
  440. CString::CString(LPCSTR lpsz, int nCharacters)
  441. {
  442. Init();
  443. if (nCharacters != 0)
  444. {
  445. AllocBuffer(nCharacters);
  446. _mbstowcsz(m_pchData, lpsz, nCharacters);
  447. ReleaseBuffer(nCharacters);
  448. }
  449. }
  450. //////////////////////////////////////////////////////////////////////////////
  451. // Diagnostic support
  452. #ifdef _DEBUG
  453. CDumpContext& AFXAPI operator<<(CDumpContext& dc, const CString& string)
  454. {
  455. dc << string.m_pchData;
  456. return dc;
  457. }
  458. #endif //_DEBUG
  459. //////////////////////////////////////////////////////////////////////////////
  460. // Assignment operators
  461. // All assign a new value to the string
  462. // (a) first see if the buffer is big enough
  463. // (b) if enough room, copy on top of old buffer, set size and type
  464. // (c) otherwise free old string data, and create a new one
  465. //
  466. // All routines return the new string (but as a 'const CString&' so that
  467. // assigning it again will cause a copy, eg: s1 = s2 = "hi there".
  468. //
  469. void CString::AssignCopy(int nSrcLen, LPCWSTR lpszSrcData)
  470. {
  471. AllocBeforeWrite(nSrcLen);
  472. memcpy(m_pchData, lpszSrcData, nSrcLen*sizeof(WCHAR));
  473. GetData()->nDataLength = nSrcLen;
  474. m_pchData[nSrcLen] = '\0';
  475. }
  476. const CString& CString::operator=(const CString& stringSrc)
  477. {
  478. if (m_pchData != stringSrc.m_pchData)
  479. {
  480. if ((GetData()->nRefs < 0 && GetData() != _afxDataNil) ||
  481. stringSrc.GetData()->nRefs < 0)
  482. {
  483. // actual copy necessary since one of the strings is locked
  484. AssignCopy(stringSrc.GetData()->nDataLength, stringSrc.m_pchData);
  485. }
  486. else
  487. {
  488. // can just copy references around
  489. Release();
  490. ASSERT(stringSrc.GetData() != _afxDataNil, "CString::operator=(const CString& stringSrc)");
  491. // robkenny: increment before copy is safer
  492. InterlockedIncrement(&stringSrc.GetData()->nRefs);
  493. m_pchData = stringSrc.m_pchData;
  494. m_pchDataAnsi = NULL;
  495. }
  496. }
  497. return *this;
  498. }
  499. const CString& CString::operator=(LPCWSTR lpsz)
  500. {
  501. ASSERT(lpsz == NULL || AfxIsValidString(lpsz), "CString::operator=(LPCWSTR lpsz)");
  502. AssignCopy(SafeStrlen(lpsz), lpsz);
  503. return *this;
  504. }
  505. /////////////////////////////////////////////////////////////////////////////
  506. // Special conversion assignment
  507. const CString& CString::operator=(LPCSTR lpsz)
  508. {
  509. int nSrcLen = lpsz != NULL ? strlenChars(lpsz) : 0;
  510. AllocBeforeWrite(nSrcLen);
  511. _mbstowcsz(m_pchData, lpsz, nSrcLen+1);
  512. ReleaseBuffer();
  513. return *this;
  514. }
  515. //////////////////////////////////////////////////////////////////////////////
  516. // concatenation
  517. // NOTE: "operator+" is done as friend functions for simplicity
  518. // There are three variants:
  519. // CString + CString
  520. // and for ? = WCHAR, LPCWSTR
  521. // CString + ?
  522. // ? + CString
  523. void CString::ConcatCopy(int nSrc1Len, LPCWSTR lpszSrc1Data,
  524. int nSrc2Len, LPCWSTR lpszSrc2Data)
  525. {
  526. // -- master concatenation routine
  527. // Concatenate two sources
  528. // -- assume that 'this' is a new CString object
  529. int nNewLen = nSrc1Len + nSrc2Len;
  530. if (nNewLen != 0)
  531. {
  532. AllocBuffer(nNewLen);
  533. memcpy(m_pchData, lpszSrc1Data, nSrc1Len*sizeof(WCHAR));
  534. memcpy(m_pchData+nSrc1Len, lpszSrc2Data, nSrc2Len*sizeof(WCHAR));
  535. }
  536. }
  537. CString AFXAPI operator+(const CString& string1, const CString& string2)
  538. {
  539. CString s;
  540. s.ConcatCopy(string1.GetData()->nDataLength, string1.m_pchData,
  541. string2.GetData()->nDataLength, string2.m_pchData);
  542. return s;
  543. }
  544. CString AFXAPI operator+(const CString& string, LPCWSTR lpsz)
  545. {
  546. ASSERT(lpsz == NULL || AfxIsValidString(lpsz), "CString::operator+(const CString& string, LPCWSTR lpsz)");
  547. CString s;
  548. s.ConcatCopy(string.GetData()->nDataLength, string.m_pchData,
  549. CString::SafeStrlen(lpsz), lpsz);
  550. return s;
  551. }
  552. CString AFXAPI operator+(LPCWSTR lpsz, const CString& string)
  553. {
  554. ASSERT(lpsz == NULL || AfxIsValidString(lpsz), "CString::operator+(LPCWSTR lpsz, const CString& string)");
  555. CString s;
  556. s.ConcatCopy(CString::SafeStrlen(lpsz), lpsz, string.GetData()->nDataLength,
  557. string.m_pchData);
  558. return s;
  559. }
  560. //////////////////////////////////////////////////////////////////////////////
  561. // concatenate in place
  562. void CString::ConcatInPlace(int nSrcLen, LPCWSTR lpszSrcData)
  563. {
  564. // -- the main routine for += operators
  565. // concatenating an empty string is a no-op!
  566. if (nSrcLen == 0)
  567. return;
  568. // if the buffer is too small, or we have a width mis-match, just
  569. // allocate a new buffer (slow but sure)
  570. if (GetData()->nRefs > 1 || GetData()->nDataLength + nSrcLen > GetData()->nAllocLength)
  571. {
  572. // we have to grow the buffer, use the ConcatCopy routine
  573. CStringData<WCHAR>* pOldData = GetData();
  574. ConcatCopy(GetData()->nDataLength, m_pchData, nSrcLen, lpszSrcData);
  575. ASSERT(pOldData != NULL, "CString::ConcatInPlace");
  576. CString::Release(pOldData);
  577. }
  578. else
  579. {
  580. // fast concatenation when buffer big enough
  581. memcpy(m_pchData+GetData()->nDataLength, lpszSrcData, nSrcLen*sizeof(WCHAR));
  582. GetData()->nDataLength += nSrcLen;
  583. ASSERT(GetData()->nDataLength <= GetData()->nAllocLength, "CString::ConcatInPlace");
  584. m_pchData[GetData()->nDataLength] = '\0';
  585. }
  586. }
  587. const CString& CString::operator+=(LPCWSTR lpsz)
  588. {
  589. ASSERT(lpsz == NULL || AfxIsValidString(lpsz), "CString::operator+=(LPCWSTR lpsz)");
  590. ConcatInPlace(SafeStrlen(lpsz), lpsz);
  591. return *this;
  592. }
  593. const CString& CString::operator+=(WCHAR ch)
  594. {
  595. ConcatInPlace(1, &ch);
  596. return *this;
  597. }
  598. const CString& CString::operator+=(const CString& string)
  599. {
  600. ConcatInPlace(string.GetData()->nDataLength, string.m_pchData);
  601. return *this;
  602. }
  603. ///////////////////////////////////////////////////////////////////////////////
  604. // Advanced direct buffer access
  605. LPWSTR CString::GetBuffer(int nMinBufLength)
  606. {
  607. ASSERT(nMinBufLength >= 0, "CString::GetBuffer");
  608. if (GetData()->nRefs > 1 || nMinBufLength > GetData()->nAllocLength)
  609. {
  610. #ifdef _DEBUG
  611. // give a warning in case locked string becomes unlocked
  612. if (GetData() != _afxDataNil && GetData()->nRefs < 0)
  613. TRACE0("Warning: GetBuffer on locked CString creates unlocked CString!\n");
  614. #endif
  615. // we have to grow the buffer
  616. CStringData<WCHAR>* pOldData = GetData();
  617. int nOldLen = GetData()->nDataLength; // AllocBuffer will tromp it
  618. if (nMinBufLength < nOldLen)
  619. nMinBufLength = nOldLen;
  620. AllocBuffer(nMinBufLength);
  621. memcpy(m_pchData, pOldData->data(), (nOldLen+1)*sizeof(WCHAR));
  622. GetData()->nDataLength = nOldLen;
  623. CString::Release(pOldData);
  624. }
  625. ASSERT(GetData()->nRefs <= 1, "CString::GetBuffer");
  626. // return a pointer to the character storage for this string
  627. ASSERT(m_pchData != NULL, "CString::GetBuffer");
  628. return m_pchData;
  629. }
  630. void CString::ReleaseBuffer(int nNewLength)
  631. {
  632. CopyBeforeWrite(); // just in case GetBuffer was not called
  633. if (nNewLength == -1)
  634. nNewLength = wcslen(m_pchData); // zero terminated
  635. ASSERT(nNewLength <= GetData()->nAllocLength, "CString::ReleaseBuffer");
  636. GetData()->nDataLength = nNewLength;
  637. m_pchData[nNewLength] = '\0';
  638. }
  639. LPWSTR CString::GetBufferSetLength(int nNewLength)
  640. {
  641. ASSERT(nNewLength >= 0, "CString::GetBufferSetLength");
  642. GetBuffer(nNewLength);
  643. GetData()->nDataLength = nNewLength;
  644. m_pchData[nNewLength] = '\0';
  645. return m_pchData;
  646. }
  647. void CString::FreeExtra()
  648. {
  649. ASSERT(GetData()->nDataLength <= GetData()->nAllocLength, "CString::FreeExtra");
  650. if (GetData()->nDataLength != GetData()->nAllocLength)
  651. {
  652. CStringData<WCHAR>* pOldData = GetData();
  653. AllocBuffer(GetData()->nDataLength);
  654. memcpy(m_pchData, pOldData->data(), pOldData->nDataLength*sizeof(WCHAR));
  655. ASSERT(m_pchData[GetData()->nDataLength] == '\0', "CString::FreeExtra");
  656. CString::Release(pOldData);
  657. }
  658. ASSERT(GetData() != NULL, "CString::FreeExtra");
  659. }
  660. LPWSTR CString::LockBuffer()
  661. {
  662. LPWSTR lpsz = GetBuffer(0);
  663. GetData()->nRefs = -1;
  664. return lpsz;
  665. }
  666. void CString::UnlockBuffer()
  667. {
  668. ASSERT(GetData()->nRefs == -1, "CString::UnlockBuffer");
  669. if (GetData() != _afxDataNil)
  670. GetData()->nRefs = 1;
  671. }
  672. ///////////////////////////////////////////////////////////////////////////////
  673. // Commonly used routines (rarely used routines in STREX.CPP)
  674. int CString::Find(WCHAR ch) const
  675. {
  676. return Find(ch, 0);
  677. }
  678. int CString::Find(WCHAR ch, int nStart) const
  679. {
  680. int nLength = GetData()->nDataLength;
  681. if (nStart >= nLength)
  682. return -1;
  683. // find first single character
  684. LPWSTR lpsz = wcschr(m_pchData + nStart, (_TUCHAR)ch);
  685. // return -1 if not found and index otherwise
  686. return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
  687. }
  688. int CString::FindOneOf(LPCWSTR lpszCharSet) const
  689. {
  690. return FindOneOf(lpszCharSet, 0);
  691. }
  692. int CString::FindOneOf(LPCWSTR lpszCharSet, int nCount) const
  693. {
  694. ASSERT(AfxIsValidString(lpszCharSet), "CString::FindOneOf");
  695. LPCWSTR lpsz = wcspbrk(m_pchData + nCount, lpszCharSet);
  696. return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
  697. }
  698. int CString::FindOneNotOf(const WCHAR * lpszCharSet, int nCount) const
  699. {
  700. ASSERT(AfxIsValidString(lpszCharSet), "CString::FindOneNotOf");
  701. while (wcschr(lpszCharSet, m_pchData[nCount]))
  702. {
  703. nCount += 1;
  704. }
  705. if (nCount >= GetLength())
  706. {
  707. // entire string contains lpszCharSet
  708. return -1;
  709. }
  710. return nCount;
  711. }
  712. void CString::MakeUpper()
  713. {
  714. CopyBeforeWrite();
  715. _wcsupr(m_pchData);
  716. }
  717. void CString::MakeLower()
  718. {
  719. CopyBeforeWrite();
  720. _wcslwr(m_pchData);
  721. }
  722. void CString::MakeReverse()
  723. {
  724. CopyBeforeWrite();
  725. _wcsrev(m_pchData);
  726. }
  727. void CString::SetAt(int nIndex, WCHAR ch)
  728. {
  729. ASSERT(nIndex >= 0, "CString::SetAt");
  730. ASSERT(nIndex < GetData()->nDataLength, "CString::SetAt");
  731. CopyBeforeWrite();
  732. m_pchData[nIndex] = ch;
  733. }
  734. ///////////////////////////////////////////////////////////////////////////////
  735. // More sophisticated construction
  736. CString::CString(WCHAR ch, int nLength)
  737. {
  738. Init();
  739. if (nLength >= 1)
  740. {
  741. AllocBuffer(nLength);
  742. for (int i = 0; i < nLength; i++)
  743. m_pchData[i] = ch;
  744. }
  745. }
  746. CString::CString(int nLength)
  747. {
  748. Init();
  749. if (nLength >= 1)
  750. {
  751. AllocBuffer(nLength);
  752. GetData()->nDataLength = 0;
  753. }
  754. }
  755. CString::CString(LPCWSTR lpch, int nLength)
  756. {
  757. Init();
  758. if (nLength != 0)
  759. {
  760. ASSERT(AfxIsValidAddress(lpch, nLength, FALSE), "CString::CString(LPCWSTR lpch, int nLength)");
  761. AllocBuffer(nLength);
  762. memcpy(m_pchData, lpch, nLength*sizeof(WCHAR));
  763. }
  764. }
  765. /////////////////////////////////////////////////////////////////////////////
  766. // Special conversion constructors
  767. //////////////////////////////////////////////////////////////////////////////
  768. // Assignment operators
  769. const CString& CString::operator=(WCHAR ch)
  770. {
  771. AssignCopy(1, &ch);
  772. return *this;
  773. }
  774. //////////////////////////////////////////////////////////////////////////////
  775. // less common string expressions
  776. CString AFXAPI operator+(const CString& string1, WCHAR ch)
  777. {
  778. CString s;
  779. s.ConcatCopy(string1.GetData()->nDataLength, string1.m_pchData, 1, &ch);
  780. return s;
  781. }
  782. CString AFXAPI operator+(WCHAR ch, const CString& string)
  783. {
  784. CString s;
  785. s.ConcatCopy(1, &ch, string.GetData()->nDataLength, string.m_pchData);
  786. return s;
  787. }
  788. //////////////////////////////////////////////////////////////////////////////
  789. // Advanced manipulation
  790. int CString::Delete(int nIndex, int nCount /* = 1 */)
  791. {
  792. ASSERT(nIndex >= 0, "CString::Delete negative value of nIndex");
  793. ASSERT(nIndex <= GetData()->nDataLength, "CString::Delete nIndex larger than buffer size");
  794. ASSERT(nCount >= 0, "CString::Delete negative nCount");
  795. ASSERT(nCount <= GetData()->nDataLength - nIndex, "CString::Delete attempting to delete beyond end of buffer");
  796. if (nIndex < 0)
  797. nIndex = 0;
  798. int nNewLength = GetData()->nDataLength;
  799. if (nCount > 0 && nIndex < nNewLength)
  800. {
  801. // Don't let them delete beyond the end of the string.
  802. if (nCount > nNewLength - nIndex)
  803. {
  804. nCount = nNewLength - nIndex;
  805. }
  806. CopyBeforeWrite();
  807. int nBytesToCopy = nNewLength - nIndex - nCount + 1;
  808. memcpy(m_pchData + nIndex,
  809. m_pchData + nIndex + nCount, nBytesToCopy * sizeof(WCHAR));
  810. GetData()->nDataLength = nNewLength - nCount;
  811. }
  812. return nNewLength;
  813. }
  814. int CString::Insert(int nIndex, WCHAR ch)
  815. {
  816. CopyBeforeWrite();
  817. if (nIndex < 0)
  818. nIndex = 0;
  819. int nNewLength = GetData()->nDataLength;
  820. if (nIndex > nNewLength)
  821. nIndex = nNewLength;
  822. nNewLength++;
  823. if (GetData()->nAllocLength < nNewLength)
  824. {
  825. CStringData<WCHAR>* pOldData = GetData();
  826. LPWSTR pstr = m_pchData;
  827. AllocBuffer(nNewLength);
  828. memcpy(m_pchData, pstr, (pOldData->nDataLength+1)*sizeof(WCHAR));
  829. CString::Release(pOldData);
  830. }
  831. // move existing bytes down
  832. memcpy(m_pchData + nIndex + 1,
  833. m_pchData + nIndex, (nNewLength-nIndex)*sizeof(WCHAR));
  834. m_pchData[nIndex] = ch;
  835. GetData()->nDataLength = nNewLength;
  836. return nNewLength;
  837. }
  838. int CString::Insert(int nIndex, LPCWSTR pstr)
  839. {
  840. if (nIndex < 0)
  841. nIndex = 0;
  842. int nInsertLength = SafeStrlen(pstr);
  843. int nNewLength = GetData()->nDataLength;
  844. if (nInsertLength > 0)
  845. {
  846. CopyBeforeWrite();
  847. if (nIndex > nNewLength)
  848. nIndex = nNewLength;
  849. nNewLength += nInsertLength;
  850. if (GetData()->nAllocLength < nNewLength)
  851. {
  852. CStringData<WCHAR>* pOldData = GetData();
  853. LPWSTR lpwsz = m_pchData;
  854. AllocBuffer(nNewLength);
  855. memcpy(m_pchData, lpwsz, (pOldData->nDataLength+1)*sizeof(WCHAR));
  856. CString::Release(pOldData);
  857. }
  858. // move existing bytes down
  859. memcpy(m_pchData + nIndex + nInsertLength,
  860. m_pchData + nIndex,
  861. (nNewLength-nIndex-nInsertLength+1)*sizeof(WCHAR));
  862. memcpy(m_pchData + nIndex,
  863. pstr, nInsertLength*sizeof(WCHAR));
  864. GetData()->nDataLength = nNewLength;
  865. }
  866. return nNewLength;
  867. }
  868. int CString::Replace(WCHAR chOld, WCHAR chNew)
  869. {
  870. int nCount = 0;
  871. // short-circuit the nop case
  872. if (chOld != chNew)
  873. {
  874. // otherwise modify each character that matches in the string
  875. CopyBeforeWrite();
  876. LPWSTR psz = m_pchData;
  877. LPWSTR pszEnd = psz + GetData()->nDataLength;
  878. while (psz < pszEnd)
  879. {
  880. // replace instances of the specified character only
  881. if (*psz == chOld)
  882. {
  883. *psz = chNew;
  884. nCount++;
  885. }
  886. psz = wcsinc(psz);
  887. }
  888. }
  889. return nCount;
  890. }
  891. int CString::Replace(LPCWSTR lpszOld, LPCWSTR lpszNew)
  892. {
  893. return ReplaceRoutine(lpszOld, lpszNew, wcsstr);
  894. }
  895. int CString::ReplaceI(LPCWSTR lpszOld, LPCWSTR lpszNew)
  896. {
  897. return ReplaceRoutine(lpszOld, lpszNew, wcsistr);
  898. }
  899. int CString::ReplaceRoutine(LPCWSTR lpszOld, LPCWSTR lpszNew, _pfn_wcsstr tcsstr)
  900. {
  901. // can't have empty or NULL lpszOld
  902. int nSourceLen = SafeStrlen(lpszOld);
  903. if (nSourceLen == 0)
  904. return 0;
  905. int nReplacementLen = SafeStrlen(lpszNew);
  906. // loop once to figure out the size of the result string
  907. int nCount = 0;
  908. LPWSTR lpszStart = m_pchData;
  909. LPWSTR lpszEnd = m_pchData + GetData()->nDataLength;
  910. LPWSTR lpszTarget;
  911. while (lpszStart < lpszEnd)
  912. {
  913. while ((lpszTarget = tcsstr(lpszStart, lpszOld)) != NULL)
  914. {
  915. nCount++;
  916. lpszStart = lpszTarget + nSourceLen;
  917. }
  918. lpszStart += wcslen(lpszStart) + 1;
  919. }
  920. // if any changes were made, make them
  921. if (nCount > 0)
  922. {
  923. CopyBeforeWrite();
  924. // if the buffer is too small, just
  925. // allocate a new buffer (slow but sure)
  926. int nOldLength = GetData()->nDataLength;
  927. const int nNewLength = nOldLength + (nReplacementLen-nSourceLen)*nCount;
  928. if (GetData()->nAllocLength < nNewLength + 1 || GetData()->nRefs > 1)
  929. {
  930. CStringData<WCHAR>* pOldData = GetData();
  931. LPWSTR pstr = m_pchData;
  932. AllocBuffer(nNewLength);
  933. memcpy(m_pchData, pstr, pOldData->nDataLength*sizeof(WCHAR));
  934. CString::Release(pOldData);
  935. }
  936. // else, we just do it in-place
  937. lpszStart = m_pchData;
  938. lpszEnd = m_pchData + GetData()->nDataLength;
  939. // loop again to actually do the work
  940. while (lpszStart < lpszEnd)
  941. {
  942. while ( (lpszTarget = tcsstr(lpszStart, lpszOld)) != NULL)
  943. {
  944. int nBalance = nOldLength - ((int)(lpszTarget - m_pchData) + nSourceLen);
  945. memmove(lpszTarget + nReplacementLen, lpszTarget + nSourceLen,
  946. nBalance * sizeof(WCHAR));
  947. memcpy(lpszTarget, lpszNew, nReplacementLen*sizeof(WCHAR));
  948. lpszStart = lpszTarget + nReplacementLen;
  949. lpszStart[nBalance] = '\0';
  950. nOldLength += (nReplacementLen - nSourceLen);
  951. }
  952. lpszStart += wcslen(lpszStart) + 1;
  953. }
  954. ASSERT(m_pchData[nNewLength] == '\0', "CString::ReplaceRoutine");
  955. GetData()->nDataLength = nNewLength;
  956. }
  957. return nCount;
  958. }
  959. int CString::Remove(WCHAR chRemove)
  960. {
  961. CopyBeforeWrite();
  962. LPWSTR pstrSource = m_pchData;
  963. LPWSTR pstrDest = m_pchData;
  964. LPWSTR pstrEnd = m_pchData + GetData()->nDataLength;
  965. while (pstrSource < pstrEnd)
  966. {
  967. if (*pstrSource != chRemove)
  968. {
  969. *pstrDest = *pstrSource;
  970. pstrDest = wcsinc(pstrDest);
  971. }
  972. pstrSource = wcsinc(pstrSource);
  973. }
  974. *pstrDest = '\0';
  975. int nCount = (int)(pstrSource - pstrDest);
  976. GetData()->nDataLength -= nCount;
  977. return nCount;
  978. }
  979. //////////////////////////////////////////////////////////////////////////////
  980. // Very simple sub-string extraction
  981. CString CString::Mid(int nFirst) const
  982. {
  983. return Mid(nFirst, GetData()->nDataLength - nFirst);
  984. }
  985. CString CString::Mid(int nFirst, int nCount) const
  986. {
  987. CString dest;
  988. Mid(nFirst, nCount, dest);
  989. return dest;
  990. }
  991. CString CString::Right(int nCount) const
  992. {
  993. CString dest;
  994. Right(nCount, dest);
  995. return dest;
  996. }
  997. CString CString::Left(int nCount) const
  998. {
  999. CString dest;
  1000. Left(nCount, dest);
  1001. return dest;
  1002. }
  1003. // strspn equivalent
  1004. CString CString::SpanIncluding(LPCWSTR lpszCharSet) const
  1005. {
  1006. ASSERT(AfxIsValidString(lpszCharSet), "CString::SpanIncluding");
  1007. return Left(wcsspn(m_pchData, lpszCharSet));
  1008. }
  1009. // strcspn equivalent
  1010. CString CString::SpanExcluding(LPCWSTR lpszCharSet) const
  1011. {
  1012. ASSERT(AfxIsValidString(lpszCharSet), "CString::SpanIncluding");
  1013. return Left(wcscspn(m_pchData, lpszCharSet));
  1014. }
  1015. void CString::Mid(int nFirst, CString & csMid) const
  1016. {
  1017. Mid(nFirst, GetData()->nDataLength - nFirst, csMid);
  1018. }
  1019. void CString::Mid(int nFirst, int nCount, CString & csMid) const
  1020. {
  1021. // out-of-bounds requests return sensible things
  1022. if (nFirst < 0)
  1023. nFirst = 0;
  1024. if (nCount < 0)
  1025. nCount = 0;
  1026. if (nFirst + nCount > GetData()->nDataLength)
  1027. nCount = GetData()->nDataLength - nFirst;
  1028. if (nFirst > GetData()->nDataLength)
  1029. nCount = 0;
  1030. ASSERT(nFirst >= 0, "CString::Mid(int nFirst, int nCount)");
  1031. ASSERT(nFirst + nCount <= GetData()->nDataLength, "CString::Mid(int nFirst, int nCount)");
  1032. // optimize case of returning entire string
  1033. if (nFirst == 0 && nFirst + nCount == GetData()->nDataLength)
  1034. {
  1035. csMid = *this;
  1036. return;
  1037. }
  1038. AllocCopy(csMid, nCount, nFirst, 0);
  1039. }
  1040. void CString::Right(int nCount, CString & csRight) const
  1041. {
  1042. if (nCount < 0)
  1043. nCount = 0;
  1044. if (nCount >= GetData()->nDataLength)
  1045. return;
  1046. AllocCopy(csRight, nCount, GetData()->nDataLength-nCount, 0);
  1047. }
  1048. void CString::Left(int nCount, CString & csLeft) const
  1049. {
  1050. if (nCount < 0)
  1051. nCount = 0;
  1052. if (nCount >= GetData()->nDataLength)
  1053. return;
  1054. AllocCopy(csLeft, nCount, 0, 0);
  1055. }
  1056. void CString::SpanIncluding(const WCHAR * lpszCharSet, CString & csSpanInc) const
  1057. {
  1058. ASSERT(AfxIsValidString(lpszCharSet), "CString::SpanIncluding");
  1059. return Left(wcsspn(m_pchData, lpszCharSet), csSpanInc);
  1060. }
  1061. void CString::SpanExcluding(const WCHAR * lpszCharSet, CString & csSpanExc) const
  1062. {
  1063. ASSERT(AfxIsValidString(lpszCharSet), "CString::SpanIncluding");
  1064. return Left(wcscspn(m_pchData, lpszCharSet), csSpanExc);
  1065. }
  1066. //////////////////////////////////////////////////////////////////////////////
  1067. // Finding
  1068. int CString::ReverseFind(WCHAR ch) const
  1069. {
  1070. // find last single character
  1071. LPCWSTR lpsz = wcsrchr(m_pchData, (_TUCHAR) ch);
  1072. // return -1 if not found, distance from beginning otherwise
  1073. return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
  1074. }
  1075. // find a sub-string (like strstr)
  1076. int CString::Find(LPCWSTR lpszSub) const
  1077. {
  1078. return Find(lpszSub, 0);
  1079. }
  1080. int CString::Find(LPCWSTR lpszSub, int nStart) const
  1081. {
  1082. ASSERT(AfxIsValidString(lpszSub), "CString::Find");
  1083. int nLength = GetData()->nDataLength;
  1084. if (nStart > nLength)
  1085. return -1;
  1086. // find first matching substring
  1087. LPWSTR lpsz = wcsstr(m_pchData + nStart, lpszSub);
  1088. // return -1 for not found, distance from beginning otherwise
  1089. return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
  1090. }
  1091. /////////////////////////////////////////////////////////////////////////////
  1092. // CString formatting
  1093. #define TCHAR_ARG WCHAR
  1094. #define WCHAR_ARG WCHAR
  1095. #define CHAR_ARG WCHAR
  1096. #ifdef _X86_
  1097. #define DOUBLE_ARG _AFX_DOUBLE
  1098. #else
  1099. #define DOUBLE_ARG double
  1100. #endif
  1101. #define FORCE_ANSI 0x10000
  1102. #define FORCE_UNICODE 0x20000
  1103. #define FORCE_INT64 0x40000
  1104. void CString::FormatV(const WCHAR * lpszFormat, va_list argList)
  1105. {
  1106. ASSERT(AfxIsValidString(lpszFormat), "CString::FormatV");
  1107. // Determine how many characters are necessary to contain the entire formatted string.
  1108. int nMaxLen = _vscwprintf(lpszFormat, argList);
  1109. nMaxLen += 1; // One extra for EOS
  1110. GetBuffer(nMaxLen);
  1111. // Pass the actual number of chars allocated to the format routine (typically is larger)
  1112. nMaxLen = GetAllocLength();
  1113. StringCchVPrintfW(m_pchData, nMaxLen, lpszFormat, argList);
  1114. ReleaseBuffer();
  1115. }
  1116. // formatting (using wsprintf style formatting)
  1117. void AFX_CDECL CString::Format(const WCHAR * lpszFormat, ...)
  1118. {
  1119. ASSERT(AfxIsValidString(lpszFormat), "CString::Format");
  1120. va_list argList;
  1121. va_start(argList, lpszFormat);
  1122. FormatV(lpszFormat, argList);
  1123. va_end(argList);
  1124. }
  1125. void CString::FormatV(const char * lpszFormat, va_list argList)
  1126. {
  1127. // Determine how many characters are necessary to contain the entire formatted string.
  1128. int nMaxLen = _vscprintf(lpszFormat, argList);
  1129. nMaxLen += 1; // One extra for EOS
  1130. char * buffer = (char *)malloc(nMaxLen);
  1131. if (buffer == NULL)
  1132. {
  1133. CSTRING_THROW_EXCEPTION
  1134. }
  1135. StringCchVPrintfA(buffer, nMaxLen, lpszFormat, argList);
  1136. *this = buffer;
  1137. free(buffer);
  1138. }
  1139. // formatting (using wsprintf style formatting)
  1140. void AFX_CDECL CString::Format(const char * lpszFormat, ...)
  1141. {
  1142. va_list argList;
  1143. va_start(argList, lpszFormat);
  1144. FormatV(lpszFormat, argList);
  1145. va_end(argList);
  1146. }
  1147. // formatting (using FormatMessage style formatting)
  1148. void AFX_CDECL CString::FormatMessage(LPCWSTR lpszFormat, ...)
  1149. {
  1150. // format message into temporary buffer lpszTemp
  1151. va_list argList;
  1152. va_start(argList, lpszFormat);
  1153. LPWSTR lpszTemp;
  1154. if (::FormatMessageW(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER,
  1155. lpszFormat, 0, 0, (LPWSTR)&lpszTemp, 0, &argList) == 0 ||
  1156. lpszTemp == NULL)
  1157. {
  1158. CSTRING_THROW_EXCEPTION
  1159. }
  1160. else
  1161. {
  1162. // assign lpszTemp into the resulting string and free the temporary
  1163. *this = lpszTemp;
  1164. LocalFree(lpszTemp);
  1165. va_end(argList);
  1166. }
  1167. }
  1168. void CString::TrimRight(LPCWSTR lpszTargetList)
  1169. {
  1170. // find beginning of trailing matches
  1171. // by starting at beginning (DBCS aware)
  1172. CopyBeforeWrite();
  1173. LPWSTR lpsz = m_pchData;
  1174. LPWSTR lpszLast = NULL;
  1175. while (*lpsz != '\0')
  1176. {
  1177. if (wcschr(lpszTargetList, *lpsz) != NULL)
  1178. {
  1179. if (lpszLast == NULL)
  1180. lpszLast = lpsz;
  1181. }
  1182. else
  1183. lpszLast = NULL;
  1184. lpsz = wcsinc(lpsz);
  1185. }
  1186. if (lpszLast != NULL)
  1187. {
  1188. // truncate at left-most matching character
  1189. *lpszLast = '\0';
  1190. GetData()->nDataLength = (int)(lpszLast - m_pchData);
  1191. }
  1192. }
  1193. void CString::TrimRight(WCHAR chTarget)
  1194. {
  1195. // find beginning of trailing matches
  1196. // by starting at beginning (DBCS aware)
  1197. CopyBeforeWrite();
  1198. LPWSTR lpsz = m_pchData;
  1199. LPWSTR lpszLast = NULL;
  1200. while (*lpsz != '\0')
  1201. {
  1202. if (*lpsz == chTarget)
  1203. {
  1204. if (lpszLast == NULL)
  1205. lpszLast = lpsz;
  1206. }
  1207. else
  1208. lpszLast = NULL;
  1209. lpsz = wcsinc(lpsz);
  1210. }
  1211. if (lpszLast != NULL)
  1212. {
  1213. // truncate at left-most matching character
  1214. *lpszLast = '\0';
  1215. GetData()->nDataLength = (int)(lpszLast - m_pchData);
  1216. }
  1217. }
  1218. void CString::TrimRight()
  1219. {
  1220. // find beginning of trailing spaces by starting at beginning (DBCS aware)
  1221. CopyBeforeWrite();
  1222. LPWSTR lpsz = m_pchData;
  1223. LPWSTR lpszLast = NULL;
  1224. while (*lpsz != '\0')
  1225. {
  1226. if (iswspace(*lpsz))
  1227. {
  1228. if (lpszLast == NULL)
  1229. lpszLast = lpsz;
  1230. }
  1231. else
  1232. lpszLast = NULL;
  1233. lpsz = wcsinc(lpsz);
  1234. }
  1235. if (lpszLast != NULL)
  1236. {
  1237. // truncate at trailing space start
  1238. *lpszLast = '\0';
  1239. GetData()->nDataLength = (int)(lpszLast - m_pchData);
  1240. }
  1241. }
  1242. void CString::TrimLeft(LPCWSTR lpszTargets)
  1243. {
  1244. // if we're not trimming anything, we're not doing any work
  1245. if (SafeStrlen(lpszTargets) == 0)
  1246. return;
  1247. CopyBeforeWrite();
  1248. LPCWSTR lpsz = m_pchData;
  1249. while (*lpsz != '\0')
  1250. {
  1251. if (wcschr(lpszTargets, *lpsz) == NULL)
  1252. break;
  1253. lpsz = wcsinc(lpsz);
  1254. }
  1255. if (lpsz != m_pchData)
  1256. {
  1257. // fix up data and length
  1258. int nDataLength = GetData()->nDataLength - (int)(lpsz - m_pchData);
  1259. memmove(m_pchData, lpsz, (nDataLength+1)*sizeof(WCHAR));
  1260. GetData()->nDataLength = nDataLength;
  1261. }
  1262. }
  1263. void CString::TrimLeft(WCHAR chTarget)
  1264. {
  1265. // find first non-matching character
  1266. CopyBeforeWrite();
  1267. LPCWSTR lpsz = m_pchData;
  1268. while (chTarget == *lpsz)
  1269. lpsz = wcsinc(lpsz);
  1270. if (lpsz != m_pchData)
  1271. {
  1272. // fix up data and length
  1273. int nDataLength = GetData()->nDataLength - (int)(lpsz - m_pchData);
  1274. memmove(m_pchData, lpsz, (nDataLength+1)*sizeof(WCHAR));
  1275. GetData()->nDataLength = nDataLength;
  1276. }
  1277. }
  1278. void CString::TrimLeft()
  1279. {
  1280. // find first non-space character
  1281. CopyBeforeWrite();
  1282. LPCWSTR lpsz = m_pchData;
  1283. while (iswspace(*lpsz))
  1284. lpsz = wcsinc(lpsz);
  1285. if (lpsz != m_pchData)
  1286. {
  1287. // fix up data and length
  1288. int nDataLength = GetData()->nDataLength - (int)(lpsz - m_pchData);
  1289. memmove(m_pchData, lpsz, (nDataLength+1)*sizeof(WCHAR));
  1290. GetData()->nDataLength = nDataLength;
  1291. }
  1292. }
  1293. void CString::SplitPath(
  1294. CString * csDrive,
  1295. CString * csDir,
  1296. CString * csName,
  1297. CString * csExt) const
  1298. {
  1299. WCHAR * drive = NULL;
  1300. WCHAR * dir = NULL;
  1301. WCHAR * name = NULL;
  1302. WCHAR * ext = NULL;
  1303. if (csDrive)
  1304. {
  1305. drive = csDrive->GetBuffer(_MAX_DRIVE);
  1306. }
  1307. if (csDir)
  1308. {
  1309. dir = csDir->GetBuffer(_MAX_DIR);
  1310. }
  1311. if (csName)
  1312. {
  1313. name = csName->GetBuffer(_MAX_FNAME);
  1314. }
  1315. if (csExt)
  1316. {
  1317. ext = csExt->GetBuffer(_MAX_EXT);
  1318. }
  1319. _wsplitpath(Get(), drive, dir, name, ext);
  1320. if (csDrive)
  1321. {
  1322. csDrive->ReleaseBuffer(-1);
  1323. }
  1324. if (csDir)
  1325. {
  1326. csDir->ReleaseBuffer(-1);
  1327. }
  1328. if (csName)
  1329. {
  1330. csName->ReleaseBuffer(-1);
  1331. }
  1332. if (csExt)
  1333. {
  1334. csExt->ReleaseBuffer(-1);
  1335. }
  1336. }
  1337. void CString::MakePath(
  1338. const CString * csDrive,
  1339. const CString * csDir,
  1340. const CString * csName,
  1341. const CString * csExt)
  1342. {
  1343. Truncate(0);
  1344. if (csDrive && !csDrive->IsEmpty())
  1345. {
  1346. ConcatInPlace(SafeStrlen(csDrive->Get()), csDrive->Get());
  1347. }
  1348. if (csDir && !csDir->IsEmpty())
  1349. {
  1350. ConcatInPlace(SafeStrlen(csDir->Get()), csDir->Get());
  1351. }
  1352. if (csName && !csName->IsEmpty())
  1353. {
  1354. // Make sure there is a \ between the two
  1355. if (!IsEmpty() && !IsPathSep(GetLength()) && !csName->IsPathSep(0) )
  1356. {
  1357. ConcatInPlace(1, L"\\");
  1358. }
  1359. ConcatInPlace(SafeStrlen(csName->Get()), csName->Get());
  1360. }
  1361. if (csExt && !csExt->IsEmpty())
  1362. {
  1363. // Make sure the extension has a dot
  1364. if (csExt->GetAt(0) != L'.')
  1365. {
  1366. ConcatInPlace(1, L".");
  1367. }
  1368. ConcatInPlace(SafeStrlen(csExt->Get()), csExt->Get());
  1369. }
  1370. }
  1371. void CString::AppendPath(const WCHAR * lpszPath)
  1372. {
  1373. int nLen = GetLength();
  1374. BOOL bThisHasSep = (nLen > 0) ? IsPathSep(nLen - 1) : FALSE;
  1375. BOOL bThatHasSep = ShimLib::IsPathSep(*lpszPath);
  1376. if (lpszPath == NULL || *lpszPath == 0)
  1377. {
  1378. return;
  1379. }
  1380. else if (nLen == 0)
  1381. {
  1382. // No path seperator is necessary
  1383. }
  1384. else if ((nLen == 2) && (GetAt(1) == L':') && !bThatHasSep )
  1385. {
  1386. // We must place a path seperator between the two
  1387. ConcatInPlace(1, L"\\");
  1388. }
  1389. else if (!bThisHasSep && !bThatHasSep )
  1390. {
  1391. // We must place a path seperator between the two
  1392. ConcatInPlace(1, L"\\");
  1393. }
  1394. else if (bThisHasSep && bThatHasSep )
  1395. {
  1396. // Both have seperators, remove one
  1397. do
  1398. {
  1399. lpszPath += 1;
  1400. }
  1401. while (ShimLib::IsPathSep(*lpszPath));
  1402. }
  1403. ConcatInPlace(SafeStrlen(lpszPath), lpszPath);
  1404. }
  1405. // Find the trailing path component
  1406. // Return index of the last path seperator or -1 if none found
  1407. int CString::FindLastPathComponent() const
  1408. {
  1409. for (int nLen = GetLength() - 1; nLen >= 0; --nLen)
  1410. {
  1411. if (IsPathSep(nLen))
  1412. {
  1413. return nLen;
  1414. }
  1415. }
  1416. return -1;
  1417. }
  1418. // Remove the trailing path component from the string
  1419. void CString::StripPath()
  1420. {
  1421. int nLastPathComponent = FindLastPathComponent();
  1422. if (nLastPathComponent != -1)
  1423. {
  1424. Truncate(nLastPathComponent);
  1425. }
  1426. else
  1427. {
  1428. Truncate(0);
  1429. }
  1430. }
  1431. char * CString::GetAnsi() const
  1432. {
  1433. // Since we don't know if the original (WCHAR) data has changed
  1434. // we need to update the ansi string each time.
  1435. if (m_pchDataAnsi)
  1436. {
  1437. free(m_pchDataAnsi);
  1438. m_pchDataAnsi = NULL;
  1439. }
  1440. // Get the number of bytes necessary for the WCHAR string
  1441. int nBytes = WideCharToMultiByte(CP_ACP, 0, m_pchData, -1, NULL, 0, NULL, NULL);
  1442. m_pchDataAnsi = (char *) malloc(nBytes);
  1443. if (m_pchDataAnsi)
  1444. {
  1445. WideCharToMultiByte(CP_ACP, 0, m_pchData, -1, m_pchDataAnsi, nBytes, NULL, NULL);
  1446. }
  1447. else
  1448. {
  1449. CSTRING_THROW_EXCEPTION
  1450. }
  1451. return m_pchDataAnsi;
  1452. }
  1453. void CString::GetLastPathComponent(CString & pathComponent) const
  1454. {
  1455. int nPath = FindLastPathComponent();
  1456. if (nPath < 0)
  1457. {
  1458. pathComponent = *this;
  1459. }
  1460. else
  1461. {
  1462. Mid(nPath+1, pathComponent);
  1463. }
  1464. }
  1465. // Get what's not the "file" portion of this path
  1466. void CString::GetNotLastPathComponent(CString & pathComponent) const
  1467. {
  1468. int nPath = FindLastPathComponent();
  1469. if (nPath < 1)
  1470. {
  1471. pathComponent.Truncate(0);
  1472. }
  1473. else
  1474. {
  1475. Left(nPath, pathComponent);
  1476. }
  1477. }
  1478. // Get the Drive portion of this path,
  1479. // Either C: or \\server\disk format.
  1480. void CString::GetDrivePortion(CString & csDrivePortion) const
  1481. {
  1482. const WCHAR * lpwszPath = Get();
  1483. const WCHAR * lpwszNonDrivePortion = ShimLib::GetDrivePortion(lpwszPath);
  1484. if (lpwszPath == lpwszNonDrivePortion)
  1485. {
  1486. csDrivePortion.Truncate(0);
  1487. }
  1488. else
  1489. {
  1490. Left((int)(lpwszNonDrivePortion - lpwszPath), csDrivePortion);
  1491. }
  1492. }
  1493. // Return number of chars in the string, 0 for error
  1494. DWORD CString::GetModuleFileNameW(
  1495. HMODULE hModule // handle to module
  1496. )
  1497. {
  1498. Truncate(0);
  1499. // There is no method of determining the necessary size of the buffer before calling
  1500. // GetModulefileName. So we'll keep calling it until the number of chars is smaller
  1501. // than our buffer size. There is a limit of ~32000 chars (\\?\ type paths)
  1502. for (DWORD cchNeeded = MAX_PATH; cchNeeded < 32000; cchNeeded *= 2)
  1503. {
  1504. WCHAR * lpsz = GetBuffer(cchNeeded);
  1505. // Return value is number of chars placed into the buffer
  1506. DWORD cchActual = ::GetModuleFileNameW(hModule, lpsz, cchNeeded);
  1507. ReleaseBuffer(cchActual);
  1508. // If GetModuleFileNameW returns fewer characters than our buffer, then we have the entire string.
  1509. if (cchActual < cchNeeded)
  1510. {
  1511. break;
  1512. }
  1513. // Try again with a larger buffer...
  1514. }
  1515. return GetLength();
  1516. }
  1517. DWORD CString::GetSystemDirectoryW(void)
  1518. {
  1519. Truncate(0);
  1520. UINT cchNeeded = ::GetSystemDirectoryW(NULL, 0);
  1521. if (cchNeeded)
  1522. {
  1523. cchNeeded += 1; // One for the NULL
  1524. // Get a pointer to the actual lpsz data
  1525. WCHAR * lpszPath = GetBuffer(cchNeeded);
  1526. DWORD cchActual = ::GetSystemDirectoryW(lpszPath, cchNeeded);
  1527. if (cchActual < cchNeeded)
  1528. {
  1529. ReleaseBuffer(cchActual);
  1530. }
  1531. else
  1532. {
  1533. // error
  1534. ReleaseBuffer(0);
  1535. }
  1536. }
  1537. return GetLength();
  1538. }
  1539. DWORD CString::GetSystemWindowsDirectoryW(void)
  1540. {
  1541. Truncate(0);
  1542. UINT cchNeeded = ::GetSystemWindowsDirectoryW(NULL, 0);
  1543. if (cchNeeded)
  1544. {
  1545. cchNeeded += 1; // One for the NULL
  1546. // Get a pointer to the actual lpsz data
  1547. WCHAR * lpszPath = GetBuffer(cchNeeded);
  1548. DWORD cchActual = ::GetSystemWindowsDirectoryW(lpszPath, cchNeeded);
  1549. if (cchActual < cchNeeded)
  1550. {
  1551. ReleaseBuffer(cchActual);
  1552. }
  1553. else
  1554. {
  1555. // error
  1556. ReleaseBuffer(0);
  1557. }
  1558. }
  1559. return GetLength();
  1560. }
  1561. DWORD CString::GetWindowsDirectoryW(void)
  1562. {
  1563. Truncate(0);
  1564. UINT cchNeeded = ::GetWindowsDirectoryW(NULL, 0);
  1565. if (cchNeeded)
  1566. {
  1567. cchNeeded += 1; // One for the NULL
  1568. // Get a pointer to the actual lpsz data
  1569. WCHAR * lpszPath = GetBuffer(cchNeeded);
  1570. DWORD cchActual = ::GetWindowsDirectoryW(lpszPath, cchNeeded);
  1571. if (cchActual < cchNeeded)
  1572. {
  1573. ReleaseBuffer(cchActual);
  1574. }
  1575. else
  1576. {
  1577. // error
  1578. ReleaseBuffer(0);
  1579. }
  1580. }
  1581. return GetLength();
  1582. }
  1583. DWORD CString::GetShortPathNameW(void)
  1584. {
  1585. DWORD cchNeeded = ::GetShortPathNameW(Get(), NULL, 0);
  1586. if (cchNeeded)
  1587. {
  1588. CString csCopy;
  1589. cchNeeded += 1; // One for the NULL
  1590. // Get a pointer to the actual lpsz data
  1591. WCHAR * lpszPath = csCopy.GetBuffer(cchNeeded);
  1592. DWORD cchActual = ::GetShortPathNameW(Get(), lpszPath, cchNeeded);
  1593. if (cchActual > 0 && cchActual < cchNeeded)
  1594. {
  1595. csCopy.ReleaseBuffer(cchActual);
  1596. *this = csCopy;
  1597. return GetLength();
  1598. }
  1599. else
  1600. {
  1601. // error
  1602. csCopy.ReleaseBuffer(0);
  1603. }
  1604. }
  1605. return 0;
  1606. }
  1607. DWORD CString::GetLongPathNameW(void)
  1608. {
  1609. DWORD cchNeeded = ::GetLongPathNameW(Get(), NULL, 0);
  1610. if (cchNeeded)
  1611. {
  1612. CString csCopy;
  1613. cchNeeded += 1; // One for the NULL
  1614. // Get a pointer to the actual lpsz data
  1615. WCHAR * lpszPath = csCopy.GetBuffer(cchNeeded);
  1616. DWORD cchActual = ::GetLongPathNameW(Get(), lpszPath, cchNeeded);
  1617. if (cchActual > 0 && cchActual < cchNeeded)
  1618. {
  1619. csCopy.ReleaseBuffer(cchActual);
  1620. *this = csCopy;
  1621. return GetLength();
  1622. }
  1623. else
  1624. {
  1625. // error
  1626. csCopy.ReleaseBuffer(0);
  1627. }
  1628. }
  1629. return 0;
  1630. }
  1631. DWORD CString::GetFullPathNameW(void)
  1632. {
  1633. DWORD cchNeeded = ::GetFullPathNameW(Get(), 0, NULL, NULL);
  1634. if (cchNeeded)
  1635. {
  1636. CString csCopy;
  1637. cchNeeded += 1; // One for the NULL
  1638. // Get a pointer to the actual lpsz data
  1639. WCHAR * lpszPath = csCopy.GetBuffer(cchNeeded);
  1640. DWORD cchActual = ::GetFullPathNameW(Get(), cchNeeded, lpszPath, NULL);
  1641. if (cchActual > 0 && cchActual < cchNeeded)
  1642. {
  1643. csCopy.ReleaseBuffer(cchActual);
  1644. *this = csCopy;
  1645. return GetLength();
  1646. }
  1647. else
  1648. {
  1649. // error
  1650. csCopy.ReleaseBuffer(0);
  1651. }
  1652. }
  1653. return 0;
  1654. }
  1655. DWORD CString::GetTempPathW(void)
  1656. {
  1657. Truncate(0);
  1658. DWORD cchNeeded = ::GetTempPathW(0, NULL);
  1659. if (cchNeeded)
  1660. {
  1661. cchNeeded += 1; // One for the NULL
  1662. // Get a pointer to the actual lpsz data
  1663. WCHAR * lpszPath = GetBuffer(cchNeeded);
  1664. DWORD cchActual = ::GetTempPathW(cchNeeded, lpszPath);
  1665. if (cchActual < cchNeeded)
  1666. {
  1667. ReleaseBuffer(cchActual);
  1668. }
  1669. else
  1670. {
  1671. // error
  1672. ReleaseBuffer(0);
  1673. }
  1674. }
  1675. return GetLength();
  1676. }
  1677. DWORD CString::GetTempFileNameW(
  1678. LPCWSTR lpPathName, // directory name
  1679. LPCWSTR lpPrefixString, // file name prefix
  1680. UINT uUnique // integer
  1681. )
  1682. {
  1683. // There is no method of determining the necessary size of the buffer before calling GetTempFileNameW
  1684. // All you can do is to make sure you buffer has enough space for lpPathName plus an 8.3 filename.
  1685. DWORD cchNeeded = SafeStrlen(lpPathName);
  1686. // extra for \ 8 . 3 null
  1687. cchNeeded += 1 + 8 + 1 + 3 + 1;
  1688. WCHAR * lpsz = GetBuffer(cchNeeded);
  1689. (void) ::GetTempFileNameW(lpPathName, lpPrefixString, uUnique, lpsz);
  1690. ReleaseBuffer(-1);
  1691. return GetLength();
  1692. }
  1693. DWORD CString::GetCurrentDirectoryW(void)
  1694. {
  1695. Truncate(0);
  1696. DWORD cchNeeded = ::GetCurrentDirectoryW(0, NULL);
  1697. if (cchNeeded)
  1698. {
  1699. cchNeeded += 1; // One for the NULL
  1700. // Get a pointer to the actual lpsz data
  1701. WCHAR * lpszPath = GetBuffer(cchNeeded);
  1702. DWORD cchActual = ::GetCurrentDirectoryW(cchNeeded, lpszPath);
  1703. if (cchActual < cchNeeded)
  1704. {
  1705. ReleaseBuffer(cchActual);
  1706. }
  1707. else
  1708. {
  1709. // error
  1710. ReleaseBuffer(0);
  1711. }
  1712. }
  1713. return GetLength();
  1714. }
  1715. DWORD CString::GetLocaleInfoW(LCID Locale, LCTYPE LCType)
  1716. {
  1717. Truncate(0);
  1718. DWORD cchNeeded = ::GetLocaleInfoW(Locale, LCType, NULL, 0);
  1719. if (cchNeeded)
  1720. {
  1721. cchNeeded += 1; // One for the NULL
  1722. // Get a pointer to the actual lpsz data
  1723. WCHAR * lpszPath = GetBuffer(cchNeeded);
  1724. DWORD cchActual = ::GetLocaleInfoW(Locale, LCType, lpszPath, cchNeeded);
  1725. if (cchActual < cchNeeded)
  1726. {
  1727. ReleaseBuffer(cchActual);
  1728. }
  1729. else
  1730. {
  1731. // error
  1732. ReleaseBuffer(0);
  1733. }
  1734. }
  1735. return GetLength();
  1736. }
  1737. DWORD CString::ExpandEnvironmentStringsW( )
  1738. {
  1739. // ExpandEnvironmentStrings returns a count that includes the null char.
  1740. DWORD cchNeeded = ::ExpandEnvironmentStringsW(Get(), NULL, 0);
  1741. if (cchNeeded)
  1742. {
  1743. CString csCopy;
  1744. // Get a pointer to the actual lpsz data
  1745. WCHAR * lpszPath = csCopy.GetBuffer(cchNeeded);
  1746. DWORD cchActual = ::ExpandEnvironmentStringsW(Get(), lpszPath, cchNeeded);
  1747. if (cchActual > 0 && cchActual <= cchNeeded)
  1748. {
  1749. csCopy.ReleaseBuffer(cchActual-1);
  1750. *this = csCopy;
  1751. return GetLength();
  1752. }
  1753. else
  1754. {
  1755. // error
  1756. csCopy.ReleaseBuffer(0);
  1757. }
  1758. }
  1759. return 0;
  1760. }
  1761. // delete all characters to the right of nIndex
  1762. void CString::Truncate(int nIndex)
  1763. {
  1764. ASSERT(nIndex >= 0, "CString::Truncate");
  1765. CopyBeforeWrite();
  1766. if (nIndex < GetLength())
  1767. {
  1768. SetAt(nIndex, L'\0');
  1769. GetData()->nDataLength = nIndex;
  1770. }
  1771. }
  1772. BOOL CString::PatternMatch(const WCHAR * lpszPattern) const
  1773. {
  1774. return PatternMatchW(lpszPattern, Get());
  1775. }
  1776. /*++
  1777. Read a *string* registry value.
  1778. If the type is not REG_SZ or REG_EXPAND_SZ then this routine returns STATUS_INVALID_PARAMETER.
  1779. REG_EXPAND_SZ type is automatically expanded.
  1780. This API does not use ADVAPI, so it is safe to use in DllMain.
  1781. --*/
  1782. DWORD CString::NtReqQueryValueExW(
  1783. const WCHAR * lpszKey,
  1784. const WCHAR * lpszValue)
  1785. {
  1786. HANDLE KeyHandle;
  1787. // Convert the key name into a UNICODE_STRING
  1788. UNICODE_STRING strKeyName = {0};
  1789. RtlInitUnicodeString(&strKeyName, lpszKey);
  1790. OBJECT_ATTRIBUTES ObjectAttributes;
  1791. InitializeObjectAttributes(&ObjectAttributes,
  1792. &strKeyName,
  1793. OBJ_CASE_INSENSITIVE,
  1794. NULL,
  1795. NULL);
  1796. NTSTATUS status = NtOpenKey(&KeyHandle,
  1797. KEY_QUERY_VALUE,
  1798. &ObjectAttributes);
  1799. if (status == STATUS_SUCCESS)
  1800. {
  1801. // Make a UNICODE_STRING of the key value
  1802. UNICODE_STRING strValueName = {0};
  1803. RtlInitUnicodeString(&strValueName, lpszValue ? lpszValue : L"");
  1804. // Determine the size of the key data
  1805. DWORD dwValueLength;
  1806. status = NtQueryValueKey(KeyHandle,
  1807. &strValueName,
  1808. KeyValueFullInformation,
  1809. NULL,
  1810. 0,
  1811. &dwValueLength);
  1812. if (status == STATUS_BUFFER_TOO_SMALL)
  1813. {
  1814. PKEY_VALUE_FULL_INFORMATION pKeyValueInfo =
  1815. (PKEY_VALUE_FULL_INFORMATION) RtlAllocateHeap(RtlProcessHeap(),
  1816. HEAP_ZERO_MEMORY,
  1817. dwValueLength);
  1818. if (pKeyValueInfo)
  1819. {
  1820. status = NtQueryValueKey(KeyHandle,
  1821. &strValueName,
  1822. KeyValueFullInformation,
  1823. pKeyValueInfo,
  1824. dwValueLength,
  1825. &dwValueLength);
  1826. if (status == STATUS_SUCCESS)
  1827. {
  1828. // Save the registry type
  1829. if (pKeyValueInfo->Type == REG_EXPAND_SZ ||
  1830. pKeyValueInfo->Type == REG_SZ)
  1831. {
  1832. CSTRING_TRY
  1833. {
  1834. DWORD cchValueSize = pKeyValueInfo->DataLength / sizeof(WCHAR);
  1835. // Grab an extra character in case the reg value doesn't have EOS
  1836. // We are being extra cautious
  1837. WCHAR * lpszBuffer = GetBuffer(cchValueSize + 1);
  1838. RtlMoveMemory(lpszBuffer, ((PBYTE) pKeyValueInfo) + pKeyValueInfo->DataOffset, pKeyValueInfo->DataLength);
  1839. // cchValueSize might count the EOS character,
  1840. // (ReleaseBuffer expects the string length)
  1841. if (cchValueSize > 0 && lpszBuffer[cchValueSize-1] == 0)
  1842. {
  1843. cchValueSize -= 1;
  1844. // cchValueSize now contains the string length.
  1845. }
  1846. ReleaseBuffer(cchValueSize);
  1847. // Check to see if we need to convert REG_EXPAND_SZ to REG_SZ
  1848. if (pKeyValueInfo->Type == REG_EXPAND_SZ)
  1849. {
  1850. ExpandEnvironmentStringsW();
  1851. }
  1852. }
  1853. CSTRING_CATCH
  1854. {
  1855. // We catch these CString exceptions so we can free memory and close the handles.
  1856. status = STATUS_NO_MEMORY;
  1857. }
  1858. }
  1859. else
  1860. {
  1861. // Registry entry is not a string type
  1862. status = STATUS_INVALID_PARAMETER;
  1863. }
  1864. }
  1865. RtlFreeHeap(RtlProcessHeap(), 0, pKeyValueInfo);
  1866. }
  1867. }
  1868. NtClose(KeyHandle);
  1869. }
  1870. return status;
  1871. }
  1872. }; // end of namespace ShimLib