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.

762 lines
21 KiB

  1. //
  2. // reconv.cpp
  3. //
  4. #include "private.h"
  5. #include "globals.h"
  6. #include "common.h"
  7. #include "korimx.h"
  8. #include "candlstx.h"
  9. #include "fnrecon.h"
  10. #include "funcprv.h"
  11. #include "helpers.h"
  12. #include "immxutil.h"
  13. #include "editcb.h"
  14. #include "hanja.h"
  15. #include "ucutil.h"
  16. //////////////////////////////////////////////////////////////////////////////
  17. //
  18. // CFunction
  19. //
  20. //////////////////////////////////////////////////////////////////////////////
  21. //+---------------------------------------------------------------------------
  22. //
  23. // ctor
  24. //
  25. //----------------------------------------------------------------------------
  26. CFunction::CFunction(CFunctionProvider *pFuncPrv)
  27. {
  28. _pFuncPrv = pFuncPrv;
  29. _pFuncPrv->AddRef();
  30. }
  31. //+---------------------------------------------------------------------------
  32. //
  33. // dtor
  34. //
  35. //----------------------------------------------------------------------------
  36. CFunction::~CFunction()
  37. {
  38. SafeRelease(_pFuncPrv);
  39. }
  40. #if 1
  41. //+---------------------------------------------------------------------------
  42. //
  43. // CFunction::GetTarget
  44. //
  45. //----------------------------------------------------------------------------
  46. HRESULT CFunction::GetTarget(TfEditCookie ec, ITfContext *pic, ITfRange *pRange, BOOL bAdjust, ITfRange **ppRangeTmp, WCHAR **ppszText, ULONG *pcch)
  47. {
  48. ITfProperty* pProp;
  49. ITfRange* pRangeTmp = NULL;
  50. // init
  51. *pcch = 0;
  52. // AIMM?
  53. if (CKorIMX::GetAIMM(pic))
  54. {
  55. // Allocate just one char string buffer
  56. *ppszText = new WCHAR[2];
  57. Assert(*ppszText != NULL);
  58. if (*ppszText == NULL)
  59. return E_OUTOFMEMORY;
  60. pRange->Clone(&pRangeTmp);
  61. *pcch = 1;
  62. pRangeTmp->GetText(ec, 0, *ppszText, sizeof(WCHAR), pcch);
  63. *((*ppszText) + 1) = L'\0';
  64. *ppRangeTmp = pRangeTmp;
  65. return S_OK;
  66. }
  67. // if reading prop exist.
  68. if (SUCCEEDED(pic->GetProperty(GUID_PROP_READING, &pProp)))
  69. {
  70. ITfRange *pPropRange;
  71. HRESULT hr = pProp->FindRange(ec, pRange, &pPropRange, TF_ANCHOR_START);
  72. if (SUCCEEDED(hr) && pPropRange)
  73. {
  74. BSTR bstr;
  75. if (SUCCEEDED(GetBSTRPropertyData(ec, pProp, pPropRange, &bstr)))
  76. {
  77. pPropRange->Clone(&pRangeTmp);
  78. if (bAdjust || CompareRanges(ec, pRange, pRangeTmp) == CR_EQUAL)
  79. {
  80. *pcch = SysStringLen(bstr);
  81. *ppszText = new WCHAR[*pcch + 1];
  82. if (*ppszText)
  83. StringCchCopyW(*ppszText, *pcch + 1, bstr);
  84. }
  85. }
  86. SysFreeString(bstr);
  87. pPropRange->Release();
  88. }
  89. pProp->Release();
  90. }
  91. // If no reading property
  92. if (!(*ppszText))
  93. {
  94. LONG cch;
  95. BOOL fEmpty;
  96. pRange->IsEmpty(ec, &fEmpty);
  97. pRange->Clone(&pRangeTmp);
  98. // Select only one char
  99. if (!fEmpty)
  100. {
  101. pRangeTmp->Collapse(ec, TF_ANCHOR_START);
  102. pRangeTmp->ShiftEnd(ec, 1, &cch, NULL);
  103. }
  104. else
  105. {
  106. pRangeTmp->ShiftEnd(ec, 1, &cch, NULL);
  107. if (cch==0)
  108. pRangeTmp->ShiftStart(ec, -1, &cch, NULL);
  109. }
  110. Assert(cch != 0);
  111. if (cch)
  112. {
  113. // Allocate just one char string buffer
  114. *ppszText = new WCHAR[2];
  115. Assert(*ppszText != NULL);
  116. if (*ppszText == NULL)
  117. return E_OUTOFMEMORY;
  118. *pcch = 1;
  119. pRangeTmp->GetText(ec, 0, *ppszText, sizeof(WCHAR), pcch);
  120. *((*ppszText) + 1) = L'\0';
  121. // Office #154974
  122. // If there is any embedded char exist, skip it forward.
  123. while (**ppszText == TS_CHAR_EMBEDDED)
  124. {
  125. pRangeTmp->ShiftStart(ec, 1, &cch, NULL);
  126. if (cch == 0)
  127. break;
  128. pRangeTmp->ShiftEnd(ec, 1, &cch, NULL);
  129. if (cch == 0)
  130. break;
  131. *pcch = 1;
  132. pRangeTmp->GetText(ec, 0, *ppszText, sizeof(WCHAR), pcch);
  133. *((*ppszText) + 1) = L'\0';
  134. }
  135. }
  136. }
  137. *ppRangeTmp = pRangeTmp;
  138. return S_OK;
  139. }
  140. #else
  141. //+---------------------------------------------------------------------------
  142. //
  143. // CFunction::GetTarget
  144. //
  145. //----------------------------------------------------------------------------
  146. HRESULT CFunction::GetTarget(TfEditCookie ec, ITfContext *pic, ITfRange *pRange, BOOL bAdjust, ITfRange **ppRangeTmp, WCHAR **ppszText, ULONG *pcch)
  147. {
  148. ITfRange *pRangeTmp = NULL;
  149. LONG cch;
  150. BOOL fEmpty;
  151. *pcch = 0;
  152. pRange->IsEmpty(ec, &fEmpty);
  153. if (!fEmpty)
  154. {
  155. pRange->Clone(&pRangeTmp);
  156. pRangeTmp->Collapse(ec, TF_ANCHOR_START);
  157. pRangeTmp->ShiftEnd(ec, 1, &cch, NULL);
  158. if (cch)
  159. {
  160. *ppszText = new WCHAR[2];
  161. *pcch = 1;
  162. pRangeTmp->GetText(ec, 0, *ppszText, sizeof(WCHAR), pcch);
  163. *((*ppszText) + 1) = L'\0';
  164. }
  165. }
  166. else
  167. {
  168. pRange->Clone(&pRangeTmp);
  169. pRangeTmp->ShiftEnd(ec, 1, &cch, NULL);
  170. if (cch)
  171. {
  172. *ppszText = new WCHAR[2];
  173. *pcch = 1;
  174. pRangeTmp->GetText(ec, 0, *ppszText, sizeof(WCHAR), pcch);
  175. *((*ppszText) + 1) = L'\0';
  176. }
  177. }
  178. *ppRangeTmp = pRangeTmp;
  179. return S_OK;
  180. }
  181. #endif
  182. //+---------------------------------------------------------------------------
  183. //
  184. // CFunction::GetFocusedTarget
  185. //
  186. //----------------------------------------------------------------------------
  187. BOOL CFunction::GetFocusedTarget(TfEditCookie ec, ITfContext *pic, ITfRange *pRange, BOOL bAdjust, ITfRange **ppRangeTmp)
  188. {
  189. ITfRange *pRangeTmp = NULL;
  190. ITfRange *pRangeTmp2 = NULL;
  191. IEnumTfRanges *pEnumTrack = NULL;
  192. ITfRange *pPropRange;
  193. ITfReadOnlyProperty *pProp = NULL;
  194. BOOL bRet = FALSE;
  195. BOOL fWholeDoc = FALSE;
  196. if (!pRange)
  197. {
  198. fWholeDoc = TRUE;
  199. if (FAILED(GetRangeForWholeDoc(ec, pic, &pRange)))
  200. return FALSE;
  201. }
  202. if (bAdjust)
  203. {
  204. //
  205. // multi owner and PF_FOCUS range support.
  206. //
  207. if (FAILED(AdjustRangeByTextOwner(ec, pic,
  208. pRange,
  209. &pRangeTmp2,
  210. CLSID_KorIMX)))
  211. goto Exit;
  212. GUID rgGuid[1];
  213. rgGuid[0] = GUID_ATTR_KORIMX_INPUT;
  214. if (FAILED(AdjustRangeByAttribute(_pFuncPrv->_pime->_GetLibTLS(),
  215. ec, pic,
  216. pRangeTmp2,
  217. &pRangeTmp,
  218. rgGuid, 1)))
  219. goto Exit;
  220. }
  221. else
  222. {
  223. pRange->Clone(&pRangeTmp);
  224. }
  225. //
  226. // check if there is an intersection of PF_FOCUS range and owned range.
  227. // if there is no such range, we return FALSE.
  228. //
  229. if (FAILED(EnumTrackTextAndFocus(ec, pic, pRangeTmp, &pProp, &pEnumTrack)))
  230. goto Exit;
  231. while(pEnumTrack->Next(1, &pPropRange, 0) == S_OK)
  232. {
  233. if (IsOwnerAndFocus(_pFuncPrv->_pime->_GetLibTLS(), ec, CLSID_KorIMX, pProp, pPropRange))
  234. bRet = TRUE;
  235. pPropRange->Release();
  236. }
  237. pProp->Release();
  238. if (bRet)
  239. {
  240. *ppRangeTmp = pRangeTmp;
  241. (*ppRangeTmp)->AddRef();
  242. }
  243. Exit:
  244. SafeRelease(pEnumTrack);
  245. SafeRelease(pRangeTmp);
  246. SafeRelease(pRangeTmp2);
  247. if (fWholeDoc)
  248. pRange->Release();
  249. return bRet;
  250. }
  251. //////////////////////////////////////////////////////////////////////////////
  252. //
  253. // CFnReconversion
  254. //
  255. //////////////////////////////////////////////////////////////////////////////
  256. //+---------------------------------------------------------------------------
  257. //
  258. // IUnknown
  259. //
  260. //----------------------------------------------------------------------------
  261. STDAPI CFnReconversion::QueryInterface(REFIID riid, void **ppvObj)
  262. {
  263. #if NEVER
  264. *ppvObj = NULL;
  265. if (IsEqualIID(riid, IID_IUnknown) ||
  266. IsEqualIID(riid, IID_ITfFnReconversion))
  267. {
  268. *ppvObj = SAFECAST(this, CFnReconversion *);
  269. }
  270. if (*ppvObj)
  271. {
  272. AddRef();
  273. return S_OK;
  274. }
  275. #endif
  276. return E_NOINTERFACE;
  277. }
  278. STDAPI_(ULONG) CFnReconversion::AddRef()
  279. {
  280. return InterlockedIncrement(&_cRef);
  281. }
  282. STDAPI_(ULONG) CFnReconversion::Release()
  283. {
  284. long cr;
  285. cr = InterlockedDecrement(&_cRef);
  286. Assert(cr >= 0);
  287. if (cr == 0)
  288. {
  289. delete this;
  290. }
  291. return cr;
  292. }
  293. //+---------------------------------------------------------------------------
  294. //
  295. // ctor
  296. //
  297. //----------------------------------------------------------------------------
  298. CFnReconversion::CFnReconversion(CKorIMX *pKorImx, CFunctionProvider *pFuncPrv) : CFunction(pFuncPrv)
  299. {
  300. m_pKorImx = pKorImx;
  301. _cRef = 1;
  302. }
  303. //+---------------------------------------------------------------------------
  304. //
  305. // dtor
  306. //
  307. //----------------------------------------------------------------------------
  308. CFnReconversion::~CFnReconversion()
  309. {
  310. }
  311. //+---------------------------------------------------------------------------
  312. //
  313. // CFnReconversion::GetDisplayName
  314. //
  315. //----------------------------------------------------------------------------
  316. STDAPI CFnReconversion::GetDisplayName(BSTR *pbstrName)
  317. {
  318. *pbstrName = SysAllocString(L"Hanja Conv");
  319. return S_OK;
  320. }
  321. //+---------------------------------------------------------------------------
  322. //
  323. // CFnReconversion::IsEnabled
  324. //
  325. //----------------------------------------------------------------------------
  326. STDAPI CFnReconversion::IsEnabled(BOOL *pfEnable)
  327. {
  328. *pfEnable = TRUE;
  329. return S_OK;
  330. }
  331. //+---------------------------------------------------------------------------
  332. //
  333. // CFnReconversion::QueryRange
  334. //
  335. //----------------------------------------------------------------------------
  336. STDAPI CFnReconversion::QueryRange(ITfRange *pRange, ITfRange **ppNewRange, BOOL *pfConvertable)
  337. {
  338. CEditSession2 *pes;
  339. ITfContext *pic;
  340. ESSTRUCT ess;
  341. HRESULT hr = E_OUTOFMEMORY;
  342. if (!pRange || !ppNewRange || !pfConvertable)
  343. return E_INVALIDARG;
  344. if (FAILED(pRange->GetContext(&pic)))
  345. goto Exit;
  346. ESStructInit(&ess, ESCB_RECONV_QUERYRECONV);
  347. ess.pRange = pRange;
  348. ess.pv1 = this;
  349. ess.pv2 = ppNewRange;
  350. if ((pes = new CEditSession2(pic, m_pKorImx, &ess, CKorIMX::_EditSessionCallback2)) != NULL)
  351. {
  352. pes->Invoke(ES2_READONLY | ES2_SYNC, &hr);
  353. pes->Release();
  354. }
  355. *pfConvertable = (hr == S_OK);
  356. if (hr == S_FALSE)
  357. hr = S_OK;
  358. pic->Release();
  359. Exit:
  360. return hr;
  361. }
  362. //+---------------------------------------------------------------------------
  363. //
  364. // CFnReconversion::_QueryRange
  365. //
  366. //----------------------------------------------------------------------------
  367. HRESULT CFnReconversion::_QueryRange(TfEditCookie ec, ITfContext *pic, ITfRange *pRange, ITfRange **ppNewRange)
  368. {
  369. ULONG cch = 0;
  370. WCHAR *pszText = NULL;
  371. HRESULT hr = E_FAIL;
  372. ITfRange *pRangeTmp = NULL;
  373. //
  374. // KIMX doesn't support entire document reconversion.
  375. //
  376. if (!pRange)
  377. return hr;
  378. GetTarget(ec, pic, pRange, ppNewRange ? TRUE : FALSE, &pRangeTmp, &pszText, &cch);
  379. if (cch)
  380. {
  381. if (ppNewRange)
  382. pRangeTmp->Clone(ppNewRange);
  383. hr = S_OK;
  384. // In case of AIMM we should return error if the input char can't be coverted.
  385. if (CKorIMX::GetAIMM(pic))
  386. {
  387. HANJA_CAND_STRING_LIST CandStrList;
  388. if (GetConversionList(*pszText, &CandStrList))
  389. {
  390. // free buffer and return
  391. cicMemFree(CandStrList.pwsz);
  392. cicMemFree(CandStrList.pHanjaString);
  393. }
  394. else
  395. hr = S_FALSE;
  396. }
  397. }
  398. else
  399. hr = S_FALSE;
  400. if (pszText)
  401. delete pszText;
  402. SafeRelease(pRangeTmp);
  403. return hr;
  404. }
  405. //+---------------------------------------------------------------------------
  406. //
  407. // CFnReconversion::GetReconversion
  408. //
  409. //----------------------------------------------------------------------------
  410. STDAPI CFnReconversion::GetReconversion(ITfRange *pRange, ITfCandidateList **ppCandList)
  411. {
  412. ITfContext *pic;
  413. CCandidateListEx *pCandList;
  414. HRESULT hr;
  415. if (!pRange || !ppCandList)
  416. return E_INVALIDARG;
  417. if (FAILED(pRange->GetContext(&pic)))
  418. return E_FAIL;
  419. hr = GetReconversionProc(pic, pRange, &pCandList, fFalse);
  420. if (pCandList != NULL)
  421. {
  422. pCandList->QueryInterface( IID_ITfCandidateList, (void**)ppCandList );
  423. pCandList->Release();
  424. }
  425. pic->Release();
  426. return hr;
  427. }
  428. //+---------------------------------------------------------------------------
  429. //
  430. // CFnReconversion::_GetReconversion
  431. //
  432. //----------------------------------------------------------------------------
  433. HRESULT CFnReconversion::_GetReconversion(TfEditCookie ec, ITfContext *pic, ITfRange *pRange, CCandidateListEx **ppCandList, BOOL fSelection)
  434. {
  435. ULONG cch = 0;
  436. WCHAR *pszReading = NULL;
  437. HRESULT hr = E_FAIL;
  438. ITfRange *pRangeTmp = NULL;
  439. CCandidateStringEx *pCandExtraStr;
  440. GetTarget(ec, pic, pRange, TRUE, &pRangeTmp, &pszReading, &cch);
  441. if (cch)
  442. {
  443. CCandidateListEx *pCandList;
  444. HANJA_CAND_STRING_LIST CandStrList;
  445. WCHAR szCand[2];
  446. WCHAR wch = 0;
  447. ULONG cch;
  448. // build candidate list
  449. pCandList = new CCandidateListEx(SetResult, pic, pRangeTmp);
  450. Assert(pCandList != NULL);
  451. if (pCandList == NULL)
  452. return E_OUTOFMEMORY;
  453. // Copy reading string
  454. StringCchCopyW(_szReading, ARRAYSIZE(_szReading), pszReading);
  455. // Get conv list from Hanja dict
  456. if (GetConversionList(*pszReading, &CandStrList))
  457. {
  458. // If AIMM, don't select coversion char.
  459. if (!CKorIMX::GetAIMM(pic))
  460. {
  461. // If there candidate exist, Set selection converting char
  462. if (fSelection)
  463. SetSelectionSimple(ec, pic, pRangeTmp);
  464. // if it is Hanja already converted, Add Hangul pronoun as extra cand str.
  465. pRangeTmp->GetText(ec, 0, &wch, sizeof(WCHAR), &cch);
  466. if (cch && !fIsHangul(wch))
  467. {
  468. pCandList->AddExtraString(pszReading, MAKELANGID(LANG_KOREAN, SUBLANG_DEFAULT), NULL, this, &pCandExtraStr);
  469. pCandExtraStr->Release();
  470. }
  471. }
  472. for (UINT i=0; i<CandStrList.csz; i++)
  473. {
  474. //LANGID langid = GetLangIdFromCand(pszReading, pchCand);
  475. CCandidateStringEx *pCandStr;
  476. szCand[0] = CandStrList.pHanjaString[i].wchHanja;
  477. szCand[1] = L'\0';
  478. pCandList->AddString(szCand,
  479. MAKELANGID(LANG_KOREAN, SUBLANG_DEFAULT),
  480. NULL, this, &pCandStr);
  481. pCandStr->SetInlineComment(CandStrList.pHanjaString[i].wzMeaning);
  482. pCandStr->m_bHanjaCat = CandStrList.pHanjaString[i].bHanjaCat;
  483. pCandStr->SetReadingString(_szReading);
  484. pCandStr->Release();
  485. }
  486. // free buffer and return
  487. cicMemFree(CandStrList.pwsz);
  488. cicMemFree(CandStrList.pHanjaString);
  489. *ppCandList = pCandList;
  490. hr = S_OK;
  491. }
  492. }
  493. if (pszReading)
  494. delete pszReading;
  495. SafeRelease(pRangeTmp);
  496. return hr;
  497. }
  498. //+---------------------------------------------------------------------------
  499. //
  500. // CFnReconversion::Reconvert
  501. //
  502. //----------------------------------------------------------------------------
  503. HRESULT CFnReconversion::Reconvert(ITfRange *pRange)
  504. {
  505. CCandidateListEx *pCandList = NULL;
  506. ITfRange *pRangeTmp = NULL;
  507. ITfContext *pic;
  508. HRESULT hr;
  509. if (!pRange)
  510. return E_INVALIDARG;
  511. hr = E_FAIL;
  512. if (FAILED(pRange->Clone(&pRangeTmp)))
  513. goto Exit;
  514. if (FAILED(pRange->GetContext(&pic)))
  515. goto Exit;
  516. if (SUCCEEDED(hr = GetReconversionProc(pic, pRange, &pCandList, fTrue)))
  517. {
  518. hr = ShowCandidateList(pic, pRange, pCandList);
  519. SafeRelease(pCandList);
  520. }
  521. SafeRelease(pRangeTmp);
  522. SafeRelease(pic);
  523. Exit:
  524. return hr;
  525. }
  526. /* G E T R E C O N V E R S I O N P R O C */
  527. /*------------------------------------------------------------------------------
  528. Get candidate list of reconversion
  529. ------------------------------------------------------------------------------*/
  530. HRESULT CFnReconversion::GetReconversionProc(ITfContext *pic, ITfRange *pRange, CCandidateListEx **ppCandList, BOOL fSelection)
  531. {
  532. CEditSession2 *pes;
  533. ESSTRUCT ess;
  534. HRESULT hr;
  535. if (!ppCandList)
  536. return E_INVALIDARG;
  537. *ppCandList = NULL;
  538. ESStructInit(&ess, ESCB_RECONV_GETRECONV);
  539. ess.pRange = pRange;
  540. ess.pv1 = this;
  541. ess.pv2 = ppCandList;
  542. ess.fBool = fSelection;
  543. hr = E_OUTOFMEMORY;
  544. if ((pes = new CEditSession2(pic, m_pKorImx, &ess, CKorIMX::_EditSessionCallback2)))
  545. {
  546. if (fSelection)
  547. pes->Invoke(ES2_READWRITE | ES2_SYNC, &hr);
  548. else
  549. pes->Invoke(ES2_READONLY | ES2_SYNC, &hr);
  550. pes->Release();
  551. }
  552. return hr;
  553. }
  554. //+---------------------------------------------------------------------------
  555. //
  556. // CFnReconversion::ShowCandidateList
  557. //
  558. //----------------------------------------------------------------------------
  559. HRESULT CFnReconversion::ShowCandidateList(ITfContext *pic, ITfRange *pRange, CCandidateListEx *pCandList)
  560. {
  561. CEditSession2 *pes;
  562. ESSTRUCT ess;
  563. HRESULT hr;
  564. hr = E_OUTOFMEMORY;
  565. ESStructInit(&ess, ESCB_RECONV_SHOWCAND);
  566. ess.pRange = pRange;
  567. ess.pv1 = this;
  568. ess.pCandList = pCandList;
  569. if ((pes = new CEditSession2(pic, m_pKorImx, &ess, CKorIMX::_EditSessionCallback2)))
  570. {
  571. pes->Invoke(ES2_READWRITE | ES2_SYNC, &hr);
  572. pes->Release();
  573. }
  574. return hr;
  575. }
  576. //+---------------------------------------------------------------------------
  577. //
  578. // CFnReconversion::SetResult
  579. // (Static function)
  580. //
  581. //----------------------------------------------------------------------------
  582. HRESULT CFnReconversion::SetResult(ITfContext *pic, ITfRange *pRange, CCandidateListEx *pCandList, CCandidateStringEx *pCand, TfCandidateResult imcr)
  583. {
  584. CEditSession2 *pes;
  585. ESSTRUCT ess;
  586. CFnReconversion *pReconv = (CFnReconversion *)(pCand->m_punk);
  587. ITfRange *pRangeTmp;
  588. HRESULT hr;
  589. hr = E_OUTOFMEMORY;
  590. if (SUCCEEDED(pRange->Clone(&pRangeTmp)))
  591. {
  592. if (imcr == CAND_FINALIZED)
  593. {
  594. ESStructInit(&ess, ESCB_FINALIZERECONVERSION);
  595. ess.pCandList = pCandList;
  596. ess.pCandStr = pCand;
  597. //pCandList->AddRef(); // be released in edit session callback
  598. //pCand->AddRef();
  599. }
  600. else
  601. if (imcr == CAND_SELECTED)
  602. ESStructInit(&ess, ESCB_ONSELECTRECONVERSION);
  603. else
  604. if (imcr == CAND_CANCELED)
  605. ESStructInit(&ess, ESCB_ONCANCELRECONVERSION);
  606. // Save useful parameters
  607. ess.pv1 = pReconv;
  608. ess.lParam = pReconv->_pFuncPrv->_pime->GetTID();
  609. ess.pRange = pRangeTmp;
  610. if ((pes = new CEditSession2(pic, pReconv->m_pKorImx, &ess, CKorIMX::_EditSessionCallback2)))
  611. {
  612. pes->Invoke(ES2_READWRITE | ES2_ASYNC, &hr);
  613. pes->Release();
  614. }
  615. // Call back function must release pRangeTmp
  616. // pRangeTmp->Release();
  617. }
  618. return S_OK;
  619. }