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.

1193 lines
36 KiB

  1. /*************************************************************************
  2. * @doc SHROOM EXTERNAL API
  3. *
  4. * HHSYSSRT.CPP
  5. *
  6. * Copyright (C) Microsoft Corporation 1997
  7. * All Rights reserved.
  8. *
  9. * This file contains the implementation of CHHSysSort methods.
  10. * CHHSysSort is a pluggable sort object that uses the system's
  11. * CompareString function to do comparisons. CHHSysSort supports
  12. * NULL terminated strings that are either Unicode or ANSI.
  13. *
  14. **************************************************************************
  15. *
  16. * Written By : Bill Aloof modified by Paul Tissue
  17. * Current Owner: PaulTi
  18. *
  19. **************************************************************************/
  20. #ifdef _DEBUG
  21. #undef THIS_FILE
  22. static const char THIS_FILE[] = __FILE__;
  23. #endif
  24. #include "header.h"
  25. #include "atlinc.h" // includes for ATL.
  26. #include "iterror.h"
  27. #include "itSort.h"
  28. #include "itSortid.h"
  29. #include "hhsyssrt.h"
  30. #include "animate.h"
  31. #ifndef ITWW_CBKEY_MAX //defined in itww.h
  32. #define ITWW_CBKEY_MAX 1024
  33. #endif
  34. #ifndef ITWW_CBREC_MAX // itww.h does not define this
  35. #define ITWW_CBREC_MAX 8
  36. #endif
  37. #define HHWW_MAX_KEYWORD_OBJECT_SIZE (ITWW_CBKEY_MAX-ITWW_CBREC_MAX)
  38. #define HHWW_MAX_KEYWORD_LENGTH (((HHWW_MAX_KEYWORD_OBJECT_SIZE-sizeof(HHKEYINFO)-sizeof(DWORD))/sizeof(WCHAR))-sizeof(WCHAR))
  39. #ifdef _DEBUG
  40. static BOOL g_bShowMessage = TRUE;
  41. #endif
  42. inline int __cdecl my_wcslen( const WCHAR* p ) { const WCHAR* pwz=p; while(*pwz++); return (int)(pwz-p-1); }
  43. inline WCHAR* __cdecl my_wcscpy( WCHAR* pdst, const WCHAR* psrc ) { WCHAR* pwz=pdst; while(*pwz++=*psrc++); return(pdst); }
  44. //---------------------------------------------------------------------------
  45. // Constructor and Destructor
  46. //---------------------------------------------------------------------------
  47. CHHSysSort::CHHSysSort()
  48. {
  49. OSVERSIONINFO osvi;
  50. m_fInitialized = m_fDirty = FALSE;
  51. memset(&m_srtctl, NULL, sizeof(SRTCTL));
  52. m_hmemAnsi1 = m_hmemAnsi2 = NULL;
  53. m_cbBufAnsi1Cur = m_cbBufAnsi2Cur = 0;
  54. // See if we're running on NT; if GetVersionEx fails, we'll assume
  55. // we're not since that's causes us do take the more conservative route
  56. // when doing comparisons.
  57. osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  58. m_fWinNT = (GetVersionEx(&osvi) ?
  59. (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) : FALSE);
  60. //MessageBox( GetActiveWindow(), "Creating HTML Help sort object!", "HHSort Module", MB_OK | MB_SETFOREGROUND | MB_SYSTEMMODAL );
  61. }
  62. CHHSysSort::~CHHSysSort()
  63. {
  64. Close();
  65. //MessageBox( GetActiveWindow(), "Destroying HTML Help sort object!", "HHSort Module", MB_OK | MB_SETFOREGROUND | MB_SYSTEMMODAL );
  66. }
  67. //---------------------------------------------------------------------------
  68. // IHHSortKey Method Implementations
  69. //---------------------------------------------------------------------------
  70. #pragma optimize( "agtw", on )
  71. /********************************************************************
  72. * @method STDMETHODIMP | IHHSortKey | GetSize |
  73. * Determines the size of a key.
  74. * @parm LPCVOID* | pvKey | Pointer to key
  75. * @parm DWORD* | pcbSize | Out param containing key size.
  76. *
  77. * @rvalue E_POINTER | pvKey or pcbSize was NULL
  78. *
  79. ********************************************************************/
  80. STDMETHODIMP
  81. CHHSysSort::GetSize(LPCVOID pvKey, DWORD* pcbSize)
  82. {
  83. if (pvKey == NULL || pcbSize == NULL)
  84. return ((E_POINTER));
  85. if (!m_fInitialized)
  86. return ((E_NOTOPEN));
  87. *pcbSize = sizeof(WCHAR) * (my_wcslen((WCHAR *)pvKey) + 1);
  88. DWORD dwLength = *pcbSize;
  89. // now add our info struct size
  90. *pcbSize += sizeof(HHKEYINFO);
  91. HHKEYINFO* pInfo = (HHKEYINFO*)(((DWORD_PTR)(pvKey))+dwLength);
  92. // now add the size representing the SeeAlso
  93. if( (pInfo->wFlags) & HHWW_SEEALSO ) {
  94. *pcbSize += sizeof(WCHAR) * (my_wcslen((WCHAR *)(((DWORD_PTR)pvKey)+*pcbSize)) + 1);
  95. }
  96. // now add the size representing the UIDs
  97. else if( !((pInfo->wFlags) & HHWW_UID_OVERFLOW) && pInfo->dwCount ) {
  98. *pcbSize += pInfo->dwCount*sizeof(DWORD);
  99. }
  100. #ifdef _DEBUG
  101. // if we get a keyword object that is too big then lets truncate it and
  102. // lie to Centaur and say every thing is OK (otherwise Centaur will fault!).
  103. if( *pcbSize > HHWW_MAX_KEYWORD_OBJECT_SIZE ) {
  104. if( g_bShowMessage ) {
  105. TCHAR szKeyword[1024];
  106. WideCharToMultiByte(m_srtctl.dwCodePageID, NULL,
  107. (PCWSTR) pvKey, wcslen((PCWSTR)pvKey)+1, szKeyword, 1024, NULL, NULL);
  108. TCHAR szMsg[4096];
  109. wsprintf( szMsg, "The keyword \"%s\" contains too many hits. Centaur will crash now!", szKeyword );
  110. strcat( szMsg, "\n\nPress 'OK' to continue or 'Cancel' to disable this warning.");
  111. int iReturn = MsgBox( szMsg, MB_OKCANCEL | MB_SETFOREGROUND | MB_SYSTEMMODAL );
  112. if( iReturn == IDCANCEL )
  113. g_bShowMessage = FALSE;
  114. }
  115. return E_UNEXPECTED;
  116. }
  117. #endif
  118. return (S_OK);
  119. }
  120. #pragma optimize( "", off )
  121. #pragma optimize( "agtw", on )
  122. /********************************************************************
  123. * @method STDMETHODIMP | IITSortKey | Compare |
  124. * Compares two keys and returns information about their sort order.
  125. *
  126. * @parm LPCVOID | pvKey1 | Pointer to a key.
  127. * @parm LPCVOID | pvKey2 | Pointer to a key.
  128. * @parm LONG* | plResult | (out) Indicates whether pvKey1 is less than, equal to, or
  129. * greater than pvKey2.
  130. * @parm DWORD* | pgrfReason | (out) Provides additional information about
  131. * the comparison (see comments below).
  132. *
  133. * @rvalue E_POINTER | Either pvKey1, pvKey2, or *plResult was NULL
  134. *
  135. * @comm
  136. * On exit, *plResult is set according to strcmp conventions:
  137. * <lt> 0, = 0, <gt> 0, depending on whether pvKey1 is less than, equal to, or
  138. * greater than pvKey2. If pgrfReason is not NULL, *pgrfReason may be
  139. * filled in on exit with one or more bit flags giving more information about
  140. * the result of the comparison, if the result was affected by something other
  141. * than raw lexical comparison (such as special character mappings). If
  142. * *pgrfReason contains 0 on exit, that means the comparison result
  143. * was purely lexical; if *pgrfReason contains IITSK_COMPREASON_UNKNOWN,
  144. * then the sort object implementation wasn't able to provide additional
  145. * information about the comparison result.
  146. *
  147. ********************************************************************/
  148. STDMETHODIMP
  149. CHHSysSort::Compare(LPCVOID pvKey1, LPCVOID pvKey2, LONG* plResult,
  150. DWORD* pgrfReason)
  151. {
  152. HRESULT hr = S_OK;
  153. LONG lResult;
  154. if (pvKey1 == NULL || pvKey2 == NULL || plResult == NULL)
  155. return ((E_POINTER));
  156. if (!m_fInitialized)
  157. return ((E_NOTOPEN));
  158. // for leveling to work, we must take leveling into consideration
  159. // so that "heading" keywords get sorted just above the associated
  160. // leveled keywords.
  161. //
  162. // for example:
  163. //
  164. // Security
  165. // rights
  166. // Admins
  167. // Users
  168. // token
  169. // Security Zones
  170. // Security, rights of Administrators
  171. //
  172. // is actually (word sorted as):
  173. //
  174. // Security
  175. // Security Zones
  176. // Security, rights
  177. // Security, rights, Admins
  178. // Security, rights, Users
  179. // Security, token
  180. //
  181. // but we want it to be:
  182. //
  183. // Security
  184. // Security, rights
  185. // Security, rights, Admins
  186. // Security, rights, Users
  187. // Security, token
  188. // Security Zones
  189. //
  190. // and we want to ignore tilde and underscore prefixing
  191. // for root level keywords
  192. // check if we need to do a new sort
  193. if( m_srtctl.dwKeyType == IHHSK100_KEYTYPE_UNICODE_SZ ) {
  194. // make copies of the keywords and substitute a space+space
  195. // for each comma+space delimiter pair
  196. //
  197. // note, work backward in the list and only substitute
  198. // in the same number has levels we have for the keyword
  199. int iChar;
  200. // keyword #1
  201. WCHAR wszKey1[HHWW_MAX_KEYWORD_LENGTH+1];
  202. my_wcscpy( (PWSTR) wszKey1, (PCWSTR) pvKey1 );
  203. PCWSTR pwszKey1 = wszKey1;
  204. int iLen1 = my_wcslen( pwszKey1 );
  205. DWORD dwLevel1 = *((DWORD*)(((DWORD_PTR)pvKey1)+(sizeof(WCHAR)*(iLen1+1)))) >> 16;
  206. if( dwLevel1 ) { // leveled string?
  207. UNALIGNED DWORD* updw = ((DWORD*)(((DWORD_PTR)pvKey1)+((sizeof(WCHAR)*(iLen1+1))+sizeof(DWORD))));
  208. int iOffset =(int)(*updw);
  209. DWORD dwCount = 0;
  210. for( iChar = iOffset; iChar > 0; iChar-- ) {
  211. if( wszKey1[iChar] == L' ' ) {
  212. if( ((iChar-1) > 0) && (wszKey1[iChar-1] == L',') ) {
  213. wszKey1[iChar] = L' ';
  214. wszKey1[iChar-1] = L' ';
  215. if( ++dwCount == dwLevel1 )
  216. break;
  217. }
  218. }
  219. }
  220. }
  221. // keyword #2
  222. WCHAR wszKey2[HHWW_MAX_KEYWORD_LENGTH+1];
  223. my_wcscpy( (PWSTR) wszKey2, (PCWSTR) pvKey2 );
  224. PCWSTR pwszKey2 = wszKey2;
  225. int iLen2 = my_wcslen( pwszKey2 );
  226. DWORD dwLevel2 = *((DWORD*)(((DWORD_PTR)pvKey2)+(sizeof(WCHAR)*(iLen2+1)))) >> 16;
  227. if( dwLevel2 ) { // leveled string?
  228. UNALIGNED DWORD* updw = (DWORD*)(((DWORD_PTR)pvKey2) + ((sizeof(WCHAR)*(iLen2+1))+sizeof(DWORD)));
  229. int iOffset =(int)(*updw);
  230. DWORD dwCount = 0;
  231. iChar = iOffset;
  232. while (iChar > 0)
  233. {
  234. iChar--;
  235. }
  236. for( iChar = iOffset; iChar > 0; iChar-- ) {
  237. if( wszKey2[iChar] == L' ' ) {
  238. if( ((iChar-1) > 0) && (wszKey2[iChar-1] == L',') ) {
  239. wszKey2[iChar] = L' ';
  240. wszKey2[iChar-1] = L' ';
  241. if( ++dwCount == dwLevel2 )
  242. break;
  243. }
  244. }
  245. }
  246. }
  247. // determine number of special char prefixes
  248. // keyword #1
  249. WCHAR wszPrefix1[HHWW_MAX_KEYWORD_LENGTH+1];
  250. int iSpecialChars1 = 0; // a value of -1 means all chars are special
  251. for( iChar = 0; iChar < iLen1; iChar++ ) {
  252. if( ( (*pwszKey1) == L'_') || ( (*pwszKey1) == L'~') ) {
  253. wszPrefix1[iChar] = *pwszKey1;
  254. iSpecialChars1++;
  255. pwszKey1++;
  256. }
  257. else
  258. break;
  259. }
  260. wszPrefix1[iSpecialChars1] = L'\0';
  261. if( iChar == iLen1 ) {
  262. iSpecialChars1 = -1;
  263. }
  264. // keyword #2
  265. WCHAR wszPrefix2[HHWW_MAX_KEYWORD_LENGTH+1];
  266. int iSpecialChars2 = 0; // a value of -1 means all chars are special
  267. for( iChar = 0; iChar < iLen2; iChar++ ) {
  268. if( ( (*pwszKey2) == L'_') || ( (*pwszKey2) == L'~') ) {
  269. wszPrefix2[iChar] = *pwszKey2;
  270. iSpecialChars2++;
  271. pwszKey2++;
  272. }
  273. else
  274. break;
  275. }
  276. wszPrefix2[iSpecialChars2] = L'\0';
  277. if( iChar == iLen2 ) {
  278. iSpecialChars2 = -1;
  279. }
  280. // if both of the keywords contains just special chars or
  281. // none of them contain special characters then we do a normal sort
  282. if( !( ((iSpecialChars1 == -1) && (iSpecialChars2 == -1)) ||
  283. ((!iSpecialChars1) && (!iSpecialChars2)) ) ) {
  284. // do a normal sort with the special characters ignored
  285. if( iSpecialChars1 == -1 )
  286. *plResult = -1;
  287. else if( iSpecialChars2 == -1 )
  288. *plResult = 1;
  289. else if( SUCCEEDED(hr = CompareSz(pwszKey1,
  290. /*my_wcslen(pwszKey1)*/ (iSpecialChars1==-1)?0:iLen1-(int)((((DWORD_PTR)pwszKey1)-((DWORD_PTR)&wszKey1))/sizeof(WCHAR)),
  291. pwszKey2,
  292. /*my_wcslen(pwszKey2)*/ (iSpecialChars2==-1)?0:iLen2-(int)((((DWORD_PTR)pwszKey2)-((DWORD_PTR)&wszKey2))/sizeof(WCHAR)),
  293. &lResult, TRUE)) ) {
  294. *plResult = lResult;
  295. // if identical then sort based on the prefixes
  296. if( lResult == 0 ) {
  297. // if both contain special prefixes then sort based on the prefixes
  298. if( iSpecialChars1 && iSpecialChars2 ) {
  299. if( SUCCEEDED(hr = CompareSz(wszPrefix1,
  300. /*my_wcslen(wszPrefix1)*/ (iSpecialChars1==-1)?(LONG)((((DWORD_PTR)pwszKey1)-((DWORD_PTR)&wszKey1))/sizeof(WCHAR)):iSpecialChars1,
  301. wszPrefix2,
  302. /*my_wcslen(wszPrefix2)*/ (iSpecialChars2==-1)?(LONG)((((DWORD_PTR)pwszKey2)-((DWORD_PTR)&wszKey2))/sizeof(WCHAR)):iSpecialChars2,
  303. &lResult, TRUE)) ) {
  304. *plResult = lResult;
  305. }
  306. }
  307. else if( iSpecialChars1 )
  308. *plResult = 1;
  309. else if( iSpecialChars2 )
  310. *plResult = -1;
  311. else
  312. *plResult = 0;
  313. }
  314. // verify that they do not differ by level alone
  315. // if they do differ then place the lowest level one last
  316. if( lResult == 0 ) {
  317. if( dwLevel1 > dwLevel2 )
  318. *plResult = -1;
  319. if( dwLevel1 < dwLevel2 )
  320. *plResult = 1;
  321. }
  322. }
  323. return (hr);
  324. }
  325. // normal sort
  326. if (SUCCEEDED(hr = CompareSz(wszKey1, iLen1, wszKey2, iLen2, &lResult, TRUE ) ) ) {
  327. *plResult = lResult;
  328. // verify that they do not differ by level alone
  329. // if they do differ then place the lowest level one last
  330. if( lResult == 0 ) {
  331. if( dwLevel1 > dwLevel2 )
  332. *plResult = -1;
  333. if( dwLevel1 < dwLevel2 )
  334. *plResult = 1;
  335. }
  336. }
  337. return (hr);
  338. }
  339. // normal sort
  340. if( SUCCEEDED(hr = CompareSz(pvKey1, -1, pvKey2, -1, &lResult, TRUE) ) )
  341. {
  342. // We can set the out params now that we know no error occurred.
  343. *plResult = lResult;
  344. if (pgrfReason != NULL)
  345. *pgrfReason = IITSK_COMPREASON_UNKNOWN;
  346. }
  347. else
  348. {
  349. // Some kind of unexpected error occurred.
  350. //SetErrCode(&hr, E_UNEXPECTED);
  351. }
  352. return (hr);
  353. }
  354. #pragma optimize( "", off )
  355. /********************************************************************
  356. * @method STDMETHODIMP | IITSortKey | IsRelated |
  357. * Compares two keys and returns information about their sort order.
  358. *
  359. * @parm LPCVOID | pvKey1 | Pointer to a key.
  360. * @parm LPCVOID | pvKey2 | Pointer to a key.
  361. * @parm DWORD | dwKeyRelation | Specifies the relationship to check.
  362. * Valid parameters are: <nl>
  363. * IITSK_KEYRELATION_PREFIX ((DWORD) 0) <nl>
  364. * IITSK_KEYRELATION_INFIX ((DWORD) 1) <nl>
  365. * IITSK_KEYRELATION_SUFFIX ((DWORD) 2) <nl>
  366. * @parm DWORD* | pgrfReason | (out) Provides additional information about
  367. * the comparison.
  368. *
  369. * @rvalue S_OK | Indicates that pvKey1 is related to pvKey2 according to
  370. * dwKeyRelation.
  371. * @rvalue S_FALSE | pvKey1 is not related to pvKey2.
  372. * @rvalue E_INVALIDARG | The value specified for dwKeyRelation is not supported.
  373. *
  374. * @comm
  375. * If pgrfReason is not NULL, *pgrfReason will be filled in
  376. * just as it would be by IITSortKey::Compare.
  377. *
  378. *
  379. ********************************************************************/
  380. STDMETHODIMP
  381. CHHSysSort::IsRelated(LPCVOID pvKey1, LPCVOID pvKey2, DWORD dwKeyRelation,
  382. DWORD* pgrfReason)
  383. {
  384. HRESULT hr;
  385. LONG lResult;
  386. // We will let the first call to Compare catch any entry error
  387. // conditions because it checks for everything we would, except for
  388. // the type of key relation the caller is testing for.
  389. if (dwKeyRelation != IITSK_KEYRELATION_PREFIX)
  390. return ((E_INVALIDARG));
  391. if (SUCCEEDED(hr = Compare(pvKey1, pvKey2, &lResult, NULL)))
  392. {
  393. if (lResult < 0)
  394. {
  395. LONG cchKey1;
  396. cchKey1 = my_wcslen((WCHAR *) pvKey1);
  397. if (SUCCEEDED(hr = CompareSz(pvKey1, cchKey1,
  398. pvKey2, cchKey1,
  399. &lResult, TRUE)))
  400. {
  401. hr = (lResult == 0 ? S_OK : S_FALSE);
  402. }
  403. }
  404. else
  405. hr = (lResult == 0 ? S_OK : S_FALSE);
  406. }
  407. if (SUCCEEDED(hr) && pgrfReason != NULL)
  408. *pgrfReason = IITSK_COMPREASON_UNKNOWN;
  409. return (hr);
  410. }
  411. /*****************************************************************
  412. *@method STDMETHODIMP | IITSortKey | Convert |
  413. * Converts a key of one type into a key of another type.
  414. * @parm DWORD | dwKeyTypeIn | Type of input key
  415. * @parm LPCVOID | pvKeyIn | Pointer to input key
  416. * @parm DWORD | dwKeyTypeOut | Type to convert key to.
  417. * @parm LPCVOID | pvKeyOut | Pointer to buffer for output key.
  418. * @parm DWORD | *pcbSizeOut | Size of output buffer.
  419. *
  420. * @rvalue S_OK | The operation completed successfully.
  421. * @rvalue E_INVALIDARG | the specified conversion is not supported,
  422. * for example, one or both of the REFGUID parameters is invalid.
  423. * @rvalue E_FAIL | the buffer pointed to by pvKeyOut was too small
  424. * to hold the converted key.
  425. * @comm
  426. * This is intended mainly for converting an uncompressed key
  427. * into a compressed key, but a sort object is free to provide
  428. * whatever conversion combinations it wants to.
  429. * *pcbSizeOut should contain the size of the buffer pointed
  430. * to by pvKeyOut. To make sure the buffer size specified in
  431. * *pcbSizeOut is adequate, pass 0 on entry.
  432. *
  433. * @comm
  434. * Not implemented yet.
  435. ****************************************************************/
  436. STDMETHODIMP
  437. CHHSysSort::Convert(DWORD dwKeyTypeIn, LPCVOID pvKeyIn,
  438. DWORD dwKeyTypeOut, LPVOID pvKeyOut, DWORD* pcbSizeOut)
  439. {
  440. if (!m_fInitialized)
  441. return ((E_NOTOPEN));
  442. return (E_NOTIMPL);
  443. }
  444. STDMETHODIMP
  445. CHHSysSort::ResolveDuplicates( LPCVOID pvKey1, LPCVOID pvKey2,
  446. LPCVOID pvKeyOut, DWORD* pcbSizeOut)
  447. {
  448. HRESULT hr = S_OK;
  449. NextAnimation();
  450. // get keyword 1 stuff
  451. int iLen1 = my_wcslen( (WCHAR*) pvKey1 );
  452. int iOffsetInfo1 = sizeof(WCHAR) * (iLen1+1);
  453. HHKEYINFO* pInfo1 = (HHKEYINFO*)(((DWORD_PTR)pvKey1)+iOffsetInfo1);
  454. int iOffsetURLIds1 = iOffsetInfo1 + sizeof(HHKEYINFO);
  455. // get keyword 2 stuff
  456. int iLen2 = my_wcslen( (WCHAR*) pvKey2 );
  457. int iOffsetInfo2 = sizeof(WCHAR) * (iLen2+1);
  458. HHKEYINFO* pInfo2 = (HHKEYINFO*)(((DWORD_PTR)pvKey2)+iOffsetInfo2);
  459. int iOffsetURLIds2 = iOffsetInfo2 + sizeof(HHKEYINFO);
  460. // copy the string (from the shortest or the first key)
  461. const WCHAR* pwszKeyOut = NULL;
  462. int iOffsetOut = 0;
  463. if( iOffsetInfo2 < iOffsetInfo1 ) {
  464. iOffsetOut = iOffsetInfo2;
  465. pwszKeyOut = (WCHAR*) pvKey2;
  466. }
  467. else {
  468. iOffsetOut = iOffsetInfo1;
  469. pwszKeyOut = (WCHAR*) pvKey1;
  470. }
  471. my_wcscpy( (WCHAR*) pvKeyOut, pwszKeyOut );
  472. // if either key has reached the maximum size or the other key is a
  473. // See Also, then return just that key and continue
  474. DWORD dwKey1URLIdsSize = ((pInfo1->wFlags) & HHWW_SEEALSO) ? 0 : ((pInfo1->dwCount)*sizeof(DWORD));
  475. DWORD dwKey2URLIdsSize = ((pInfo2->wFlags) & HHWW_SEEALSO) ? 0 : ((pInfo2->dwCount)*sizeof(DWORD));
  476. DWORD dwKey1Size = iOffsetInfo1 + sizeof(HHKEYINFO) + dwKey1URLIdsSize;
  477. DWORD dwKey2Size = iOffsetInfo2 + sizeof(HHKEYINFO) + dwKey2URLIdsSize;
  478. DWORD dwKeyOutSize = iOffsetOut + sizeof(HHKEYINFO) + dwKey1URLIdsSize + dwKey2URLIdsSize;
  479. // default largest stuff to key 1
  480. BOOL bCopyLargest = FALSE;
  481. LPCVOID pvKeyLargest = pvKey1;
  482. HHKEYINFO* pInfoLargest = pInfo1;
  483. int iOffsetInfoLargest = iOffsetInfo1;
  484. int iOffsetURLIdsLargest = iOffsetURLIds1;
  485. // determine if any single key exceeds the max or if just one key is a see also
  486. DWORD dwTruncate = 0;
  487. if( (dwKey1Size >= HHWW_MAX_KEYWORD_OBJECT_SIZE) || ((pInfo2->wFlags) & HHWW_SEEALSO) ) {
  488. bCopyLargest = TRUE;
  489. }
  490. else if( (dwKey2Size >= HHWW_MAX_KEYWORD_OBJECT_SIZE) || ((pInfo1->wFlags) & HHWW_SEEALSO) ) {
  491. bCopyLargest = TRUE;
  492. pvKeyLargest = pvKey2;
  493. pInfoLargest = pInfo2;
  494. iOffsetInfoLargest = iOffsetInfo2;
  495. iOffsetURLIdsLargest = iOffsetURLIds2;
  496. }
  497. else if( dwKeyOutSize > HHWW_MAX_KEYWORD_OBJECT_SIZE ) {
  498. dwTruncate = ((dwKeyOutSize - HHWW_MAX_KEYWORD_OBJECT_SIZE) / sizeof(DWORD)) + ((dwKeyOutSize%sizeof(DWORD))?1:0);
  499. }
  500. // copy the info struct
  501. HHKEYINFO Info = *pInfoLargest;
  502. if( !bCopyLargest ) {
  503. Info.dwCount = pInfo1->dwCount + pInfo2->dwCount - dwTruncate;
  504. }
  505. if( (Info.wFlags) & HHWW_SEEALSO )
  506. Info.dwCount = 0; // reset the UID count if this is just a See Also
  507. *( (HHKEYINFO*)( ( (DWORD_PTR)pvKeyOut)+iOffsetOut) ) = Info;
  508. iOffsetOut += sizeof(Info);
  509. // append the UIDs
  510. if( !bCopyLargest ) {
  511. // add first set of UIDs
  512. DWORD dwCount = pInfo1->dwCount;
  513. if( dwCount > Info.dwCount ) {
  514. dwCount = Info.dwCount;
  515. dwTruncate = pInfo2->dwCount;
  516. }
  517. UNALIGNED DWORD* pdwURLId = (DWORD*)(((DWORD_PTR)pvKey1)+iOffsetURLIds1);
  518. for( int iURLId = 0; iURLId < (int) dwCount; iURLId++ ) {
  519. *((UNALIGNED DWORD*)(((DWORD_PTR)pvKeyOut)+iOffsetOut)) = *(pdwURLId+iURLId);
  520. iOffsetOut += sizeof(DWORD);
  521. }
  522. // add second set of UIDs
  523. dwCount = pInfo2->dwCount - dwTruncate;
  524. pdwURLId = (DWORD*)(((DWORD_PTR)pvKey2)+iOffsetURLIds2);
  525. for( iURLId = 0; iURLId < (int) dwCount; iURLId++ ) {
  526. *((UNALIGNED DWORD*)(((DWORD_PTR)pvKeyOut)+iOffsetOut)) = *(pdwURLId+iURLId);
  527. iOffsetOut += sizeof(DWORD);
  528. }
  529. }
  530. else { // copy largest
  531. // if it is a See Also, then just store that
  532. if( (pInfoLargest->wFlags) & HHWW_SEEALSO ) {
  533. // we just need to copy the see also string and update the offset
  534. WCHAR* pwszSeeAlso = (WCHAR*)(((DWORD_PTR)pvKeyLargest)+iOffsetURLIdsLargest);
  535. int iLen = my_wcslen(pwszSeeAlso) + 1;
  536. for( int iChar = 0; iChar < iLen; iChar++ ) {
  537. *((WCHAR*)(((DWORD_PTR)pvKeyOut)+iOffsetOut)) = *((WCHAR*)(((DWORD_PTR)pwszSeeAlso)+(iChar*sizeof(WCHAR))));
  538. iOffsetOut += sizeof(WCHAR);
  539. }
  540. }
  541. else { // otherwise, add the UIDs only
  542. DWORD dwCount = pInfoLargest->dwCount;
  543. UNALIGNED DWORD* pdwURLId = (DWORD*)(((DWORD_PTR)pvKeyLargest)+iOffsetURLIdsLargest);
  544. for( int iURLId = 0; iURLId < (int) dwCount; iURLId++ ) {
  545. *((UNALIGNED DWORD*)(((DWORD_PTR)pvKeyOut)+iOffsetOut)) = *(pdwURLId+iURLId);
  546. iOffsetOut += sizeof(DWORD);
  547. }
  548. }
  549. }
  550. *pcbSizeOut = iOffsetOut;
  551. return hr;
  552. }
  553. //---------------------------------------------------------------------------
  554. // IHHSortKeyConfig Method Implementations
  555. //---------------------------------------------------------------------------
  556. /*******************************************************************
  557. * @method STDMETHODIMP | IITSortKeyConfig | SetLocaleInfo |
  558. * Sets locale information to be used by the sort key interface.
  559. *
  560. * @parm DWORD | dwCodePageID | ANSI code page no. specified at build time.
  561. * @parm LCID | lcid | Win32 locale identifier specified at build time.
  562. *
  563. * @rvalue S_OK | The operation completed successfully.
  564. *
  565. ********************************************************************/
  566. STDMETHODIMP
  567. CHHSysSort::SetLocaleInfo(DWORD dwCodePageID, LCID lcid)
  568. {
  569. if (!m_fInitialized)
  570. return ((E_NOTOPEN));
  571. m_cs.Lock();
  572. m_srtctl.dwCodePageID = dwCodePageID;
  573. m_srtctl.lcid = lcid;
  574. m_fDirty = TRUE;
  575. m_cs.Unlock();
  576. return (S_OK);
  577. }
  578. /*******************************************************************
  579. * @method STDMETHODIMP | IITSortKeyConfig | GetLocaleInfo |
  580. * Retrieves locale information used by the sort key interface.
  581. *
  582. * @parm DWORD | dwCodePageID | ANSI code page no. specified at build time.
  583. * @parm LCID | lcid | Win32 locale identifier specified at build time.
  584. *
  585. * @rvalue E_POINTER | Either pdwCodePageID or plcid is NULL.
  586. * @rvalue E_NOTOPEN | (?) is not initialized.
  587. * @rvalue S_OK | The operation completed successfully.
  588. *
  589. ********************************************************************/
  590. STDMETHODIMP
  591. CHHSysSort::GetLocaleInfo(DWORD* pdwCodePageID, LCID* plcid)
  592. {
  593. if (pdwCodePageID == NULL || plcid == NULL)
  594. return ((E_POINTER));
  595. if (!m_fInitialized)
  596. return ((E_NOTOPEN));
  597. m_cs.Lock();
  598. *pdwCodePageID = m_srtctl.dwCodePageID;
  599. *plcid = m_srtctl.lcid;
  600. m_cs.Unlock();
  601. return (S_OK);
  602. }
  603. /*******************************************************************
  604. * @method STDMETHODIMP | IITSortKeyConfig | SetKeyType |
  605. * Sets the sort key type that the sort object expects to see in calls
  606. * that take keys as parameters (IITSortKey::GetSize, Compare, IsRelated).
  607. *
  608. * @parm DWORD | dwKeyType | Sort key type. Possible values are:
  609. * IITSK_KEYTYPE_UNICODE_SZ or IITSK_KEYTYPE_ANSI_SZ
  610. *
  611. * @rvalue S_OK | The sort key type was understood by the sort object.
  612. * @rvalue E_INVALIDARG | Invalid sort key type.
  613. *
  614. ********************************************************************/
  615. STDMETHODIMP
  616. CHHSysSort::SetKeyType(DWORD dwKeyType)
  617. {
  618. if (!m_fInitialized)
  619. return ((E_NOTOPEN));
  620. switch (dwKeyType)
  621. {
  622. case IHHSK666_KEYTYPE_UNICODE_SZ:
  623. case IHHSK100_KEYTYPE_UNICODE_SZ:
  624. break;
  625. default:
  626. return ((E_INVALIDARG));
  627. };
  628. m_cs.Lock();
  629. m_srtctl.dwKeyType = dwKeyType;
  630. m_fDirty = TRUE;
  631. m_cs.Unlock();
  632. return (S_OK);
  633. }
  634. /*******************************************************************
  635. * @method STDMETHODIMP | IITSortKeyConfig | GetKeyType |
  636. * Retrieves the sort key type that the sort object expects to see in calls
  637. * that take keys as parameters (IITSortKey::GetSize, Compare, IsRelated).
  638. *
  639. * @parm DWORD* | pdwKeyType | Pointer to the sort key type.
  640. *
  641. * @rvalue S_OK | The operation completed successfully.
  642. * @rvalue E_POINTER | The key type is null.
  643. *
  644. ********************************************************************/
  645. STDMETHODIMP
  646. CHHSysSort::GetKeyType(DWORD* pdwKeyType)
  647. {
  648. if (pdwKeyType == NULL)
  649. return ((E_POINTER));
  650. if (!m_fInitialized)
  651. return ((E_NOTOPEN));
  652. *pdwKeyType = m_srtctl.dwKeyType;
  653. return (S_OK);
  654. }
  655. /*******************************************************************
  656. * @method STDMETHODIMP | IITSortKeyConfig | SetControlInfo |
  657. * Sets data that controls how sort key comparisons are made.
  658. *
  659. * @parm DWORD | grfSortFlags | One or more of the following sort flags:<nl>
  660. * IITSKC_SORT_STRINGSORT 0x00001000 use string sort method <nl>
  661. * IITSKC_NORM_IGNORECASE 0x00000001 ignore case <nl>
  662. * IITSKC_NORM_IGNORENONSPACE 0x00000002 ignore nonspacing chars <nl>
  663. * IITSKC_NORM_IGNORESYMBOLS 0x00000004 ignore symbols <nl>
  664. * IITSKC_NORM_IGNOREKANATYPE 0x00010000 ignore kanatype <nl>
  665. * IITSKC_NORM_IGNOREWIDTH 0x00020000 ignore width <nl>
  666. *
  667. * @parm DWORD | dwReserved | Reserved for future use.
  668. *
  669. *
  670. ********************************************************************/
  671. STDMETHODIMP
  672. CHHSysSort::SetControlInfo(DWORD grfSortFlags, DWORD dwReserved)
  673. {
  674. DWORD grfFlagsUnsupported;
  675. if (!m_fInitialized)
  676. return ((E_NOTOPEN));
  677. grfFlagsUnsupported = ~(IITSKC_SORT_STRINGSORT |
  678. IITSKC_NORM_IGNORECASE |
  679. IITSKC_NORM_IGNORENONSPACE |
  680. IITSKC_NORM_IGNORESYMBOLS |
  681. IITSKC_NORM_IGNORESYMBOLS |
  682. IITSKC_NORM_IGNOREKANATYPE |
  683. IITSKC_NORM_IGNOREWIDTH);
  684. if ((grfSortFlags & grfFlagsUnsupported) != 0)
  685. return ((E_INVALIDARG));
  686. m_cs.Lock();
  687. m_srtctl.grfSortFlags = grfSortFlags;
  688. m_fDirty = TRUE;
  689. m_cs.Unlock();
  690. return (S_OK);
  691. }
  692. /*******************************************************************
  693. * @method STDMETHODIMP | IITSortKeyConfig | GetControlInfo |
  694. * Retrieves data that controls how sort key comparisons are made.
  695. *
  696. * @parm DWORD* | pgrfSortFlags | Pointer to the sort key flags. See
  697. * <om .SetControlInfo> for a list of valid flags.
  698. *
  699. * @parm DWORD* | pdwReserved | Reserved for future use.
  700. *
  701. *
  702. * @rvalue E_POINTER | The value pgrfSortFlags is NULL.
  703. * @rvalue S_OK | The operation completed successfully.
  704. *
  705. ********************************************************************/
  706. STDMETHODIMP
  707. CHHSysSort::GetControlInfo(DWORD* pgrfSortFlags, DWORD* pdwReserved)
  708. {
  709. if (pgrfSortFlags == NULL)
  710. return ((E_POINTER));
  711. if (!m_fInitialized)
  712. return ((E_NOTOPEN));
  713. *pgrfSortFlags = m_srtctl.grfSortFlags;
  714. return (S_OK);
  715. }
  716. /*******************************************************************
  717. * @method STDMETHODIMP | IITSortKeyConfig | LoadExternalSortData |
  718. * Loads external sort data such as tables containing the relative
  719. * sort order of specific characters for a textual key type, from the
  720. * specified stream.
  721. *
  722. * @parm IStream* | pStream | Pointer to the external stream object
  723. * from which to load data.
  724. * @parm DWORD | dwExtDataType | Describes the format of sort data.
  725. *
  726. * @comm
  727. * Although the format of the external sort data is entirely
  728. * implementation-specific, this interface provides a general type for
  729. * data that can be passed in dwExtDataType: IITWBC_EXTDATA_SORTTABLE ((DWORD) 2).
  730. *
  731. * @comm
  732. * Not implemented yet.
  733. ********************************************************************/
  734. STDMETHODIMP
  735. CHHSysSort::LoadExternalSortData(IStream* pStream, DWORD dwExtDataType)
  736. {
  737. if (!m_fInitialized)
  738. return ((E_NOTOPEN));
  739. return (E_NOTIMPL);
  740. }
  741. //---------------------------------------------------------------------------
  742. // IPersistStreamInit Method Implementations
  743. //---------------------------------------------------------------------------
  744. STDMETHODIMP
  745. CHHSysSort::GetClassID(CLSID* pclsid)
  746. {
  747. if (pclsid == NULL)
  748. return ((E_POINTER));
  749. *pclsid = CLSID_HHSysSort;
  750. return (S_OK);
  751. }
  752. STDMETHODIMP
  753. CHHSysSort::IsDirty(void)
  754. {
  755. if (!m_fInitialized)
  756. return ((E_NOTOPEN));
  757. return (m_fDirty ? S_OK : S_FALSE);
  758. }
  759. STDMETHODIMP
  760. CHHSysSort::Load(IStream* pStream)
  761. {
  762. HRESULT hr;
  763. DWORD dwVersion;
  764. DWORD cbRead;
  765. if (pStream == NULL)
  766. return ((E_POINTER));
  767. // Lock before checking m_fInitialized to make sure we don't compete
  768. // with a call to ::InitNew.
  769. m_cs.Lock();
  770. if (m_fInitialized)
  771. return ((E_ALREADYOPEN));
  772. if (SUCCEEDED(hr = pStream->Read((LPVOID) &dwVersion, sizeof(DWORD), &cbRead)) &&
  773. SUCCEEDED(hr = ((cbRead == sizeof(DWORD)) ? S_OK : E_BADFORMAT)) &&
  774. SUCCEEDED(hr = ((dwVersion == HHSYSSORT_VERSION) ? S_OK : E_BADVERSION)) &&
  775. SUCCEEDED(hr = pStream->Read((LPVOID) &m_srtctl, sizeof(SRTCTL), &cbRead)) &&
  776. SUCCEEDED(hr = ((cbRead == sizeof(SRTCTL)) ? S_OK : E_BADFORMAT)))
  777. {
  778. m_fInitialized = TRUE;
  779. }
  780. m_cs.Unlock();
  781. return (hr);
  782. }
  783. STDMETHODIMP
  784. CHHSysSort::Save(IStream* pStream, BOOL fClearDirty)
  785. {
  786. HRESULT hr;
  787. DWORD dwVersion;
  788. DWORD cbWritten;
  789. if (pStream == NULL)
  790. return ((E_POINTER));
  791. if (!m_fInitialized)
  792. return ((E_NOTOPEN));
  793. m_cs.Lock();
  794. dwVersion = HHSYSSORT_VERSION;
  795. if (SUCCEEDED(hr = pStream->Write((LPVOID) &dwVersion, sizeof(DWORD), &cbWritten)) &&
  796. SUCCEEDED(hr = pStream->Write((LPVOID) &m_srtctl, sizeof(SRTCTL), &cbWritten)) &&
  797. fClearDirty)
  798. {
  799. m_fDirty = FALSE;
  800. }
  801. m_cs.Unlock();
  802. return (hr);
  803. }
  804. STDMETHODIMP
  805. CHHSysSort::GetSizeMax(ULARGE_INTEGER* pcbSizeMax)
  806. {
  807. return (E_NOTIMPL);
  808. }
  809. STDMETHODIMP
  810. CHHSysSort::InitNew(void)
  811. {
  812. // Lock before checking m_fInitialized to make sure we don't compete
  813. // with a call to ::Load.
  814. m_cs.Lock();
  815. if (m_fInitialized)
  816. return ((E_ALREADYOPEN));
  817. m_srtctl.dwCodePageID = GetACP();
  818. m_srtctl.lcid = GetUserDefaultLCID();
  819. m_srtctl.dwKeyType = IHHSK100_KEYTYPE_UNICODE_SZ;
  820. // CompareString does word sort by default, but we have to
  821. // tell it to ignore case.
  822. m_srtctl.grfSortFlags = IITSKC_NORM_IGNORECASE;
  823. m_fInitialized = TRUE;
  824. m_cs.Unlock();
  825. return (S_OK);
  826. }
  827. //---------------------------------------------------------------------------
  828. // Private Method Implementations
  829. //---------------------------------------------------------------------------
  830. #pragma optimize( "agtw", on )
  831. // Compares either two Unicode strings or two Ansi strings, calling the
  832. // appropriate variant of CompareString. The cch params should denote
  833. // count of characters, NOT bytes, not including a NULL terminator. -1
  834. // is a valid value for the cch params, which means compare the strings
  835. // until a NULL terminator is found. If fUnicode is TRUE, this routine
  836. // may decide to convert the string to Ansi before doing the compare if
  837. // the system doesn't support CompareStringW. The result of the
  838. // comparison is returned in *plResult in strcmp-compatible form.
  839. HRESULT
  840. CHHSysSort::CompareSz(LPCVOID pvSz1, LONG cch1, LPCVOID pvSz2, LONG cch2,
  841. LONG* plResult, BOOL fUnicode)
  842. {
  843. HRESULT hr = S_OK;
  844. LONG lResult;
  845. BOOL fAnsiCompare;
  846. SRTCTL srtctl;
  847. PSTR psz1 = NULL;
  848. PSTR psz2 = NULL;
  849. m_cs.Lock();
  850. srtctl = m_srtctl;
  851. m_cs.Unlock();
  852. fAnsiCompare = !fUnicode || !m_fWinNT;
  853. // See if we need to convert from Unicode to ANSI.
  854. if (fAnsiCompare && fUnicode)
  855. {
  856. DWORD cbAnsi1;
  857. DWORD cbAnsi2;
  858. m_cs.Lock();
  859. if (cch1 < 0)
  860. hr = GetSize(pvSz1, &cbAnsi1);
  861. else
  862. // leave enough space for double byte chars in MBCS.
  863. cbAnsi1 = (cch1 + 1) * sizeof(WCHAR);
  864. if (cch2 < 0)
  865. hr = GetSize(pvSz2, &cbAnsi2);
  866. else
  867. // leave enough space for double byte chars in MBCS.
  868. cbAnsi2 = (cch2 + 1) * sizeof(WCHAR);
  869. if (SUCCEEDED(hr) &&
  870. SUCCEEDED(hr = ReallocBuffer(&m_hmemAnsi1, &m_cbBufAnsi1Cur, cbAnsi1)) &&
  871. SUCCEEDED(hr = ReallocBuffer(&m_hmemAnsi2, &m_cbBufAnsi2Cur, cbAnsi2)))
  872. {
  873. // We lock the ansi buffers here, but we won't unlock them
  874. // until the end of this routine so that we can pass them
  875. // to compare string.
  876. psz1 = (PSTR) GlobalLock(m_hmemAnsi1);
  877. psz2 = (PSTR) GlobalLock(m_hmemAnsi2);
  878. if ((cch1 = WideCharToMultiByte(srtctl.dwCodePageID, NULL,
  879. (PCWSTR) pvSz1, cch1, psz1, m_cbBufAnsi1Cur, NULL, NULL)) != 0 &&
  880. (cch2 = WideCharToMultiByte(srtctl.dwCodePageID, NULL,
  881. (PCWSTR) pvSz2, cch2, psz2, m_cbBufAnsi2Cur, NULL, NULL)) != 0)
  882. {
  883. // Set up for call to CompareStringA.
  884. psz1[cch1] = 0;
  885. psz2[cch2] = 0;
  886. pvSz1 = (LPCVOID) psz1;
  887. pvSz2 = (LPCVOID) psz2;
  888. }
  889. else
  890. hr = E_UNEXPECTED;
  891. }
  892. }
  893. if (SUCCEEDED(hr))
  894. {
  895. if (fAnsiCompare)
  896. lResult = CompareStringA(srtctl.lcid, srtctl.grfSortFlags,
  897. (PCSTR) pvSz1, cch1, (PCSTR) pvSz2, cch2);
  898. else
  899. lResult = CompareStringW(srtctl.lcid, srtctl.grfSortFlags,
  900. (PCWSTR) pvSz1, cch1, (PCWSTR) pvSz2, cch2);
  901. if (lResult == 0)
  902. // Some kind of unexpected error occurred.
  903. ; //SetErrCode(&hr, E_UNEXPECTED);
  904. else
  905. // We need to subtract 2 from the lResult to convert
  906. // it into a strcmp-compatible form.
  907. *plResult = lResult - 2;
  908. }
  909. if (psz1 != NULL)
  910. GlobalUnlock(m_hmemAnsi1);
  911. if (psz2 != NULL)
  912. GlobalUnlock(m_hmemAnsi2);
  913. if (fAnsiCompare && fUnicode)
  914. m_cs.Unlock();
  915. return (hr);
  916. }
  917. #pragma optimize( "", off )
  918. /*************************************************************************
  919. * @doc INTERNAL
  920. *
  921. * @func HRESULT | ReallocBufferHmem |
  922. * This function will reallocate or allocate anew a buffer of
  923. * requested size.
  924. *
  925. * @parm HGLOBAL* | phmemBuf |
  926. * Pointer to buffer handle; buffer handle can be NULL if
  927. * a new buffer needs to be allocated. New buffer handle
  928. * is returned through this param.
  929. *
  930. * @parm DWORD* | pcbBufCur |
  931. * Current size of existing buffer, if any. Should be
  932. * 0 if *phmemBuf == 0. New size is returned through
  933. * this param.
  934. *
  935. * @parm DWORD | cbBufNew |
  936. * Current size of existing buffer, if any. Should be
  937. * 0 if *phmemBuf == 0.
  938. *
  939. * @rvalue E_POINTER | phmemBuf or pcbBufCur was NULL
  940. * @rvalue E_OUTOFMEMORY | Ran out of memory (re)allocating the buffer.
  941. *************************************************************************/
  942. HRESULT ReallocBufferHmem(HGLOBAL* phmemBuf, DWORD* pcbBufCur,
  943. DWORD cbBufNew)
  944. {
  945. HRESULT hr = S_OK;
  946. if (phmemBuf == NULL || pcbBufCur == NULL)
  947. return (E_POINTER);
  948. // Need to make sure we have a buffer big enough to hold what the caller
  949. // needs to store.
  950. if (cbBufNew > *pcbBufCur)
  951. {
  952. HGLOBAL hmemNew;
  953. if (*phmemBuf == NULL)
  954. hmemNew = GlobalAlloc(GMEM_MOVEABLE, cbBufNew);
  955. else
  956. hmemNew = GlobalReAlloc(*phmemBuf, cbBufNew, GMEM_MOVEABLE);
  957. if (hmemNew != NULL)
  958. {
  959. // Do reassignment just in case the new hmem is different
  960. // than the old or if we just allocated a buffer for the
  961. // first time.
  962. *phmemBuf = hmemNew;
  963. *pcbBufCur = cbBufNew;
  964. }
  965. else
  966. // A pre-existing *phmemBuf is still valid;
  967. // we'll free it in Close().
  968. hr = E_OUTOFMEMORY;
  969. }
  970. return (hr);
  971. }
  972. HRESULT
  973. CHHSysSort::ReallocBuffer(HGLOBAL* phmemBuf, DWORD* pcbBufCur, DWORD cbBufNew)
  974. {
  975. HRESULT hr = S_OK;
  976. m_cs.Lock();
  977. hr = ReallocBufferHmem(phmemBuf, pcbBufCur, max(cbBufNew, cbAnsiBufInit));
  978. m_cs.Unlock();
  979. return (hr);
  980. }
  981. void
  982. CHHSysSort::Close(void)
  983. {
  984. if (m_hmemAnsi1 != NULL)
  985. {
  986. GlobalFree(m_hmemAnsi1);
  987. m_hmemAnsi1 = NULL;
  988. m_cbBufAnsi1Cur = 0;
  989. }
  990. if (m_hmemAnsi2 != NULL)
  991. {
  992. GlobalFree(m_hmemAnsi2);
  993. m_hmemAnsi2 = NULL;
  994. m_cbBufAnsi2Cur = 0;
  995. }
  996. memset(&m_srtctl, NULL, sizeof(SRTCTL));
  997. m_fInitialized = m_fDirty = FALSE;
  998. }
  999. #pragma optimize( "", off )