Source code of Windows XP (NT5)
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.

596 lines
17 KiB

  1. // AttrStrA.cpp : Implementation of CMLStrAttrAStr
  2. #include "private.h"
  3. #ifdef NEWMLSTR
  4. #include "attrstra.h"
  5. #include "mlswalk.h"
  6. #include "mlsbwalk.h"
  7. /////////////////////////////////////////////////////////////////////////////
  8. // CMLStrAttrAStr
  9. CMLStrAttrAStr::CMLStrAttrAStr(void) :
  10. m_pMLCPs(NULL),
  11. m_pMLStr(NULL)
  12. {
  13. }
  14. CMLStrAttrAStr::~CMLStrAttrAStr(void)
  15. {
  16. VERIFY(SetClient(NULL)); // Clean m_pMLStr
  17. if (m_pMLCPs)
  18. m_pMLCPs->Release();
  19. }
  20. STDMETHODIMP CMLStrAttrAStr::SetClient(IUnknown* pUnk)
  21. {
  22. ASSERT_THIS;
  23. ASSERT_READ_PTR_OR_NULL(pUnk);
  24. HRESULT hr = S_OK;
  25. // Release old client
  26. IMLangString* const pMLStr = m_pMLStr;
  27. if (pMLStr && SUCCEEDED(hr = StartEndConnectionMLStr(pMLStr, FALSE))) // End connection to MLStr
  28. {
  29. pMLStr->Release();
  30. m_pMLStr = NULL;
  31. }
  32. // Set new client
  33. if (SUCCEEDED(hr) && pUnk) // pUnk is given
  34. {
  35. ASSERT(!m_pMLStr);
  36. if (SUCCEEDED(hr = pUnk->QueryInterface(IID_IMLangString, (void**)&m_pMLStr)))
  37. {
  38. ASSERT_READ_PTR(m_pMLStr);
  39. if (FAILED(hr = StartEndConnectionMLStr(pUnk, TRUE))) // Start connection to MLStr
  40. {
  41. m_pMLStr->Release();
  42. m_pMLStr = NULL;
  43. }
  44. }
  45. }
  46. return hr;
  47. }
  48. HRESULT CMLStrAttrAStr::StartEndConnectionMLStr(IUnknown* const pUnk, BOOL fStart)
  49. {
  50. ASSERT_THIS;
  51. ASSERT_READ_PTR(pUnk);
  52. HRESULT hr;
  53. IConnectionPointContainer* pCPC;
  54. if (SUCCEEDED(hr = pUnk->QueryInterface(IID_IConnectionPointContainer, (void**)&pCPC)))
  55. {
  56. ASSERT_READ_PTR(pCPC);
  57. IConnectionPoint* pCP;
  58. if (SUCCEEDED(hr = pCPC->FindConnectionPoint(IID_IMLangStringNotifySink, &pCP)))
  59. {
  60. ASSERT_READ_PTR(pCP);
  61. if (fStart)
  62. hr = pCP->Advise((IMLStrAttr*)this, &m_dwMLStrCookie);
  63. else
  64. hr = pCP->Unadvise(m_dwMLStrCookie);
  65. pCP->Release();
  66. }
  67. pCPC->Release();
  68. }
  69. return hr;
  70. }
  71. STDMETHODIMP CMLStrAttrAStr::GetClient(IUnknown** ppUnk)
  72. {
  73. ASSERT_THIS;
  74. ASSERT_WRITE_PTR_OR_NULL(ppUnk);
  75. if (ppUnk)
  76. {
  77. IUnknown* const pUnk = m_pMLStr;
  78. *ppUnk = pUnk;
  79. if (pUnk)
  80. pUnk->AddRef();
  81. }
  82. return S_OK;
  83. }
  84. STDMETHODIMP CMLStrAttrAStr::QueryAttr(REFIID riid, LPARAM lParam, IUnknown** ppUnk, long* lConf)
  85. {
  86. return E_NOTIMPL; // CMLStrAttrAStr::QueryAttr()
  87. }
  88. STDMETHODIMP CMLStrAttrAStr::GetAttrInterface(IID* pIID, LPARAM* plParam)
  89. {
  90. return E_NOTIMPL; // CMLStrAttrAStr::GetAttrInterface()
  91. }
  92. STDMETHODIMP CMLStrAttrAStr::SetMLStr(long lDestPos, long lDestLen, IUnknown* pSrcMLStr, long lSrcPos, long lSrcLen)
  93. {
  94. return E_NOTIMPL; // CMLStrAttrAStr::SetMLStr()
  95. }
  96. STDMETHODIMP CMLStrAttrAStr::SetAStr(long lDestPos, long lDestLen, UINT uCodePage, const CHAR* pszSrc, long cchSrc, long* pcchActual, long* plActualLen)
  97. {
  98. ASSERT_THIS;
  99. ASSERT_READ_BLOCK(pszSrc, cchSrc);
  100. ASSERT_WRITE_PTR_OR_NULL(pcchActual);
  101. ASSERT_WRITE_PTR_OR_NULL(plActualLen);
  102. HRESULT hr;
  103. // Fire OnRequestEdit
  104. IEnumConnections* pEnumConn;
  105. if (SUCCEEDED(hr = EnumConnections(&pEnumConn)))
  106. {
  107. ASSERT_READ_PTR(pEnumConn);
  108. CONNECTDATA cd;
  109. while ((hr = pEnumConn->Next(1, &cd, NULL)) == S_OK)
  110. {
  111. IMLStrAttrNotifySink* pSink;
  112. if (SUCCEEDED(hr = cd.pUnk->QueryInterface(IID_IMLStrAttrNotifySink, (void**)&pSink)))
  113. {
  114. // TODO: Regularize before fire OnRequestEdit
  115. // TODO: And, calculate lNewLen,
  116. hr = pSink->OnRequestEdit(lDestPos, lDestLen, /*lNewLen*/0, IID_IMLStrAttrAStr, 0, (IMLStrAttr*)this);
  117. pSink->Release();
  118. }
  119. }
  120. pEnumConn->Release();
  121. }
  122. hr = CheckThread();
  123. CLock Lock(TRUE, this, hr);
  124. long cchDestPos;
  125. long cchDestLen;
  126. long cchActual;
  127. long lActualLen;
  128. if (SUCCEEDED(hr) && (GetBufFlags() & MLSTR_WRITE))
  129. hr = E_INVALIDARG; // Not writable StrBuf; TODO: Replace StrBuf in this case if allowed
  130. if (SUCCEEDED(hr) &&
  131. SUCCEEDED(hr = PrepareMLStrBuf()) &&
  132. SUCCEEDED(hr = RegularizePosLen(&lDestPos, &lDestLen)) &&
  133. SUCCEEDED(hr = GetCCh(0, lDestPos, &cchDestPos)) &&
  134. SUCCEEDED(hr = GetCCh(cchDestPos, lDestLen, &cchDestLen)))
  135. {
  136. IMLangStringBufA* const pMLStrBufA = GetMLStrBufA();
  137. if (uCodePage == CP_ACP)
  138. uCodePage = g_uACP;
  139. if (pMLStrBufA && uCodePage == GetCodePage())
  140. {
  141. if (cchSrc > cchDestLen)
  142. {
  143. hr = pMLStrBufA->Insert(cchDestPos, cchSrc - cchDestLen, (pcchActual || plActualLen) ? &cchSrc : NULL);
  144. cchSrc += cchDestLen;
  145. }
  146. else if (cchSrc < cchDestLen)
  147. {
  148. hr = pMLStrBufA->Delete(cchDestPos, cchDestLen - cchSrc);
  149. }
  150. CMLStrBufWalkA BufWalk(pMLStrBufA, cchDestPos, cchSrc, (pcchActual || plActualLen));
  151. lActualLen = 0;
  152. while (BufWalk.Lock(hr))
  153. {
  154. long lLen;
  155. if (plActualLen)
  156. hr = CalcLenA(uCodePage, pszSrc, BufWalk.GetCCh(), &lLen);
  157. if (SUCCEEDED(hr))
  158. {
  159. lActualLen += lLen;
  160. ::memcpy(BufWalk.GetStr(), pszSrc, sizeof(CHAR) * BufWalk.GetCCh());
  161. pszSrc += BufWalk.GetCCh();
  162. }
  163. BufWalk.Unlock(hr);
  164. }
  165. cchActual = BufWalk.GetDoneCCh();
  166. }
  167. else
  168. {
  169. IMLangStringWStr* pMLStrW;
  170. if (SUCCEEDED(hr = ((IMLStrAttr*)this)->QueryInterface(IID_IMLangStringWStr, (void**)&pMLStrW)))
  171. {
  172. CMLStrWalkW StrWalk(pMLStrW, lDestPos, lDestLen, MLSTR_WRITE, (pcchActual || plActualLen));
  173. cchActual = 0;
  174. lActualLen = 0;
  175. while (StrWalk.Lock(hr))
  176. {
  177. long cchWrittenA;
  178. long lWrittenLen;
  179. if (SUCCEEDED(hr = ConvAStrToWStr(uCodePage, pszSrc, cchSrc, StrWalk.GetStr(), StrWalk.GetCCh(), &cchWrittenA, NULL, &lWrittenLen)))
  180. {
  181. pszSrc += cchWrittenA;
  182. cchSrc -= cchWrittenA;
  183. cchActual += cchWrittenA;
  184. lActualLen += lWrittenLen;
  185. }
  186. StrWalk.Unlock(hr, lWrittenLen);
  187. }
  188. pMLStrW->Release();
  189. }
  190. }
  191. }
  192. if (SUCCEEDED(hr))
  193. {
  194. if (pcchActual)
  195. *pcchActual = cchActual;
  196. if (plActualLen)
  197. *plActualLen = lActualLen;
  198. }
  199. else
  200. {
  201. if (pcchActual)
  202. *pcchActual = 0;
  203. if (plActualLen)
  204. *plActualLen = 0;
  205. }
  206. return hr;
  207. }
  208. STDMETHODIMP CMLStrAttrAStr::SetStrBufA(long lDestPos, long lDestLen, UINT uCodePage, IMLangStringBufA* pSrcBuf, long* pcchActual, long* plActualLen)
  209. {
  210. ASSERT_THIS;
  211. return SetStrBufCommon(this, lDestPos, lDestLen, uCodePage, NULL, pSrcBuf, pcchActual, plActualLen);
  212. }
  213. STDMETHODIMP CMLStrAttrAStr::GetAStr(long lSrcPos, long lSrcLen, UINT uCodePageIn, UINT* puCodePageOut, CHAR* pszDest, long cchDest, long* pcchActual, long* plActualLen)
  214. {
  215. ASSERT_THIS;
  216. ASSERT_WRITE_PTR_OR_NULL(puCodePageOut);
  217. ASSERT_WRITE_BLOCK_OR_NULL(pszDest, cchDest);
  218. ASSERT_WRITE_PTR_OR_NULL(pcchActual);
  219. ASSERT_WRITE_PTR_OR_NULL(plActualLen);
  220. HRESULT hr = CheckThread();
  221. CLock Lock(FALSE, this, hr);
  222. long cchSrcPos;
  223. long cchSrcLen;
  224. long cchActual;
  225. long lActualLen;
  226. if (SUCCEEDED(hr) &&
  227. SUCCEEDED(hr = RegularizePosLen(&lSrcPos, &lSrcLen)) &&
  228. SUCCEEDED(hr = GetCCh(0, lSrcPos, &cchSrcPos)) &&
  229. SUCCEEDED(hr = GetCCh(cchSrcPos, lSrcLen, &cchSrcLen)))
  230. {
  231. IMLangStringBufA* const pMLStrBufA = GetMLStrBufA();
  232. if (pszDest)
  233. cchActual = min(cchSrcLen, cchDest);
  234. else
  235. cchActual = cchSrcLen;
  236. if (uCodePageIn == CP_ACP)
  237. uCodePageIn = g_uACP;
  238. if (pMLStrBufA && (puCodePageOut || uCodePageIn == GetCodePage()))
  239. {
  240. uCodePageIn = GetCodePage();
  241. CMLStrBufWalkA BufWalk(pMLStrBufA, cchSrcPos, cchActual, (pcchActual || plActualLen));
  242. lActualLen = 0;
  243. while (BufWalk.Lock(hr))
  244. {
  245. long lLen;
  246. if (plActualLen)
  247. hr = CalcLenA(uCodePageIn, BufWalk.GetStr(), BufWalk.GetCCh(), &lLen);
  248. if (SUCCEEDED(hr))
  249. {
  250. lActualLen += lLen;
  251. if (pszDest)
  252. {
  253. ::memcpy(pszDest, BufWalk.GetStr(), sizeof(CHAR) * BufWalk.GetCCh());
  254. pszDest += BufWalk.GetCCh();
  255. }
  256. }
  257. BufWalk.Unlock(hr);
  258. }
  259. cchActual = BufWalk.GetDoneCCh();
  260. }
  261. else
  262. {
  263. IMLangStringWStr* pMLStrW;
  264. if (SUCCEEDED(hr = m_pMLStr->QueryInterface(IID_IMLangStringWStr, (void**)&pMLStrW)))
  265. {
  266. BOOL fDontHaveCodePageIn = (puCodePageOut != 0);
  267. CMLStrWalkW StrWalk(pMLStrW, lSrcPos, lSrcLen, (pcchActual || plActualLen));
  268. cchActual = 0;
  269. while (StrWalk.Lock(hr))
  270. {
  271. LCID locale;
  272. UINT uLocaleCodePage;
  273. DWORD dwLocaleCodePages;
  274. DWORD dwStrCodePages;
  275. long cchWritten;
  276. long lWrittenLen;
  277. if (fDontHaveCodePageIn &&
  278. SUCCEEDED(hr = pMLStrW->GetLocale(lSrcPos, lSrcLen, &locale, NULL, NULL)) &&
  279. SUCCEEDED(hr = ::LocaleToCodePage(locale, &uLocaleCodePage)) &&
  280. SUCCEEDED(hr = PrepareMLangCodePages()) &&
  281. SUCCEEDED(hr = GetMLangCodePages()->CodePageToCodePages(uLocaleCodePage, &dwLocaleCodePages)) &&
  282. SUCCEEDED(hr = GetMLangCodePages()->GetStrCodePages(StrWalk.GetStr(), StrWalk.GetCCh(), dwLocaleCodePages, &dwStrCodePages, NULL)))
  283. {
  284. fDontHaveCodePageIn = FALSE;
  285. hr = GetMLangCodePages()->CodePagesToCodePage(dwStrCodePages, uLocaleCodePage, &uCodePageIn);
  286. }
  287. if (SUCCEEDED(hr) &&
  288. SUCCEEDED(hr = ConvWStrToAStr(pcchActual || plActualLen, uCodePageIn, StrWalk.GetStr(), StrWalk.GetCCh(), pszDest, cchDest, &cchWritten, NULL, &lWrittenLen)))
  289. {
  290. pszDest += cchWritten;
  291. cchDest -= cchWritten;
  292. cchActual += cchWritten;
  293. }
  294. StrWalk.Unlock(hr, lWrittenLen);
  295. }
  296. lActualLen = StrWalk.GetDoneLen();
  297. pMLStrW->Release();
  298. }
  299. }
  300. }
  301. if (SUCCEEDED(hr))
  302. {
  303. if (puCodePageOut)
  304. *puCodePageOut = uCodePageIn;
  305. if (pcchActual)
  306. *pcchActual = cchActual;
  307. if (plActualLen)
  308. *plActualLen = lActualLen;
  309. }
  310. else
  311. {
  312. if (puCodePageOut)
  313. *puCodePageOut = 0;
  314. if (pcchActual)
  315. *pcchActual = 0;
  316. if (plActualLen)
  317. *plActualLen = 0;
  318. }
  319. return hr;
  320. }
  321. STDMETHODIMP CMLStrAttrAStr::GetStrBufA(long lSrcPos, long lSrcMaxLen, UINT* puDestCodePage, IMLangStringBufA** ppDestBuf, long* plDestLen)
  322. {
  323. ASSERT_THIS;
  324. ASSERT_WRITE_PTR_OR_NULL(puDestCodePage);
  325. ASSERT_WRITE_PTR_OR_NULL(ppDestBuf);
  326. ASSERT_WRITE_PTR_OR_NULL(plDestLen);
  327. HRESULT hr = CheckThread();
  328. CLock Lock(FALSE, this, hr);
  329. IMLangStringBufA* pMLStrBufA;
  330. if (SUCCEEDED(hr) &&
  331. SUCCEEDED(hr = RegularizePosLen(&lSrcPos, &lSrcMaxLen)) &&
  332. lSrcMaxLen <= 0)
  333. {
  334. hr = E_INVALIDARG;
  335. }
  336. if (SUCCEEDED(hr))
  337. {
  338. pMLStrBufA = GetMLStrBufA();
  339. if (!pMLStrBufA)
  340. hr = MLSTR_E_STRBUFNOTAVAILABLE;
  341. }
  342. if (SUCCEEDED(hr))
  343. {
  344. if (puDestCodePage)
  345. *puDestCodePage = GetCodePage();
  346. if (ppDestBuf)
  347. {
  348. pMLStrBufA->AddRef();
  349. *ppDestBuf = pMLStrBufA;
  350. }
  351. if (plDestLen)
  352. *plDestLen = lSrcMaxLen;
  353. }
  354. else
  355. {
  356. if (puDestCodePage)
  357. *puDestCodePage = 0;
  358. if (ppDestBuf)
  359. *ppDestBuf = NULL;
  360. if (plDestLen)
  361. *plDestLen = 0;
  362. }
  363. return hr;
  364. }
  365. STDMETHODIMP CMLStrAttrAStr::LockAStr(long lSrcPos, long lSrcLen, long lFlags, UINT uCodePageIn, long cchRequest, UINT* puCodePageOut, CHAR** ppszDest, long* pcchDest, long* plDestLen)
  366. {
  367. ASSERT_THIS;
  368. ASSERT_WRITE_PTR_OR_NULL(puCodePageOut);
  369. ASSERT_WRITE_PTR_OR_NULL(ppszDest);
  370. ASSERT_WRITE_PTR_OR_NULL(pcchDest);
  371. ASSERT_WRITE_PTR_OR_NULL(plDestLen);
  372. HRESULT hr = CheckThread();
  373. CLock Lock(lFlags & MLSTR_WRITE, this, hr);
  374. long cchSrcPos;
  375. long cchSrcLen;
  376. CHAR* pszBuf = NULL;
  377. long cchBuf;
  378. long lLockLen;
  379. BOOL fDirectLock;
  380. if (SUCCEEDED(hr) && (!lFlags || (lFlags & ~GetBufFlags() & MLSTR_WRITE)))
  381. hr = E_INVALIDARG; // No flags specified, or not writable StrBuf; TODO: Replace StrBuf in this case if allowed
  382. if (!(lFlags & MLSTR_WRITE))
  383. cchRequest = 0;
  384. if (SUCCEEDED(hr) &&
  385. SUCCEEDED(hr = PrepareMLStrBuf()) &&
  386. SUCCEEDED(hr = RegularizePosLen(&lSrcPos, &lSrcLen)) &&
  387. SUCCEEDED(hr = GetCCh(0, lSrcPos, &cchSrcPos)) &&
  388. SUCCEEDED(hr = GetCCh(cchSrcPos, lSrcLen, &cchSrcLen)))
  389. {
  390. IMLangStringBufA* const pMLStrBufA = GetMLStrBufA();
  391. fDirectLock = (pMLStrBufA && (puCodePageOut || uCodePageIn == GetCodePage()));
  392. if (fDirectLock)
  393. {
  394. long cchInserted;
  395. long cchLockLen = cchSrcLen;
  396. if (puCodePageOut)
  397. hr = GetAStr(lSrcPos, lSrcLen, 0, &uCodePageIn, NULL, 0, NULL, NULL);
  398. if (SUCCEEDED(hr) &&
  399. cchRequest > cchSrcLen &&
  400. SUCCEEDED(hr = pMLStrBufA->Insert(cchSrcPos + cchSrcLen, cchRequest - cchSrcLen, &cchInserted)))
  401. {
  402. SetBufCCh(GetBufCCh() + cchInserted);
  403. cchLockLen += cchInserted;
  404. if (!pcchDest && cchLockLen < cchRequest)
  405. hr = E_OUTOFMEMORY; // Can't insert in StrBuf
  406. }
  407. if (SUCCEEDED(hr) &&
  408. SUCCEEDED(hr = pMLStrBufA->LockBuf(cchSrcPos, cchLockLen, &pszBuf, &cchBuf)) &&
  409. !pcchDest && cchBuf < max(cchSrcLen, cchRequest))
  410. {
  411. hr = E_OUTOFMEMORY; // Can't lock StrBuf
  412. }
  413. if (plDestLen && SUCCEEDED(hr))
  414. hr = CalcLenA(uCodePageIn, pszBuf, cchBuf, &lLockLen);
  415. }
  416. else
  417. {
  418. long cchSize;
  419. if (SUCCEEDED(hr = CalcBufSizeA(lSrcLen, &cchSize)))
  420. {
  421. cchBuf = max(cchSize, cchRequest);
  422. hr = MemAlloc(sizeof(*pszBuf) * cchBuf, (void**)&pszBuf);
  423. }
  424. if (SUCCEEDED(hr) && ((lFlags & MLSTR_READ) || puCodePageOut))
  425. hr = GetAStr(lSrcPos, lSrcLen, uCodePageIn, (puCodePageOut) ? &uCodePageIn : NULL, (lFlags & MLSTR_READ) ? pszBuf : NULL, cchBuf, (pcchDest) ? &cchBuf : NULL, (plDestLen) ? &lLockLen : NULL);
  426. }
  427. }
  428. if (SUCCEEDED(hr) &&
  429. SUCCEEDED(hr = Lock.FallThrough()))
  430. {
  431. hr = GetLockInfo()->Lock((fDirectLock) ? UnlockAStrDirect : UnlockAStrIndirect, lFlags, uCodePageIn, pszBuf, lSrcPos, lSrcLen, cchSrcPos, cchBuf);
  432. }
  433. if (SUCCEEDED(hr))
  434. {
  435. if (puCodePageOut)
  436. *puCodePageOut = uCodePageIn;
  437. if (ppszDest)
  438. *ppszDest = pszBuf;
  439. if (pcchDest)
  440. *pcchDest = cchBuf;
  441. if (plDestLen)
  442. *plDestLen = lLockLen;
  443. }
  444. else
  445. {
  446. if (pszBuf)
  447. {
  448. if (fDirectLock)
  449. GetMLStrBufA()->UnlockBuf(pszBuf, 0, 0);
  450. else
  451. MemFree(pszBuf);
  452. }
  453. if (puCodePageOut)
  454. *puCodePageOut = 0;
  455. if (ppszDest)
  456. *ppszDest = NULL;
  457. if (pcchDest)
  458. *pcchDest = 0;
  459. if (plDestLen)
  460. *plDestLen = 0;
  461. }
  462. return hr;
  463. }
  464. STDMETHODIMP CMLStrAttrAStr::UnlockAStr(const CHAR* pszSrc, long cchSrc, long* pcchActual, long* plActualLen)
  465. {
  466. ASSERT_THIS;
  467. ASSERT_READ_BLOCK(pszSrc, cchSrc);
  468. ASSERT_WRITE_PTR_OR_NULL(pcchActual);
  469. ASSERT_WRITE_PTR_OR_NULL(plActualLen);
  470. return UnlockStrCommon(pszSrc, cchSrc, pcchActual, plActualLen);
  471. }
  472. STDMETHODIMP CMLStrAttrAStr::OnRegisterAttr(IUnknown* pUnk)
  473. {
  474. return E_NOTIMPL; // CMLStrAttrAStr::OnRegisterAttr()
  475. }
  476. STDMETHODIMP CMLStrAttrAStr::OnUnregisterAttr(IUnknown* pUnk)
  477. {
  478. return E_NOTIMPL; // CMLStrAttrAStr::OnUnregisterAttr()
  479. }
  480. STDMETHODIMP CMLStrAttrAStr::OnRequestEdit(long lDestPos, long lDestLen, long lNewLen, REFIID riid, LPARAM lParam, IUnknown* pUnk)
  481. {
  482. return E_NOTIMPL; // CMLStrAttrAStr::OnRequestEdit()
  483. }
  484. STDMETHODIMP CMLStrAttrAStr::OnCanceledEdit(long lDestPos, long lDestLen, long lNewLen, REFIID riid, LPARAM lParam, IUnknown* pUnk)
  485. {
  486. return E_NOTIMPL; // CMLStrAttrAStr::OnCanceledEdit()
  487. }
  488. STDMETHODIMP CMLStrAttrAStr::OnChanged(long lDestPos, long lDestLen, long lNewLen, REFIID riid, LPARAM lParam, IUnknown* pUnk)
  489. {
  490. return E_NOTIMPL; // CMLStrAttrAStr::OnChanged()
  491. }
  492. #endif // NEWMLSTR