xref: /reactos/dll/ime/msctfime/msctfime.cpp (revision 4514e91d)
1 /*
2  * PROJECT:     ReactOS msctfime.ime
3  * LICENSE:     LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
4  * PURPOSE:     Supporting IME interface of Text Input Processors (TIPs)
5  * COPYRIGHT:   Copyright 2023 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
6  */
7 
8 #include "msctfime.h"
9 
10 WINE_DEFAULT_DEBUG_CHANNEL(msctfime);
11 
12 HINSTANCE g_hInst = NULL; /* The instance of this module */
13 BOOL g_bWinLogon = FALSE;
14 DWORD g_dwOSInfo = 0;
15 BOOL gfTFInitLib = FALSE;
16 CRITICAL_SECTION g_csLock;
17 
18 DEFINE_GUID(GUID_COMPARTMENT_CTFIME_DIMFLAGS, 0xA94C5FD2, 0xC471, 0x4031, 0x95, 0x46, 0x70, 0x9C, 0x17, 0x30, 0x0C, 0xB9);
19 
20 EXTERN_C void __cxa_pure_virtual(void)
21 {
22     ERR("__cxa_pure_virtual\n");
23 }
24 
25 UINT WM_MSIME_SERVICE = 0;
26 UINT WM_MSIME_UIREADY = 0;
27 UINT WM_MSIME_RECONVERTREQUEST = 0;
28 UINT WM_MSIME_RECONVERT = 0;
29 UINT WM_MSIME_DOCUMENTFEED = 0;
30 UINT WM_MSIME_QUERYPOSITION = 0;
31 UINT WM_MSIME_MODEBIAS = 0;
32 UINT WM_MSIME_SHOWIMEPAD = 0;
33 UINT WM_MSIME_MOUSE = 0;
34 UINT WM_MSIME_KEYMAP = 0;
35 
36 /**
37  * @implemented
38  */
39 BOOL IsMsImeMessage(UINT uMsg)
40 {
41     return (uMsg == WM_MSIME_SERVICE ||
42             uMsg == WM_MSIME_UIREADY ||
43             uMsg == WM_MSIME_RECONVERTREQUEST ||
44             uMsg == WM_MSIME_RECONVERT ||
45             uMsg == WM_MSIME_DOCUMENTFEED ||
46             uMsg == WM_MSIME_QUERYPOSITION ||
47             uMsg == WM_MSIME_MODEBIAS ||
48             uMsg == WM_MSIME_SHOWIMEPAD ||
49             uMsg == WM_MSIME_MOUSE ||
50             uMsg == WM_MSIME_KEYMAP);
51 }
52 
53 /**
54  * @implemented
55  */
56 BOOL RegisterMSIMEMessage(VOID)
57 {
58     WM_MSIME_SERVICE = RegisterWindowMessageW(L"MSIMEService");
59     WM_MSIME_UIREADY = RegisterWindowMessageW(L"MSIMEUIReady");
60     WM_MSIME_RECONVERTREQUEST = RegisterWindowMessageW(L"MSIMEReconvertRequest");
61     WM_MSIME_RECONVERT = RegisterWindowMessageW(L"MSIMEReconvert");
62     WM_MSIME_DOCUMENTFEED = RegisterWindowMessageW(L"MSIMEDocumentFeed");
63     WM_MSIME_QUERYPOSITION = RegisterWindowMessageW(L"MSIMEQueryPosition");
64     WM_MSIME_MODEBIAS = RegisterWindowMessageW(L"MSIMEModeBias");
65     WM_MSIME_SHOWIMEPAD = RegisterWindowMessageW(L"MSIMEShowImePad");
66     WM_MSIME_MOUSE = RegisterWindowMessageW(L"MSIMEMouseOperation");
67     WM_MSIME_KEYMAP = RegisterWindowMessageW(L"MSIMEKeyMap");
68     return (WM_MSIME_SERVICE &&
69             WM_MSIME_UIREADY &&
70             WM_MSIME_RECONVERTREQUEST &&
71             WM_MSIME_RECONVERT &&
72             WM_MSIME_DOCUMENTFEED &&
73             WM_MSIME_QUERYPOSITION &&
74             WM_MSIME_MODEBIAS &&
75             WM_MSIME_SHOWIMEPAD &&
76             WM_MSIME_MOUSE &&
77             WM_MSIME_KEYMAP);
78 }
79 
80 typedef BOOLEAN (WINAPI *FN_DllShutDownInProgress)(VOID);
81 
82 EXTERN_C BOOLEAN WINAPI
83 DllShutDownInProgress(VOID)
84 {
85     HMODULE hNTDLL;
86     static FN_DllShutDownInProgress s_fnDllShutDownInProgress = NULL;
87 
88     if (s_fnDllShutDownInProgress)
89         return s_fnDllShutDownInProgress();
90 
91     hNTDLL = cicGetSystemModuleHandle(L"ntdll.dll", FALSE);
92     s_fnDllShutDownInProgress =
93         (FN_DllShutDownInProgress)GetProcAddress(hNTDLL, "RtlDllShutdownInProgress");
94     if (!s_fnDllShutDownInProgress)
95         return FALSE;
96 
97     return s_fnDllShutDownInProgress();
98 }
99 
100 static BOOL
101 IsInteractiveUserLogon(VOID)
102 {
103     BOOL bOK, IsMember = FALSE;
104     PSID pSid;
105     SID_IDENTIFIER_AUTHORITY IdentAuth = { SECURITY_NT_AUTHORITY };
106 
107     if (!AllocateAndInitializeSid(&IdentAuth, 1, SECURITY_INTERACTIVE_RID,
108                                   0, 0, 0, 0, 0, 0, 0, &pSid))
109     {
110         ERR("Error: %ld\n", GetLastError());
111         return FALSE;
112     }
113 
114     bOK = CheckTokenMembership(NULL, pSid, &IsMember);
115 
116     if (pSid)
117         FreeSid(pSid);
118 
119     return bOK && IsMember;
120 }
121 
122 typedef struct LIBTHREAD
123 {
124     IUnknown *m_pUnknown1;
125     ITfDisplayAttributeMgr *m_pDisplayAttrMgr;
126 } LIBTHREAD, *PLIBTHREAD;
127 
128 HRESULT InitDisplayAttrbuteLib(PLIBTHREAD pLibThread)
129 {
130     if (!pLibThread)
131         return E_FAIL;
132 
133     if (pLibThread->m_pDisplayAttrMgr)
134     {
135         pLibThread->m_pDisplayAttrMgr->Release();
136         pLibThread->m_pDisplayAttrMgr = NULL;
137     }
138 
139     //FIXME
140     return E_NOTIMPL;
141 }
142 
143 HRESULT UninitDisplayAttrbuteLib(PLIBTHREAD pLibThread)
144 {
145     if (!pLibThread)
146         return E_FAIL;
147 
148     if (pLibThread->m_pDisplayAttrMgr)
149     {
150         pLibThread->m_pDisplayAttrMgr->Release();
151         pLibThread->m_pDisplayAttrMgr = NULL;
152     }
153 
154     return S_OK;
155 }
156 
157 void TFUninitLib_Thread(PLIBTHREAD pLibThread)
158 {
159     if (!pLibThread)
160         return;
161 
162     if (pLibThread->m_pUnknown1)
163     {
164         pLibThread->m_pUnknown1->Release();
165         pLibThread->m_pUnknown1 = NULL;
166     }
167     if (pLibThread->m_pDisplayAttrMgr)
168     {
169         pLibThread->m_pDisplayAttrMgr->Release();
170         pLibThread->m_pDisplayAttrMgr = NULL;
171     }
172 }
173 
174 /***********************************************************************
175  *      Compartment
176  */
177 
178 /**
179  * @implemented
180  */
181 HRESULT
182 GetCompartment(
183     IUnknown *pUnknown,
184     REFGUID rguid,
185     ITfCompartment **ppComp,
186     BOOL bThread)
187 {
188     *ppComp = NULL;
189 
190     ITfThreadMgr *pThreadMgr = NULL;
191     ITfCompartmentMgr *pCompMgr = NULL;
192 
193     HRESULT hr;
194     if (bThread)
195     {
196         hr = pUnknown->QueryInterface(IID_ITfThreadMgr, (void **)&pThreadMgr);
197         if (FAILED(hr))
198             return hr;
199 
200         hr = pThreadMgr->GetGlobalCompartment(&pCompMgr);
201     }
202     else
203     {
204         hr = pUnknown->QueryInterface(IID_ITfCompartmentMgr, (void **)&pCompMgr);
205     }
206 
207     if (SUCCEEDED(hr))
208     {
209         hr = E_FAIL;
210         if (pCompMgr)
211         {
212             hr = pCompMgr->GetCompartment(rguid, ppComp);
213             pCompMgr->Release();
214         }
215     }
216 
217     if (pThreadMgr)
218         pThreadMgr->Release();
219 
220     return hr;
221 }
222 
223 /**
224  * @implemented
225  */
226 HRESULT
227 SetCompartmentDWORD(
228     TfEditCookie cookie,
229     IUnknown *pUnknown,
230     REFGUID rguid,
231     DWORD dwValue,
232     BOOL bThread)
233 {
234     ITfCompartment *pComp = NULL;
235     HRESULT hr = GetCompartment(pUnknown, rguid, &pComp, bThread);
236     if (FAILED(hr))
237         return hr;
238 
239     VARIANT vari;
240     V_I4(&vari) = dwValue;
241     V_VT(&vari) = VT_I4;
242     hr = pComp->SetValue(cookie, &vari);
243 
244     pComp->Release();
245     return hr;
246 }
247 
248 /**
249  * @implemented
250  */
251 HRESULT
252 GetCompartmentDWORD(
253     IUnknown *pUnknown,
254     REFGUID rguid,
255     LPDWORD pdwValue,
256     BOOL bThread)
257 {
258     *pdwValue = 0;
259 
260     ITfCompartment *pComp = NULL;
261     HRESULT hr = GetCompartment(pUnknown, rguid, &pComp, bThread);
262     if (FAILED(hr))
263         return hr;
264 
265     VARIANT vari;
266     hr = pComp->GetValue(&vari);
267     if (hr == S_OK)
268         *pdwValue = V_I4(&vari);
269 
270     pComp->Release();
271     return hr;
272 }
273 
274 /**
275  * @implemented
276  */
277 HRESULT
278 SetCompartmentUnknown(
279     TfEditCookie cookie,
280     IUnknown *pUnknown,
281     REFGUID rguid,
282     IUnknown *punkValue)
283 {
284     ITfCompartment *pComp = NULL;
285     HRESULT hr = GetCompartment(pUnknown, rguid, &pComp, FALSE);
286     if (FAILED(hr))
287         return hr;
288 
289     VARIANT vari;
290     V_UNKNOWN(&vari) = punkValue;
291     V_VT(&vari) = VT_UNKNOWN;
292     hr = pComp->SetValue(cookie, &vari);
293 
294     pComp->Release();
295     return hr;
296 }
297 
298 /**
299  * @implemented
300  */
301 HRESULT
302 ClearCompartment(
303     TfClientId tid,
304     IUnknown *pUnknown,
305     REFGUID rguid,
306     BOOL bThread)
307 {
308     ITfCompartmentMgr *pCompMgr = NULL;
309     ITfThreadMgr *pThreadMgr = NULL;
310 
311     HRESULT hr;
312     if (bThread)
313     {
314         hr = pUnknown->QueryInterface(IID_ITfThreadMgr, (void **)&pThreadMgr);
315         if (FAILED(hr))
316             return hr;
317 
318         hr = pThreadMgr->GetGlobalCompartment(&pCompMgr);
319     }
320     else
321     {
322         hr = pUnknown->QueryInterface(IID_ITfCompartmentMgr, (void **)&pCompMgr);
323     }
324 
325     if (SUCCEEDED(hr))
326     {
327         hr = E_FAIL;
328         if (pCompMgr)
329         {
330             hr = pCompMgr->ClearCompartment(tid, rguid);
331             pCompMgr->Release();
332         }
333     }
334 
335     if (pThreadMgr)
336         pThreadMgr->Release();
337 
338     return hr;
339 }
340 
341 typedef struct CESMAP
342 {
343     ITfCompartment *m_pComp;
344     DWORD m_dwCookie;
345 } CESMAP, *PCESMAP;
346 
347 typedef INT (CALLBACK *FN_EVENTSINK)(LPVOID, REFGUID);
348 
349 class CCompartmentEventSink : public ITfCompartmentEventSink
350 {
351     CicArray<CESMAP> m_array;
352     LONG m_cRefs;
353     FN_EVENTSINK m_fnEventSink;
354     LPVOID m_pUserData;
355 
356 public:
357     CCompartmentEventSink(FN_EVENTSINK fnEventSink, LPVOID pUserData);
358     virtual ~CCompartmentEventSink();
359 
360     HRESULT _Advise(IUnknown *pUnknown, REFGUID rguid, BOOL bThread);
361     HRESULT _Unadvise();
362 
363     // IUnknown interface
364     STDMETHODIMP QueryInterface(REFIID riid, LPVOID* ppvObj) override;
365     STDMETHODIMP_(ULONG) AddRef() override;
366     STDMETHODIMP_(ULONG) Release() override;
367 
368     // ITfCompartmentEventSink interface
369     STDMETHODIMP OnChange(REFGUID rguid) override;
370 };
371 
372 /**
373  * @implemented
374  */
375 CCompartmentEventSink::CCompartmentEventSink(FN_EVENTSINK fnEventSink, LPVOID pUserData)
376     : m_array()
377     , m_cRefs(1)
378     , m_fnEventSink(fnEventSink)
379     , m_pUserData(pUserData)
380 {
381 }
382 
383 /**
384  * @implemented
385  */
386 CCompartmentEventSink::~CCompartmentEventSink()
387 {
388 }
389 
390 /**
391  * @implemented
392  */
393 STDMETHODIMP CCompartmentEventSink::QueryInterface(REFIID riid, LPVOID* ppvObj)
394 {
395     if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ITfCompartmentEventSink))
396     {
397         *ppvObj = this;
398         AddRef();
399         return S_OK;
400     }
401 
402     *ppvObj = NULL;
403     return E_NOINTERFACE;
404 }
405 
406 /**
407  * @implemented
408  */
409 STDMETHODIMP_(ULONG) CCompartmentEventSink::AddRef()
410 {
411     return ::InterlockedIncrement(&m_cRefs);
412 }
413 
414 /**
415  * @implemented
416  */
417 STDMETHODIMP_(ULONG) CCompartmentEventSink::Release()
418 {
419     if (::InterlockedDecrement(&m_cRefs) == 0)
420     {
421         delete this;
422         return 0;
423     }
424     return m_cRefs;
425 }
426 
427 /**
428  * @implemented
429  */
430 STDMETHODIMP CCompartmentEventSink::OnChange(REFGUID rguid)
431 {
432     return m_fnEventSink(m_pUserData, rguid);
433 }
434 
435 /**
436  * @implemented
437  */
438 HRESULT
439 CCompartmentEventSink::_Advise(IUnknown *pUnknown, REFGUID rguid, BOOL bThread)
440 {
441     CESMAP *pCesMap = m_array.Append(1);
442     if (!pCesMap)
443         return E_OUTOFMEMORY;
444 
445     ITfSource *pSource = NULL;
446 
447     HRESULT hr = GetCompartment(pUnknown, rguid, &pCesMap->m_pComp, bThread);
448     if (FAILED(hr))
449     {
450         hr = pCesMap->m_pComp->QueryInterface(IID_ITfSource, (void **)&pSource);
451         if (FAILED(hr))
452         {
453             hr = pSource->AdviseSink(IID_ITfCompartmentEventSink, this, &pCesMap->m_dwCookie);
454             if (FAILED(hr))
455             {
456                 if (pCesMap->m_pComp)
457                 {
458                     pCesMap->m_pComp->Release();
459                     pCesMap->m_pComp = NULL;
460                 }
461                 m_array.Remove(m_array.size() - 1, 1);
462             }
463             else
464             {
465                 hr = S_OK;
466             }
467         }
468     }
469 
470     if (pSource)
471         pSource->Release();
472 
473     return hr;
474 }
475 
476 /**
477  * @implemented
478  */
479 HRESULT CCompartmentEventSink::_Unadvise()
480 {
481     CESMAP *pCesMap = m_array.data();
482     size_t cItems = m_array.size();
483     if (!cItems)
484         return S_OK;
485 
486     do
487     {
488         ITfSource *pSource = NULL;
489         HRESULT hr = pCesMap->m_pComp->QueryInterface(IID_ITfSource, (void **)&pSource);
490         if (SUCCEEDED(hr))
491             pSource->UnadviseSink(pCesMap->m_dwCookie);
492 
493         if (pCesMap->m_pComp)
494         {
495             pCesMap->m_pComp->Release();
496             pCesMap->m_pComp = NULL;
497         }
498 
499         if (pSource)
500             pSource->Release();
501 
502         ++pCesMap;
503         --cItems;
504     } while (cItems);
505 
506     return S_OK;
507 }
508 
509 /***********************************************************************
510  *      CicInputContext
511  */
512 
513 class CInputContextOwnerCallBack;
514 
515 /* FIXME */
516 class CicInputContext
517     : public ITfCleanupContextSink
518     , public ITfContextOwnerCompositionSink
519     , public ITfCompositionSink
520 {
521 public:
522     DWORD m_dw[2];
523     LONG m_cRefs;
524     HIMC m_hIMC;
525     ITfDocumentMgr *m_pDocumentMgr;
526     ITfContext *m_pContext;
527     DWORD m_dw0_0[1];
528     CInputContextOwnerCallBack *m_pICOwnerCallback;
529     DWORD m_dw0;
530     CCompartmentEventSink *m_pCompEventSink1;
531     CCompartmentEventSink *m_pCompEventSink2;
532     DWORD m_dw0_5[4];
533     DWORD m_dw1[2];
534     DWORD m_dwQueryPos;
535     DWORD m_dw1_5[1];
536     GUID m_guid;
537     DWORD m_dw2[19];
538     WORD m_cGuidAtoms;
539     WORD m_padding;
540     DWORD m_adwGuidAtoms[256];
541     DWORD m_dw3[19];
542 
543 public:
544     CicInputContext(TfClientId cliendId, LIBTHREAD *pLibThread, HIMC hIMC);
545     virtual ~CicInputContext()
546     {
547     }
548 
549     // IUnknown interface
550     STDMETHODIMP QueryInterface(REFIID riid, LPVOID* ppvObj) override;
551     STDMETHODIMP_(ULONG) AddRef() override;
552     STDMETHODIMP_(ULONG) Release() override;
553 
554     // ITfCleanupContextSink interface
555     STDMETHODIMP OnCleanupContext(TfEditCookie ecWrite, ITfContext *pic) override;
556 
557     // ITfContextOwnerCompositionSink interface
558     STDMETHODIMP OnStartComposition(ITfCompositionView *pComposition, BOOL *pfOk) override;
559     STDMETHODIMP OnUpdateComposition(ITfCompositionView *pComposition, ITfRange *pRangeNew) override;
560     STDMETHODIMP OnEndComposition(ITfCompositionView *pComposition) override;
561 
562     // ITfCompositionSink interface
563     STDMETHODIMP OnCompositionTerminated(TfEditCookie ecWrite, ITfComposition *pComposition) override;
564 
565     HRESULT
566     GetGuidAtom(
567         _Inout_ IMCLock& imcLock,
568         _In_ BYTE iAtom,
569         _Out_opt_ LPDWORD pdwGuidAtom);
570 
571     HRESULT CreateInputContext(ITfThreadMgr *pThreadMgr, IMCLock& imcLock);
572     HRESULT DestroyInputContext();
573 };
574 
575 /**
576  * @unimplemented
577  */
578 CicInputContext::CicInputContext(TfClientId cliendId, LIBTHREAD *pLibThread, HIMC hIMC)
579 {
580     m_hIMC = hIMC;
581     m_guid = GUID_NULL;
582     m_dwQueryPos = 0;
583     m_cRefs = 1;
584 }
585 
586 /**
587  * @unimplemented
588  */
589 STDMETHODIMP CicInputContext::QueryInterface(REFIID riid, LPVOID* ppvObj)
590 {
591     *ppvObj = NULL;
592 
593     if (IsEqualIID(riid, IID_ITfContextOwnerCompositionSink))
594     {
595         *ppvObj = (ITfContextOwnerCompositionSink*)this;
596         AddRef();
597         return S_OK;
598     }
599     if (IsEqualIID(riid, IID_IUnknown))
600     {
601         *ppvObj = this;
602         AddRef();
603         return S_OK;
604     }
605 
606     return E_NOINTERFACE;
607 }
608 
609 /**
610  * @implemented
611  */
612 STDMETHODIMP_(ULONG) CicInputContext::AddRef()
613 {
614     return ::InterlockedIncrement(&m_cRefs);
615 }
616 
617 /**
618  * @implemented
619  */
620 STDMETHODIMP_(ULONG) CicInputContext::Release()
621 {
622     if (::InterlockedDecrement(&m_cRefs) == 0)
623     {
624         delete this;
625         return 0;
626     }
627     return m_cRefs;
628 }
629 
630 /**
631  * @unimplemented
632  */
633 STDMETHODIMP
634 CicInputContext::OnStartComposition(
635     ITfCompositionView *pComposition,
636     BOOL *pfOk)
637 {
638     return E_NOTIMPL;
639 }
640 
641 /**
642  * @unimplemented
643  */
644 STDMETHODIMP
645 CicInputContext::OnUpdateComposition(
646     ITfCompositionView *pComposition,
647     ITfRange *pRangeNew)
648 {
649     return E_NOTIMPL;
650 }
651 
652 /**
653  * @unimplemented
654  */
655 STDMETHODIMP
656 CicInputContext::OnEndComposition(
657     ITfCompositionView *pComposition)
658 {
659     return E_NOTIMPL;
660 }
661 
662 /**
663  * @implemented
664  */
665 HRESULT
666 CicInputContext::GetGuidAtom(
667     _Inout_ IMCLock& imcLock,
668     _In_ BYTE iAtom,
669     _Out_opt_ LPDWORD pdwGuidAtom)
670 {
671     IMCCLock<CTFIMECONTEXT> imeContext(imcLock.get().hCompStr);
672     HRESULT hr = imeContext.m_hr;
673     if (!imeContext)
674         hr = E_FAIL;
675     if (FAILED(hr))
676         return hr;
677 
678     hr = E_FAIL;
679     if (iAtom < m_cGuidAtoms)
680     {
681         *pdwGuidAtom = m_adwGuidAtoms[iAtom];
682         hr = S_OK;
683     }
684 
685     return hr;
686 }
687 
688 /**
689  * @unimplemented
690  */
691 HRESULT
692 CicInputContext::CreateInputContext(ITfThreadMgr *pThreadMgr, IMCLock& imcLock)
693 {
694     //FIXME
695     return E_NOTIMPL;
696 }
697 
698 /**
699  * @unimplemented
700  */
701 HRESULT
702 CicInputContext::DestroyInputContext()
703 {
704     // FIXME
705     return E_NOTIMPL;
706 }
707 
708 /**
709  * @implemented
710  */
711 STDMETHODIMP
712 CicInputContext::OnCompositionTerminated(TfEditCookie ecWrite, ITfComposition *pComposition)
713 {
714     return S_OK;
715 }
716 
717 /**
718  * @implemented
719  */
720 HRESULT
721 Inquire(
722     _Out_ LPIMEINFO lpIMEInfo,
723     _Out_ LPWSTR lpszWndClass,
724     _In_ DWORD dwSystemInfoFlags,
725     _In_ HKL hKL)
726 {
727     if (!lpIMEInfo)
728         return E_OUTOFMEMORY;
729 
730     StringCchCopyW(lpszWndClass, 64, L"MSCTFIME UI");
731     lpIMEInfo->dwPrivateDataSize = 0;
732 
733     switch (LOWORD(hKL)) // Language ID
734     {
735         case MAKELANGID(LANG_JAPANESE, SUBLANG_DEFAULT): // Japanese
736         {
737             lpIMEInfo->fdwProperty = IME_PROP_COMPLETE_ON_UNSELECT | IME_PROP_SPECIAL_UI |
738                                      IME_PROP_AT_CARET | IME_PROP_NEED_ALTKEY |
739                                      IME_PROP_KBD_CHAR_FIRST;
740             lpIMEInfo->fdwConversionCaps = IME_CMODE_FULLSHAPE | IME_CMODE_KATAKANA |
741                                            IME_CMODE_NATIVE;
742             lpIMEInfo->fdwSentenceCaps = IME_SMODE_CONVERSATION | IME_SMODE_PLAURALCLAUSE;
743             lpIMEInfo->fdwSelectCaps = SELECT_CAP_SENTENCE | SELECT_CAP_CONVERSION;
744             lpIMEInfo->fdwSCSCaps = SCS_CAP_SETRECONVERTSTRING | SCS_CAP_MAKEREAD |
745                                     SCS_CAP_COMPSTR;
746             lpIMEInfo->fdwUICaps = UI_CAP_ROT90;
747             break;
748         }
749         case MAKELANGID(LANG_KOREAN, SUBLANG_DEFAULT): // Korean
750         {
751             lpIMEInfo->fdwProperty = IME_PROP_COMPLETE_ON_UNSELECT | IME_PROP_SPECIAL_UI |
752                                      IME_PROP_AT_CARET | IME_PROP_NEED_ALTKEY |
753                                      IME_PROP_KBD_CHAR_FIRST;
754             lpIMEInfo->fdwConversionCaps = IME_CMODE_FULLSHAPE | IME_CMODE_NATIVE;
755             lpIMEInfo->fdwSentenceCaps = 0;
756             lpIMEInfo->fdwSCSCaps = SCS_CAP_SETRECONVERTSTRING | SCS_CAP_COMPSTR;
757             lpIMEInfo->fdwSelectCaps = SELECT_CAP_CONVERSION;
758             lpIMEInfo->fdwUICaps = UI_CAP_ROT90;
759             break;
760         }
761         case MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED): // Simplified Chinese
762         case MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL): // Traditional Chinese
763         {
764             lpIMEInfo->fdwProperty = IME_PROP_SPECIAL_UI | IME_PROP_AT_CARET |
765                                      IME_PROP_NEED_ALTKEY | IME_PROP_KBD_CHAR_FIRST;
766             lpIMEInfo->fdwConversionCaps = IME_CMODE_FULLSHAPE | IME_CMODE_NATIVE;
767             lpIMEInfo->fdwSentenceCaps = SELECT_CAP_CONVERSION;
768             lpIMEInfo->fdwSelectCaps = 0;
769             lpIMEInfo->fdwSCSCaps = SCS_CAP_SETRECONVERTSTRING | SCS_CAP_MAKEREAD |
770                                     SCS_CAP_COMPSTR;
771             lpIMEInfo->fdwUICaps = UI_CAP_ROT90;
772             break;
773         }
774         default: // Otherwise
775         {
776             lpIMEInfo->fdwProperty = IME_PROP_UNICODE | IME_PROP_AT_CARET;
777             lpIMEInfo->fdwConversionCaps = 0;
778             lpIMEInfo->fdwSentenceCaps = 0;
779             lpIMEInfo->fdwSCSCaps = 0;
780             lpIMEInfo->fdwUICaps = 0;
781             lpIMEInfo->fdwSelectCaps = 0;
782             break;
783         }
784     }
785 
786     return S_OK;
787 }
788 
789 DEFINE_GUID(IID_ITfSysHookSink, 0x495388DA, 0x21A5, 0x4852, 0x8B, 0xB1, 0xED, 0x2F, 0x29, 0xDA, 0x8D, 0x60);
790 
791 struct ITfSysHookSink : IUnknown
792 {
793     STDMETHOD(OnPreFocusDIM)(HWND hwnd) = 0;
794     STDMETHOD(OnSysKeyboardProc)(UINT, LONG) = 0;
795     STDMETHOD(OnSysShellProc)(INT, UINT, LONG) = 0;
796 };
797 
798 class TLS;
799 
800 typedef INT (CALLBACK *FN_INITDOCMGR)(UINT, ITfDocumentMgr *, ITfDocumentMgr *, LPVOID);
801 typedef INT (CALLBACK *FN_PUSHPOP)(UINT, ITfContext *, LPVOID);
802 
803 class CThreadMgrEventSink : public ITfThreadMgrEventSink
804 {
805 protected:
806     ITfThreadMgr *m_pThreadMgr;
807     DWORD m_dwCookie;
808     FN_INITDOCMGR m_fnInit;
809     FN_PUSHPOP m_fnPushPop;
810     DWORD m_dw;
811     LPVOID m_pCallbackPV;
812     LONG m_cRefs;
813 
814 public:
815     CThreadMgrEventSink(
816         FN_INITDOCMGR fnInit,
817         FN_PUSHPOP fnPushPop = NULL,
818         LPVOID pvCallbackPV = NULL);
819     virtual ~CThreadMgrEventSink() { }
820 
821     void SetCallbackPV(LPVOID pv);
822     HRESULT _Advise(ITfThreadMgr *pThreadMgr);
823     HRESULT _Unadvise();
824 
825     // IUnknown interface
826     STDMETHODIMP QueryInterface(REFIID riid, LPVOID* ppvObj) override;
827     STDMETHODIMP_(ULONG) AddRef() override;
828     STDMETHODIMP_(ULONG) Release() override;
829 
830     // ITfThreadMgrEventSink interface
831     STDMETHODIMP OnInitDocumentMgr(ITfDocumentMgr *pdim) override;
832     STDMETHODIMP OnUninitDocumentMgr(ITfDocumentMgr *pdim) override;
833     STDMETHODIMP OnSetFocus(ITfDocumentMgr *pdimFocus, ITfDocumentMgr *pdimPrevFocus) override;
834     STDMETHODIMP OnPushContext(ITfContext *pic) override;
835     STDMETHODIMP OnPopContext(ITfContext *pic) override;
836 
837     static INT CALLBACK DIMCallback(
838         UINT nCode,
839         ITfDocumentMgr *pDocMgr1,
840         ITfDocumentMgr *pDocMgr2,
841         LPVOID pUserData);
842 };
843 
844 /**
845  * @implemented
846  */
847 CThreadMgrEventSink::CThreadMgrEventSink(
848     FN_INITDOCMGR fnInit,
849     FN_PUSHPOP fnPushPop,
850     LPVOID pvCallbackPV)
851 {
852     m_fnInit = fnInit;
853     m_fnPushPop = fnPushPop;
854     m_pCallbackPV = pvCallbackPV;
855     m_cRefs = 1;
856 }
857 
858 /**
859  * @implemented
860  */
861 STDMETHODIMP CThreadMgrEventSink::QueryInterface(REFIID riid, LPVOID* ppvObj)
862 {
863     if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ITfThreadMgrEventSink))
864     {
865         *ppvObj = this;
866         AddRef();
867         return S_OK;
868     }
869     *ppvObj = NULL;
870     return E_NOINTERFACE;
871 }
872 
873 /**
874  * @implemented
875  */
876 STDMETHODIMP_(ULONG) CThreadMgrEventSink::AddRef()
877 {
878     return ::InterlockedIncrement(&m_cRefs);
879 }
880 
881 /**
882  * @implemented
883  */
884 STDMETHODIMP_(ULONG) CThreadMgrEventSink::Release()
885 {
886     if (::InterlockedDecrement(&m_cRefs) == 0)
887     {
888         delete this;
889         return 0;
890     }
891     return m_cRefs;
892 }
893 
894 INT CALLBACK
895 CThreadMgrEventSink::DIMCallback(
896     UINT nCode,
897     ITfDocumentMgr *pDocMgr1,
898     ITfDocumentMgr *pDocMgr2,
899     LPVOID pUserData)
900 {
901     return E_NOTIMPL;
902 }
903 
904 STDMETHODIMP CThreadMgrEventSink::OnInitDocumentMgr(ITfDocumentMgr *pdim)
905 {
906     if (!m_fnInit)
907         return S_OK;
908     return m_fnInit(0, pdim, NULL, m_pCallbackPV);
909 }
910 
911 STDMETHODIMP CThreadMgrEventSink::OnUninitDocumentMgr(ITfDocumentMgr *pdim)
912 {
913     if (!m_fnInit)
914         return S_OK;
915     return m_fnInit(1, pdim, NULL, m_pCallbackPV);
916 }
917 
918 STDMETHODIMP
919 CThreadMgrEventSink::OnSetFocus(ITfDocumentMgr *pdimFocus, ITfDocumentMgr *pdimPrevFocus)
920 {
921     if (!m_fnInit)
922         return S_OK;
923     return m_fnInit(2, pdimFocus, pdimPrevFocus, m_pCallbackPV);
924 }
925 
926 STDMETHODIMP CThreadMgrEventSink::OnPushContext(ITfContext *pic)
927 {
928     if (!m_fnPushPop)
929         return S_OK;
930     return m_fnPushPop(3, pic, m_pCallbackPV);
931 }
932 
933 STDMETHODIMP CThreadMgrEventSink::OnPopContext(ITfContext *pic)
934 {
935     if (!m_fnPushPop)
936         return S_OK;
937     return m_fnPushPop(4, pic, m_pCallbackPV);
938 }
939 
940 void CThreadMgrEventSink::SetCallbackPV(LPVOID pv)
941 {
942     if (!m_pCallbackPV)
943         m_pCallbackPV = pv;
944 }
945 
946 HRESULT CThreadMgrEventSink::_Advise(ITfThreadMgr *pThreadMgr)
947 {
948     m_pThreadMgr = NULL;
949 
950     HRESULT hr = E_FAIL;
951     ITfSource *pSource = NULL;
952     if (pThreadMgr->QueryInterface(IID_ITfSource, (void **)&pSource) == S_OK &&
953         pSource->AdviseSink(IID_ITfThreadMgrEventSink, this, &m_dwCookie) == S_OK)
954     {
955         m_pThreadMgr = pThreadMgr;
956         pThreadMgr->AddRef();
957         hr = S_OK;
958     }
959 
960     if (pSource)
961         pSource->Release();
962 
963     return hr;
964 }
965 
966 HRESULT CThreadMgrEventSink::_Unadvise()
967 {
968     HRESULT hr = E_FAIL;
969     ITfSource *pSource = NULL;
970 
971     if (m_pThreadMgr)
972     {
973         if (m_pThreadMgr->QueryInterface(IID_ITfSource, (void **)&pSource) == S_OK &&
974             pSource->UnadviseSink(m_dwCookie) == S_OK)
975         {
976             hr = S_OK;
977         }
978 
979         if (pSource)
980             pSource->Release();
981     }
982 
983     if (m_pThreadMgr)
984     {
985         m_pThreadMgr->Release();
986         m_pThreadMgr = NULL;
987     }
988 
989     return hr;
990 }
991 
992 /* FIXME */
993 class CFunctionProvider : public IUnknown
994 {
995 public:
996     CFunctionProvider(TfClientId clientId)
997     {
998     }
999 
1000     // IUnknown interface
1001     STDMETHODIMP QueryInterface(REFIID riid, LPVOID* ppvObj) override;
1002     STDMETHODIMP_(ULONG) AddRef() override;
1003     STDMETHODIMP_(ULONG) Release() override;
1004 };
1005 
1006 /**
1007  * @unimplemented
1008  */
1009 STDMETHODIMP CFunctionProvider::QueryInterface(REFIID riid, LPVOID* ppvObj)
1010 {
1011     return E_NOTIMPL;
1012 }
1013 
1014 /**
1015  * @unimplemented
1016  */
1017 STDMETHODIMP_(ULONG) CFunctionProvider::AddRef()
1018 {
1019     return 1;
1020 }
1021 
1022 /**
1023  * @unimplemented
1024  */
1025 STDMETHODIMP_(ULONG) CFunctionProvider::Release()
1026 {
1027     return 0;
1028 }
1029 
1030 /* FIXME */
1031 class CicBridge : public ITfSysHookSink
1032 {
1033 protected:
1034     LONG m_cRefs;
1035     DWORD m_dwImmxInit;
1036     DWORD m_dw[2];
1037     DWORD m_cActivateLocks;
1038     ITfKeystrokeMgr *m_pKeystrokeMgr;
1039     ITfDocumentMgr *m_pDocMgr;
1040     CThreadMgrEventSink *m_pThreadMgrEventSink;
1041     TfClientId m_cliendId;
1042     LIBTHREAD m_LibThread;
1043     DWORD m_dw21;
1044 
1045     static BOOL CALLBACK EnumCreateInputContextCallback(HIMC hIMC, LPARAM lParam);
1046     static BOOL CALLBACK EnumDestroyInputContextCallback(HIMC hIMC, LPARAM lParam);
1047 
1048 public:
1049     CicBridge();
1050     virtual ~CicBridge();
1051 
1052     // IUnknown interface
1053     STDMETHODIMP QueryInterface(REFIID riid, LPVOID* ppvObj) override;
1054     STDMETHODIMP_(ULONG) AddRef() override;
1055     STDMETHODIMP_(ULONG) Release() override;
1056 
1057     // ITfSysHookSink interface
1058     STDMETHODIMP OnPreFocusDIM(HWND hwnd) override;
1059     STDMETHODIMP OnSysKeyboardProc(UINT, LONG) override;
1060     STDMETHODIMP OnSysShellProc(INT, UINT, LONG) override;
1061 
1062     HRESULT InitIMMX(TLS *pTLS);
1063     BOOL UnInitIMMX(TLS *pTLS);
1064     HRESULT ActivateIMMX(TLS *pTLS, ITfThreadMgr *pThreadMgr);
1065     HRESULT DeactivateIMMX(TLS *pTLS, ITfThreadMgr *pThreadMgr);
1066 
1067     HRESULT CreateInputContext(TLS *pTLS, HIMC hIMC);
1068     HRESULT DestroyInputContext(TLS *pTLS, HIMC hIMC);
1069 
1070     void PostTransMsg(HWND hWnd, INT cTransMsgs, LPTRANSMSG pTransMsgs);
1071     void GetDocumentManager(IMCCLock<CTFIMECONTEXT>& imeContext);
1072 
1073     HRESULT ConfigureGeneral(TLS* pTLS, ITfThreadMgr *pThreadMgr, HKL hKL, HWND hWnd);
1074     HRESULT ConfigureRegisterWord(TLS* pTLS, ITfThreadMgr *pThreadMgr, HKL hKL, HWND hWnd, LPVOID lpData);
1075 };
1076 
1077 class CActiveLanguageProfileNotifySink : public ITfActiveLanguageProfileNotifySink
1078 {
1079 protected:
1080     typedef INT (CALLBACK *FN_COMPARE)(REFGUID rguid1, REFGUID rguid2, BOOL fActivated, LPVOID pUserData);
1081     LONG m_cRefs;
1082     ITfThreadMgr *m_pThreadMgr;
1083     DWORD m_dwConnection;
1084     FN_COMPARE m_fnCompare;
1085     LPVOID m_pUserData;
1086 
1087 public:
1088     CActiveLanguageProfileNotifySink(FN_COMPARE fnCompare, void *pUserData);
1089     virtual ~CActiveLanguageProfileNotifySink();
1090 
1091     HRESULT _Advise(ITfThreadMgr *pThreadMgr);
1092     HRESULT _Unadvise();
1093 
1094     // IUnknown interface
1095     STDMETHODIMP QueryInterface(REFIID riid, LPVOID* ppvObj) override;
1096     STDMETHODIMP_(ULONG) AddRef() override;
1097     STDMETHODIMP_(ULONG) Release() override;
1098 
1099     // ITfActiveLanguageProfileNotifySink interface
1100     STDMETHODIMP
1101     OnActivated(
1102         REFCLSID clsid,
1103         REFGUID guidProfile,
1104         BOOL fActivated) override;
1105 };
1106 
1107 /**
1108  * @implemented
1109  */
1110 CActiveLanguageProfileNotifySink::CActiveLanguageProfileNotifySink(
1111     FN_COMPARE fnCompare,
1112     void *pUserData)
1113 {
1114     m_dwConnection = (DWORD)-1;
1115     m_fnCompare = fnCompare;
1116     m_cRefs = 1;
1117     m_pUserData = pUserData;
1118 }
1119 
1120 /**
1121  * @implemented
1122  */
1123 CActiveLanguageProfileNotifySink::~CActiveLanguageProfileNotifySink()
1124 {
1125 }
1126 
1127 /**
1128  * @implemented
1129  */
1130 STDMETHODIMP CActiveLanguageProfileNotifySink::QueryInterface(REFIID riid, LPVOID* ppvObj)
1131 {
1132     if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ITfActiveLanguageProfileNotifySink))
1133     {
1134         *ppvObj = this;
1135         AddRef();
1136         return S_OK;
1137     }
1138     *ppvObj = NULL;
1139     return E_NOINTERFACE;
1140 }
1141 
1142 /**
1143  * @implemented
1144  */
1145 STDMETHODIMP_(ULONG) CActiveLanguageProfileNotifySink::AddRef()
1146 {
1147     return ::InterlockedIncrement(&m_cRefs);
1148 }
1149 
1150 /**
1151  * @implemented
1152  */
1153 STDMETHODIMP_(ULONG) CActiveLanguageProfileNotifySink::Release()
1154 {
1155     if (::InterlockedDecrement(&m_cRefs) == 0)
1156     {
1157         delete this;
1158         return 0;
1159     }
1160     return m_cRefs;
1161 }
1162 
1163 /**
1164  * @implemented
1165  */
1166 STDMETHODIMP
1167 CActiveLanguageProfileNotifySink::OnActivated(
1168     REFCLSID clsid,
1169     REFGUID guidProfile,
1170     BOOL fActivated)
1171 {
1172     if (!m_fnCompare)
1173         return 0;
1174 
1175     return m_fnCompare(clsid, guidProfile, fActivated, m_pUserData);
1176 }
1177 
1178 /**
1179  * @implemented
1180  */
1181 HRESULT
1182 CActiveLanguageProfileNotifySink::_Advise(
1183     ITfThreadMgr *pThreadMgr)
1184 {
1185     m_pThreadMgr = NULL;
1186 
1187     ITfSource *pSource = NULL;
1188     HRESULT hr = pThreadMgr->QueryInterface(IID_ITfSource, (void **)&pSource);
1189     if (FAILED(hr))
1190         return E_FAIL;
1191 
1192     hr = pSource->AdviseSink(IID_ITfActiveLanguageProfileNotifySink, this, &m_dwConnection);
1193     if (SUCCEEDED(hr))
1194     {
1195         m_pThreadMgr = pThreadMgr;
1196         pThreadMgr->AddRef();
1197         hr = S_OK;
1198     }
1199     else
1200     {
1201         hr = E_FAIL;
1202     }
1203 
1204     if (pSource)
1205         pSource->Release();
1206 
1207     return hr;
1208 }
1209 
1210 /**
1211  * @implemented
1212  */
1213 HRESULT
1214 CActiveLanguageProfileNotifySink::_Unadvise()
1215 {
1216     if (!m_pThreadMgr)
1217         return E_FAIL;
1218 
1219     ITfSource *pSource = NULL;
1220     HRESULT hr = m_pThreadMgr->QueryInterface(IID_ITfSource, (void **)&pSource);
1221     if (SUCCEEDED(hr))
1222     {
1223         hr = pSource->UnadviseSink(m_dwConnection);
1224         if (SUCCEEDED(hr))
1225             hr = S_OK;
1226     }
1227 
1228     if (pSource)
1229         pSource->Release();
1230 
1231     if (m_pThreadMgr)
1232     {
1233         m_pThreadMgr->Release();
1234         m_pThreadMgr = NULL;
1235     }
1236 
1237     return hr;
1238 }
1239 
1240 /* FIXME */
1241 class CicProfile : public IUnknown
1242 {
1243 protected:
1244     ITfInputProcessorProfiles *m_pIPProfiles;
1245     CActiveLanguageProfileNotifySink *m_pActiveLanguageProfileNotifySink;
1246     LANGID  m_LangID1;
1247     WORD    m_padding1;
1248     DWORD   m_dwFlags;
1249     UINT    m_nCodePage;
1250     LANGID  m_LangID2;
1251     WORD    m_padding2;
1252     DWORD   m_dw3[1];
1253     LONG    m_cRefs;
1254 
1255     static INT CALLBACK
1256     ActiveLanguageProfileNotifySinkCallback(
1257         REFGUID rguid1,
1258         REFGUID rguid2,
1259         BOOL fActivated,
1260         LPVOID pUserData);
1261 
1262 public:
1263     CicProfile();
1264     virtual ~CicProfile();
1265 
1266     // IUnknown interface
1267     STDMETHODIMP QueryInterface(REFIID riid, LPVOID* ppvObj) override;
1268     STDMETHODIMP_(ULONG) AddRef() override;
1269     STDMETHODIMP_(ULONG) Release() override;
1270 
1271     HRESULT GetActiveLanguageProfile(HKL hKL, REFGUID rguid, TF_LANGUAGEPROFILE *pProfile);
1272     HRESULT GetLangId(LANGID *pLangID);
1273     HRESULT GetCodePageA(UINT *puCodePage);
1274 
1275     HRESULT InitProfileInstance(TLS *pTLS);
1276 };
1277 
1278 /**
1279  * @implemented
1280  */
1281 CicProfile::CicProfile()
1282 {
1283     m_dwFlags &= 0xFFFFFFF0;
1284     m_cRefs = 1;
1285     m_pIPProfiles = NULL;
1286     m_pActiveLanguageProfileNotifySink = NULL;
1287     m_LangID1 = 0;
1288     m_nCodePage = CP_ACP;
1289     m_LangID2 = 0;
1290     m_dw3[0] = 0;
1291 }
1292 
1293 /**
1294  * @implemented
1295  */
1296 CicProfile::~CicProfile()
1297 {
1298     if (m_pIPProfiles)
1299     {
1300         if (m_LangID1)
1301             m_pIPProfiles->ChangeCurrentLanguage(m_LangID1);
1302 
1303         m_pIPProfiles->Release();
1304         m_pIPProfiles = NULL;
1305     }
1306 
1307     if (m_pActiveLanguageProfileNotifySink)
1308     {
1309         m_pActiveLanguageProfileNotifySink->_Unadvise();
1310         m_pActiveLanguageProfileNotifySink->Release();
1311         m_pActiveLanguageProfileNotifySink = NULL;
1312     }
1313 }
1314 
1315 /**
1316  * @implemented
1317  */
1318 STDMETHODIMP CicProfile::QueryInterface(REFIID riid, LPVOID* ppvObj)
1319 {
1320     *ppvObj = NULL;
1321     return E_NOINTERFACE;
1322 }
1323 
1324 /**
1325  * @implemented
1326  */
1327 STDMETHODIMP_(ULONG) CicProfile::AddRef()
1328 {
1329     return ::InterlockedIncrement(&m_cRefs);
1330 }
1331 
1332 /**
1333  * @implemented
1334  */
1335 STDMETHODIMP_(ULONG) CicProfile::Release()
1336 {
1337     if (::InterlockedDecrement(&m_cRefs) == 0)
1338     {
1339         delete this;
1340         return 0;
1341     }
1342     return m_cRefs;
1343 }
1344 
1345 /**
1346  * @implemented
1347  */
1348 INT CALLBACK
1349 CicProfile::ActiveLanguageProfileNotifySinkCallback(
1350     REFGUID rguid1,
1351     REFGUID rguid2,
1352     BOOL fActivated,
1353     LPVOID pUserData)
1354 {
1355     CicProfile *pThis = (CicProfile *)pUserData;
1356     pThis->m_dwFlags &= ~0xE;
1357     return 0;
1358 }
1359 
1360 /**
1361  * @implemented
1362  */
1363 HRESULT CicProfile::GetCodePageA(UINT *puCodePage)
1364 {
1365     if (!puCodePage)
1366         return E_INVALIDARG;
1367 
1368     if (m_dwFlags & 2)
1369     {
1370         *puCodePage = m_nCodePage;
1371         return S_OK;
1372     }
1373 
1374     *puCodePage = 0;
1375 
1376     LANGID LangID;
1377     HRESULT hr = GetLangId(&LangID);
1378     if (FAILED(hr))
1379         return E_FAIL;
1380 
1381     WCHAR szBuff[12];
1382     INT cch = ::GetLocaleInfoW(LangID, LOCALE_IDEFAULTANSICODEPAGE, szBuff, _countof(szBuff));
1383     if (cch)
1384     {
1385         szBuff[cch] = 0;
1386         m_nCodePage = *puCodePage = wcstoul(szBuff, NULL, 10);
1387         m_dwFlags |= 2;
1388     }
1389 
1390     return S_OK;
1391 }
1392 
1393 /**
1394  * @implemented
1395  */
1396 HRESULT CicProfile::GetLangId(LANGID *pLangID)
1397 {
1398     *pLangID = 0;
1399 
1400     if (!m_pIPProfiles)
1401         return E_FAIL;
1402 
1403     if (m_dwFlags & 4)
1404     {
1405         *pLangID = m_LangID2;
1406         return S_OK;
1407     }
1408 
1409     HRESULT hr = m_pIPProfiles->GetCurrentLanguage(pLangID);
1410     if (SUCCEEDED(hr))
1411     {
1412         m_dwFlags |= 4;
1413         m_LangID2 = *pLangID;
1414     }
1415 
1416     return hr;
1417 }
1418 
1419 class TLS
1420 {
1421 public:
1422     static DWORD s_dwTlsIndex;
1423 
1424     DWORD m_dwSystemInfoFlags;
1425     CicBridge *m_pBridge;
1426     CicProfile *m_pProfile;
1427     ITfThreadMgr *m_pThreadMgr;
1428     DWORD m_dwFlags1;
1429     DWORD m_dwFlags2;
1430     DWORD m_dwUnknown2[2];
1431     DWORD m_dwNowOpening;
1432     DWORD m_NonEAComposition;
1433     DWORD m_cWnds;
1434 
1435     /**
1436      * @implemented
1437      */
1438     static BOOL Initialize()
1439     {
1440         s_dwTlsIndex = ::TlsAlloc();
1441         return s_dwTlsIndex != (DWORD)-1;
1442     }
1443 
1444     /**
1445      * @implemented
1446      */
1447     static VOID Uninitialize()
1448     {
1449         if (s_dwTlsIndex != (DWORD)-1)
1450         {
1451             ::TlsFree(s_dwTlsIndex);
1452             s_dwTlsIndex = (DWORD)-1;
1453         }
1454     }
1455 
1456     /**
1457      * @implemented
1458      */
1459     static TLS* GetTLS()
1460     {
1461         if (s_dwTlsIndex == (DWORD)-1)
1462             return NULL;
1463 
1464         return InternalAllocateTLS();
1465     }
1466 
1467     /**
1468      * @implemented
1469      */
1470     static TLS* PeekTLS()
1471     {
1472         return (TLS*)::TlsGetValue(TLS::s_dwTlsIndex);
1473     }
1474 
1475     static TLS* InternalAllocateTLS();
1476     static BOOL InternalDestroyTLS();
1477 
1478 };
1479 
1480 DWORD TLS::s_dwTlsIndex = (DWORD)-1;
1481 
1482 /**
1483  * @implemented
1484  */
1485 TLS* TLS::InternalAllocateTLS()
1486 {
1487     TLS *pTLS = TLS::PeekTLS();
1488     if (pTLS)
1489         return pTLS;
1490 
1491     if (DllShutDownInProgress())
1492         return NULL;
1493 
1494     pTLS = (TLS *)cicMemAllocClear(sizeof(TLS));
1495     if (!pTLS)
1496         return NULL;
1497 
1498     if (!::TlsSetValue(s_dwTlsIndex, pTLS))
1499     {
1500         cicMemFree(pTLS);
1501         return NULL;
1502     }
1503 
1504     pTLS->m_dwUnknown2[0] |= 1;
1505     pTLS->m_dwUnknown2[2] |= 1;
1506     return pTLS;
1507 }
1508 
1509 /**
1510  * @implemented
1511  */
1512 BOOL TLS::InternalDestroyTLS()
1513 {
1514     TLS *pTLS = TLS::PeekTLS();
1515     if (!pTLS)
1516         return FALSE;
1517 
1518     if (pTLS->m_pBridge)
1519         pTLS->m_pBridge->Release();
1520     if (pTLS->m_pProfile)
1521         pTLS->m_pProfile->Release();
1522     if (pTLS->m_pThreadMgr)
1523         pTLS->m_pThreadMgr->Release();
1524 
1525     cicMemFree(pTLS);
1526     ::TlsSetValue(s_dwTlsIndex, NULL);
1527     return TRUE;
1528 }
1529 
1530 /**
1531  * @implemented
1532  */
1533 HRESULT
1534 CicProfile::InitProfileInstance(TLS *pTLS)
1535 {
1536     HRESULT hr = TF_CreateInputProcessorProfiles(&m_pIPProfiles);
1537     if (FAILED(hr))
1538         return hr;
1539 
1540     if (!m_pActiveLanguageProfileNotifySink)
1541     {
1542         CActiveLanguageProfileNotifySink *pSink =
1543             new CActiveLanguageProfileNotifySink(
1544                 CicProfile::ActiveLanguageProfileNotifySinkCallback, this);
1545         if (!pSink)
1546         {
1547             m_pIPProfiles->Release();
1548             m_pIPProfiles = NULL;
1549             return E_FAIL;
1550         }
1551         m_pActiveLanguageProfileNotifySink = pSink;
1552     }
1553 
1554     if (pTLS->m_pThreadMgr)
1555         m_pActiveLanguageProfileNotifySink->_Advise(pTLS->m_pThreadMgr);
1556 
1557     return hr;
1558 }
1559 
1560 /**
1561  * @implemented
1562  */
1563 STDMETHODIMP CicInputContext::OnCleanupContext(TfEditCookie ecWrite, ITfContext *pic)
1564 {
1565     TLS *pTLS = TLS::PeekTLS();
1566     if (!pTLS || !pTLS->m_pProfile)
1567         return E_OUTOFMEMORY;
1568 
1569     LANGID LangID;
1570     pTLS->m_pProfile->GetLangId(&LangID);
1571 
1572     IMEINFO IMEInfo;
1573     WCHAR szPath[MAX_PATH];
1574     if (Inquire(&IMEInfo, szPath, 0, (HKL)UlongToHandle(LangID)) != S_OK)
1575         return E_FAIL;
1576 
1577     ITfProperty *pProp = NULL;
1578     if (!(IMEInfo.fdwProperty & IME_PROP_COMPLETE_ON_UNSELECT))
1579         return S_OK;
1580 
1581     HRESULT hr = pic->GetProperty(GUID_PROP_COMPOSING, &pProp);
1582     if (FAILED(hr))
1583         return S_OK;
1584 
1585     IEnumTfRanges *pRanges = NULL;
1586     hr = pProp->EnumRanges(ecWrite, &pRanges, NULL);
1587     if (SUCCEEDED(hr))
1588     {
1589         ITfRange *pRange = NULL;
1590         while (pRanges->Next(1, &pRange, 0) == S_OK)
1591         {
1592             VARIANT vari;
1593             V_VT(&vari) = VT_EMPTY;
1594             pProp->GetValue(ecWrite, pRange, &vari);
1595             if (V_VT(&vari) == VT_I4)
1596             {
1597                 if (V_I4(&vari))
1598                     pProp->Clear(ecWrite, pRange);
1599             }
1600             pRange->Release();
1601             pRange = NULL;
1602         }
1603         pRanges->Release();
1604     }
1605     pProp->Release();
1606 
1607     return S_OK;
1608 }
1609 
1610 /***********************************************************************
1611  *      CicBridge
1612  */
1613 
1614 CicBridge::CicBridge()
1615 {
1616     m_dwImmxInit &= ~1;
1617     m_dw[0] &= ~1;
1618     m_dw[1] &= ~1;
1619     m_dw21 &= ~1;
1620     m_pKeystrokeMgr = NULL;
1621     m_pDocMgr = NULL;
1622     m_pThreadMgrEventSink = NULL;
1623     m_cliendId = 0;
1624     m_cRefs = 1;
1625 }
1626 
1627 /**
1628  * @implemented
1629  */
1630 STDMETHODIMP CicBridge::QueryInterface(REFIID riid, LPVOID* ppvObj)
1631 {
1632     *ppvObj = NULL;
1633 
1634     if (!IsEqualIID(riid, IID_ITfSysHookSink))
1635         return E_NOINTERFACE;
1636 
1637     *ppvObj = this;
1638     AddRef();
1639 
1640     return S_OK;
1641 }
1642 
1643 /**
1644  * @implemented
1645  */
1646 STDMETHODIMP_(ULONG) CicBridge::AddRef()
1647 {
1648     return ::InterlockedIncrement(&m_cRefs);
1649 }
1650 
1651 /**
1652  * @implemented
1653  */
1654 STDMETHODIMP_(ULONG) CicBridge::Release()
1655 {
1656     if (::InterlockedDecrement(&m_cRefs) == 0)
1657     {
1658         delete this;
1659         return 0;
1660     }
1661     return m_cRefs;
1662 }
1663 
1664 /**
1665  * @implemented
1666  */
1667 CicBridge::~CicBridge()
1668 {
1669     TLS *pTLS = TLS::PeekTLS();
1670     if (!pTLS || !pTLS->m_pThreadMgr)
1671         return;
1672 
1673     if (SUCCEEDED(DeactivateIMMX(pTLS, pTLS->m_pThreadMgr)))
1674         UnInitIMMX(pTLS);
1675 }
1676 
1677 void CicBridge::GetDocumentManager(IMCCLock<CTFIMECONTEXT>& imeContext)
1678 {
1679     CicInputContext *pCicIC = imeContext.get().m_pCicIC;
1680     if (pCicIC)
1681     {
1682         m_pDocMgr = pCicIC->m_pDocumentMgr;
1683         m_pDocMgr->AddRef();
1684     }
1685     else
1686     {
1687         m_pDocMgr->Release();
1688         m_pDocMgr = NULL;
1689     }
1690 }
1691 
1692 /**
1693  * @unimplemented
1694  */
1695 HRESULT CicBridge::CreateInputContext(TLS *pTLS, HIMC hIMC)
1696 {
1697     IMCLock imcLock(hIMC);
1698     HRESULT hr = imcLock.m_hr;
1699     if (!imcLock)
1700         hr = E_FAIL;
1701     if (FAILED(hr))
1702         return hr;
1703 
1704     if (!imcLock.get().hCtfImeContext)
1705     {
1706         HIMCC hCtfImeContext = ImmCreateIMCC(sizeof(CTFIMECONTEXT));
1707         if (!hCtfImeContext)
1708             return E_OUTOFMEMORY;
1709         imcLock.get().hCtfImeContext = hCtfImeContext;
1710     }
1711 
1712     IMCCLock<CTFIMECONTEXT> imeContext(imcLock.get().hCtfImeContext);
1713     CicInputContext *pCicIC = imeContext.get().m_pCicIC;
1714     if (!pCicIC)
1715     {
1716         pCicIC = new CicInputContext(m_cliendId, &m_LibThread, hIMC);
1717         if (!pCicIC)
1718         {
1719             imeContext.unlock();
1720             imcLock.unlock();
1721             DestroyInputContext(pTLS, hIMC);
1722             return E_OUTOFMEMORY;
1723         }
1724 
1725         if (!pTLS->m_pThreadMgr)
1726         {
1727             pCicIC->Release();
1728             imeContext.unlock();
1729             imcLock.unlock();
1730             DestroyInputContext(pTLS, hIMC);
1731             return E_NOINTERFACE;
1732         }
1733 
1734         imeContext.get().m_pCicIC = pCicIC;
1735     }
1736 
1737     hr = pCicIC->CreateInputContext(pTLS->m_pThreadMgr, imcLock);
1738     if (FAILED(hr))
1739     {
1740         pCicIC->Release();
1741         imeContext.get().m_pCicIC = NULL;
1742     }
1743     else
1744     {
1745         if (imcLock.get().hWnd && imcLock.get().hWnd == ::GetFocus())
1746         {
1747             GetDocumentManager(imeContext);
1748             //FIXME
1749         }
1750     }
1751 
1752     return E_NOTIMPL;
1753 }
1754 
1755 /**
1756  * @implemented
1757  */
1758 HRESULT CicBridge::DestroyInputContext(TLS *pTLS, HIMC hIMC)
1759 {
1760     IMCLock imcLock(hIMC);
1761     HRESULT hr = imcLock.m_hr;
1762     if (!imcLock)
1763         hr = E_FAIL;
1764     if (FAILED(hr))
1765         return hr;
1766 
1767     hr = E_FAIL;
1768     IMCCLock<CTFIMECONTEXT> imeContext(imcLock.get().hCtfImeContext);
1769     if (imeContext)
1770         hr = imeContext.m_hr;
1771 
1772     if (SUCCEEDED(hr) && !(imeContext.get().m_dwCicFlags & 1))
1773     {
1774         imeContext.get().m_dwCicFlags |= 1;
1775 
1776         CicInputContext *pCicIC = imeContext.get().m_pCicIC;
1777         if (pCicIC)
1778         {
1779             imeContext.get().m_pCicIC = NULL;
1780             hr = pCicIC->DestroyInputContext();
1781             pCicIC->Release();
1782             imeContext.get().m_pCicIC = NULL;
1783         }
1784     }
1785 
1786     if (imcLock.get().hCtfImeContext)
1787     {
1788         ImmDestroyIMCC(imcLock.get().hCtfImeContext);
1789         imcLock.get().hCtfImeContext = NULL;
1790         hr = S_OK;
1791     }
1792 
1793     return hr;
1794 }
1795 
1796 typedef struct ENUM_CREATE_DESTROY_IC
1797 {
1798     TLS *m_pTLS;
1799     CicBridge *m_pBridge;
1800 } ENUM_CREATE_DESTROY_IC, *PENUM_CREATE_DESTROY_IC;
1801 
1802 BOOL CALLBACK CicBridge::EnumCreateInputContextCallback(HIMC hIMC, LPARAM lParam)
1803 {
1804     PENUM_CREATE_DESTROY_IC pData = (PENUM_CREATE_DESTROY_IC)lParam;
1805     pData->m_pBridge->CreateInputContext(pData->m_pTLS, hIMC);
1806     return TRUE;
1807 }
1808 
1809 BOOL CALLBACK CicBridge::EnumDestroyInputContextCallback(HIMC hIMC, LPARAM lParam)
1810 {
1811     PENUM_CREATE_DESTROY_IC pData = (PENUM_CREATE_DESTROY_IC)lParam;
1812     pData->m_pBridge->DestroyInputContext(pData->m_pTLS, hIMC);
1813     return TRUE;
1814 }
1815 
1816 /**
1817  * @unimplemented
1818  */
1819 HRESULT CicBridge::ActivateIMMX(TLS *pTLS, ITfThreadMgr *pThreadMgr)
1820 {
1821     //FIXME
1822 
1823     if (m_cActivateLocks++ != 0)
1824         return S_OK;
1825 
1826     ITfSourceSingle *pSource = NULL;
1827     HRESULT hr = pThreadMgr->QueryInterface(IID_ITfSourceSingle, (void**)&pSource);
1828     if (FAILED(hr))
1829     {
1830         DeactivateIMMX(pTLS, pThreadMgr);
1831         return hr;
1832     }
1833 
1834     CFunctionProvider *pProvider = new CFunctionProvider(m_cliendId);
1835     if (!pProvider)
1836     {
1837         hr = E_FAIL;
1838         goto Finish;
1839     }
1840 
1841     pSource->AdviseSingleSink(m_cliendId, IID_ITfFunctionProvider, pProvider);
1842     pProvider->Release();
1843 
1844     if (!m_pDocMgr)
1845     {
1846         hr = pThreadMgr->CreateDocumentMgr(&m_pDocMgr);
1847         if (FAILED(hr))
1848         {
1849             hr = E_FAIL;
1850             goto Finish;
1851         }
1852 
1853         SetCompartmentDWORD(m_cliendId, m_pDocMgr, GUID_COMPARTMENT_CTFIME_DIMFLAGS, TRUE, FALSE);
1854     }
1855 
1856     //FIXME
1857 
1858     hr = S_OK;
1859     if (pTLS->m_dwUnknown2[1] & 1)
1860     {
1861         ENUM_CREATE_DESTROY_IC Data = { pTLS, this };
1862         ImmEnumInputContext(0, CicBridge::EnumCreateInputContextCallback, (LPARAM)&Data);
1863     }
1864 
1865 Finish:
1866     if (FAILED(hr))
1867         DeactivateIMMX(pTLS, pThreadMgr);
1868 
1869     if (pSource)
1870         pSource->Release();
1871 
1872     return hr;
1873 }
1874 
1875 /**
1876  * @unimplemented
1877  */
1878 HRESULT CicBridge::DeactivateIMMX(TLS *pTLS, ITfThreadMgr *pThreadMgr)
1879 {
1880     if (m_dw[1] & 1)
1881         return TRUE;
1882 
1883     m_dw[1] |= 1;
1884 
1885     if (m_cliendId)
1886     {
1887         ENUM_CREATE_DESTROY_IC Data = { pTLS, this };
1888         ImmEnumInputContext(0, CicBridge::EnumDestroyInputContextCallback, (LPARAM)&Data);
1889         pTLS->m_dwUnknown2[1] |= 1u;
1890 
1891         ITfSourceSingle *pSource = NULL;
1892         if (pThreadMgr->QueryInterface(IID_ITfSourceSingle, (void **)&pSource) == S_OK)
1893             pSource->UnadviseSingleSink(m_cliendId, IID_ITfFunctionProvider);
1894 
1895         m_cliendId = 0;
1896 
1897         while (m_cActivateLocks > 0)
1898         {
1899             --m_cActivateLocks;
1900             pThreadMgr->Deactivate();
1901         }
1902 
1903         if (pSource)
1904             pSource->Release();
1905     }
1906 
1907     if (m_pDocMgr)
1908     {
1909         m_pDocMgr->Release();
1910         m_pDocMgr = NULL;
1911     }
1912 
1913     //FIXME
1914 
1915     m_dw[1] &= ~1;
1916 
1917     return S_OK;
1918 }
1919 
1920 /**
1921  * @implemented
1922  */
1923 HRESULT CicBridge::InitIMMX(TLS *pTLS)
1924 {
1925     if (m_dwImmxInit & 1)
1926         return S_OK;
1927 
1928     HRESULT hr;
1929     if (!pTLS->m_pThreadMgr)
1930     {
1931         hr = TF_CreateThreadMgr(&pTLS->m_pThreadMgr);
1932         if (FAILED(hr))
1933             return E_FAIL;
1934 
1935         hr = pTLS->m_pThreadMgr->QueryInterface(IID_ITfThreadMgr, (void **)&pTLS->m_pThreadMgr);
1936         if (FAILED(hr))
1937         {
1938             pTLS->m_pThreadMgr->Release();
1939             pTLS->m_pThreadMgr = NULL;
1940             return E_FAIL;
1941         }
1942     }
1943 
1944     if (!m_pThreadMgrEventSink)
1945     {
1946         m_pThreadMgrEventSink =
1947             new CThreadMgrEventSink(CThreadMgrEventSink::DIMCallback, NULL, NULL);
1948         if (!m_pThreadMgrEventSink)
1949         {
1950             UnInitIMMX(pTLS);
1951             return E_FAIL;
1952         }
1953     }
1954 
1955     m_pThreadMgrEventSink->SetCallbackPV(m_pThreadMgrEventSink);
1956     m_pThreadMgrEventSink->_Advise(pTLS->m_pThreadMgr);
1957 
1958     if (!pTLS->m_pProfile)
1959     {
1960         pTLS->m_pProfile = new CicProfile();
1961         if (!pTLS->m_pProfile)
1962             return E_OUTOFMEMORY;
1963         hr = pTLS->m_pProfile->InitProfileInstance(pTLS);
1964         if (FAILED(hr))
1965         {
1966             UnInitIMMX(pTLS);
1967             return E_FAIL;
1968         }
1969     }
1970 
1971     hr = pTLS->m_pThreadMgr->QueryInterface(IID_ITfKeystrokeMgr, (void **)&m_pKeystrokeMgr);
1972     if (FAILED(hr))
1973     {
1974         UnInitIMMX(pTLS);
1975         return E_FAIL;
1976     }
1977 
1978     hr = InitDisplayAttrbuteLib(&m_LibThread);
1979     if (FAILED(hr))
1980     {
1981         UnInitIMMX(pTLS);
1982         return E_FAIL;
1983     }
1984 
1985     m_dwImmxInit |= 1;
1986     return S_OK;
1987 }
1988 
1989 /**
1990  * @implemented
1991  */
1992 BOOL CicBridge::UnInitIMMX(TLS *pTLS)
1993 {
1994     UninitDisplayAttrbuteLib(&m_LibThread);
1995     TFUninitLib_Thread(&m_LibThread);
1996 
1997     if (m_pKeystrokeMgr)
1998     {
1999         m_pKeystrokeMgr->Release();
2000         m_pKeystrokeMgr = NULL;
2001     }
2002 
2003     if (pTLS->m_pProfile)
2004     {
2005         pTLS->m_pProfile->Release();
2006         pTLS->m_pProfile = NULL;
2007     }
2008 
2009     if (m_pThreadMgrEventSink)
2010     {
2011         m_pThreadMgrEventSink->_Unadvise();
2012         m_pThreadMgrEventSink->Release();
2013         m_pThreadMgrEventSink = NULL;
2014     }
2015 
2016     if (pTLS->m_pThreadMgr)
2017     {
2018         pTLS->m_pThreadMgr->Release();
2019         pTLS->m_pThreadMgr = NULL;
2020     }
2021 
2022     m_dwImmxInit &= ~1;
2023     return TRUE;
2024 }
2025 
2026 /**
2027  * @implemented
2028  */
2029 STDMETHODIMP CicBridge::OnPreFocusDIM(HWND hwnd)
2030 {
2031     return S_OK;
2032 }
2033 
2034 /**
2035  * @unimplemented
2036  */
2037 STDMETHODIMP CicBridge::OnSysKeyboardProc(UINT, LONG)
2038 {
2039     return E_NOTIMPL;
2040 }
2041 
2042 /**
2043  * @implemented
2044  */
2045 STDMETHODIMP CicBridge::OnSysShellProc(INT, UINT, LONG)
2046 {
2047     return S_OK;
2048 }
2049 
2050 /**
2051  * @implemented
2052  */
2053 void CicBridge::PostTransMsg(HWND hWnd, INT cTransMsgs, LPTRANSMSG pTransMsgs)
2054 {
2055     for (INT i = 0; i < cTransMsgs; ++i, ++pTransMsgs)
2056     {
2057         ::PostMessageW(hWnd, pTransMsgs->message, pTransMsgs->wParam, pTransMsgs->lParam);
2058     }
2059 }
2060 
2061 /**
2062  * @implemented
2063  */
2064 HRESULT
2065 CicBridge::ConfigureGeneral(
2066     TLS* pTLS,
2067     ITfThreadMgr *pThreadMgr,
2068     HKL hKL,
2069     HWND hWnd)
2070 {
2071     CicProfile *pProfile = pTLS->m_pProfile;
2072     if (!pProfile)
2073         return E_OUTOFMEMORY;
2074 
2075     TF_LANGUAGEPROFILE profile;
2076     HRESULT hr = pProfile->GetActiveLanguageProfile(hKL, GUID_TFCAT_TIP_KEYBOARD, &profile);
2077     if (FAILED(hr))
2078         return hr;
2079 
2080     ITfFunctionProvider *pProvider = NULL;
2081     hr = pThreadMgr->GetFunctionProvider(profile.clsid, &pProvider);
2082     if (FAILED(hr))
2083         return hr;
2084 
2085     ITfFnConfigure *pFnConfigure = NULL;
2086     hr = pProvider->GetFunction(GUID_NULL, IID_ITfFnConfigure, (IUnknown**)&pFnConfigure);
2087     if (FAILED(hr))
2088     {
2089         pProvider->Release();
2090         return hr;
2091     }
2092 
2093     hr = pFnConfigure->Show(hWnd, profile.langid, profile.guidProfile);
2094 
2095     pFnConfigure->Release();
2096     pProvider->Release();
2097     return hr;
2098 }
2099 
2100 /**
2101  * @implemented
2102  */
2103 HRESULT
2104 CicBridge::ConfigureRegisterWord(
2105     TLS* pTLS,
2106     ITfThreadMgr *pThreadMgr,
2107     HKL hKL,
2108     HWND hWnd,
2109     LPVOID lpData)
2110 {
2111     CicProfile *pProfile = pTLS->m_pProfile;
2112     if (!pProfile)
2113         return E_OUTOFMEMORY;
2114 
2115     TF_LANGUAGEPROFILE profile;
2116     HRESULT hr = pProfile->GetActiveLanguageProfile(hKL, GUID_TFCAT_TIP_KEYBOARD, &profile);
2117     if (FAILED(hr))
2118         return hr;
2119 
2120     ITfFunctionProvider *pProvider = NULL;
2121     hr = pThreadMgr->GetFunctionProvider(profile.clsid, &pProvider);
2122     if (FAILED(hr))
2123         return hr;
2124 
2125     ITfFnConfigureRegisterWord *pFunction = NULL;
2126     hr = pProvider->GetFunction(GUID_NULL, IID_ITfFnConfigureRegisterWord, (IUnknown**)&pFunction);
2127     if (FAILED(hr))
2128     {
2129         pProvider->Release();
2130         return hr;
2131     }
2132 
2133     REGISTERWORDW* pRegWord = (REGISTERWORDW*)lpData;
2134     if (pRegWord)
2135     {
2136         if (pRegWord->lpWord)
2137         {
2138             hr = E_OUTOFMEMORY;
2139             BSTR bstrWord = SysAllocString(pRegWord->lpWord);
2140             if (bstrWord)
2141             {
2142                 hr = pFunction->Show(hWnd, profile.langid, profile.guidProfile, bstrWord);
2143                 SysFreeString(bstrWord);
2144             }
2145         }
2146         else
2147         {
2148             hr = pFunction->Show(hWnd, profile.langid, profile.guidProfile, NULL);
2149         }
2150     }
2151 
2152     pProvider->Release();
2153     pFunction->Release();
2154     return hr;
2155 }
2156 
2157 /***********************************************************************
2158  *      CicProfile
2159  */
2160 
2161 /**
2162  * @unimplemented
2163  */
2164 HRESULT
2165 CicProfile::GetActiveLanguageProfile(
2166     HKL hKL,
2167     REFGUID rguid,
2168     TF_LANGUAGEPROFILE *pProfile)
2169 {
2170     return E_NOTIMPL;
2171 }
2172 
2173 /***********************************************************************
2174  *      ImeInquire (MSCTFIME.@)
2175  *
2176  * MSCTFIME's ImeInquire does nothing.
2177  *
2178  * @implemented
2179  * @see CtfImeInquireExW
2180  */
2181 EXTERN_C
2182 BOOL WINAPI
2183 ImeInquire(
2184     _Out_ LPIMEINFO lpIMEInfo,
2185     _Out_ LPWSTR lpszWndClass,
2186     _In_ DWORD dwSystemInfoFlags)
2187 {
2188     TRACE("(%p, %p, 0x%lX)\n", lpIMEInfo, lpszWndClass, dwSystemInfoFlags);
2189     return FALSE;
2190 }
2191 
2192 /***********************************************************************
2193  *      ImeConversionList (MSCTFIME.@)
2194  *
2195  * MSCTFIME's ImeConversionList does nothing.
2196  *
2197  * @implemented
2198  * @see ImmGetConversionListW
2199  */
2200 EXTERN_C DWORD WINAPI
2201 ImeConversionList(
2202     _In_ HIMC hIMC,
2203     _In_ LPCWSTR lpSrc,
2204     _Out_ LPCANDIDATELIST lpDst,
2205     _In_ DWORD dwBufLen,
2206     _In_ UINT uFlag)
2207 {
2208     TRACE("(%p, %s, %p, 0x%lX, %u)\n", hIMC, debugstr_w(lpSrc), lpDst, dwBufLen, uFlag);
2209     return 0;
2210 }
2211 
2212 /***********************************************************************
2213  *      ImeRegisterWord (MSCTFIME.@)
2214  *
2215  * MSCTFIME's ImeRegisterWord does nothing.
2216  *
2217  * @implemented
2218  * @see ImeUnregisterWord
2219  */
2220 EXTERN_C BOOL WINAPI
2221 ImeRegisterWord(
2222     _In_ LPCWSTR lpszReading,
2223     _In_ DWORD dwStyle,
2224     _In_ LPCWSTR lpszString)
2225 {
2226     TRACE("(%s, 0x%lX, %s)\n", debugstr_w(lpszReading), dwStyle, debugstr_w(lpszString));
2227     return FALSE;
2228 }
2229 
2230 /***********************************************************************
2231  *      ImeUnregisterWord (MSCTFIME.@)
2232  *
2233  * MSCTFIME's ImeUnregisterWord does nothing.
2234  *
2235  * @implemented
2236  * @see ImeRegisterWord
2237  */
2238 EXTERN_C BOOL WINAPI
2239 ImeUnregisterWord(
2240     _In_ LPCWSTR lpszReading,
2241     _In_ DWORD dwStyle,
2242     _In_ LPCWSTR lpszString)
2243 {
2244     TRACE("(%s, 0x%lX, %s)\n", debugstr_w(lpszReading), dwStyle, debugstr_w(lpszString));
2245     return FALSE;
2246 }
2247 
2248 /***********************************************************************
2249  *      ImeGetRegisterWordStyle (MSCTFIME.@)
2250  *
2251  * MSCTFIME's ImeGetRegisterWordStyle does nothing.
2252  *
2253  * @implemented
2254  * @see ImeRegisterWord
2255  */
2256 EXTERN_C UINT WINAPI
2257 ImeGetRegisterWordStyle(
2258     _In_ UINT nItem,
2259     _Out_ LPSTYLEBUFW lpStyleBuf)
2260 {
2261     TRACE("(%u, %p)\n", nItem, lpStyleBuf);
2262     return 0;
2263 }
2264 
2265 /***********************************************************************
2266  *      ImeEnumRegisterWord (MSCTFIME.@)
2267  *
2268  * MSCTFIME's ImeEnumRegisterWord does nothing.
2269  *
2270  * @implemented
2271  * @see ImeRegisterWord
2272  */
2273 EXTERN_C UINT WINAPI
2274 ImeEnumRegisterWord(
2275     _In_ REGISTERWORDENUMPROCW lpfnEnumProc,
2276     _In_opt_ LPCWSTR lpszReading,
2277     _In_ DWORD dwStyle,
2278     _In_opt_ LPCWSTR lpszString,
2279     _In_opt_ LPVOID lpData)
2280 {
2281     TRACE("(%p, %s, %lu, %s, %p)\n", lpfnEnumProc, debugstr_w(lpszReading),
2282           dwStyle, debugstr_w(lpszString), lpData);
2283     return 0;
2284 }
2285 
2286 EXTERN_C BOOL WINAPI
2287 ImeConfigure(
2288     _In_ HKL hKL,
2289     _In_ HWND hWnd,
2290     _In_ DWORD dwMode,
2291     _Inout_opt_ LPVOID lpData)
2292 {
2293     TRACE("(%p, %p, %lu, %p)\n", hKL, hWnd, dwMode, lpData);
2294 
2295     TLS *pTLS = TLS::GetTLS();
2296     if (!pTLS || !pTLS->m_pBridge || !pTLS->m_pThreadMgr)
2297         return FALSE;
2298 
2299     CicBridge *pBridge = pTLS->m_pBridge;
2300     ITfThreadMgr *pThreadMgr = pTLS->m_pThreadMgr;
2301 
2302     if (dwMode & 1)
2303         return (pBridge->ConfigureGeneral(pTLS, pThreadMgr, hKL, hWnd) == S_OK);
2304 
2305     if (dwMode & 2)
2306         return (pBridge->ConfigureRegisterWord(pTLS, pThreadMgr, hKL, hWnd, lpData) == S_OK);
2307 
2308     return FALSE;
2309 }
2310 
2311 /***********************************************************************
2312  *      ImeDestroy (MSCTFIME.@)
2313  *
2314  * @implemented
2315  */
2316 EXTERN_C BOOL WINAPI
2317 ImeDestroy(
2318     _In_ UINT uReserved)
2319 {
2320     TRACE("(%u)\n", uReserved);
2321 
2322     TLS *pTLS = TLS::PeekTLS();
2323     if (pTLS)
2324         return FALSE;
2325 
2326     if (!pTLS->m_pBridge || !pTLS->m_pThreadMgr)
2327         return FALSE;
2328 
2329     if (pTLS->m_dwSystemInfoFlags & IME_SYSINFO_WINLOGON)
2330         return TRUE;
2331 
2332     if (pTLS->m_pBridge->DeactivateIMMX(pTLS, pTLS->m_pThreadMgr) != S_OK)
2333         return FALSE;
2334 
2335     return pTLS->m_pBridge->UnInitIMMX(pTLS);
2336 }
2337 
2338 /***********************************************************************
2339  *      ImeEscape (MSCTFIME.@)
2340  *
2341  * MSCTFIME's ImeEscape does nothing.
2342  *
2343  * @implemented
2344  * @see CtfImeEscapeEx
2345  */
2346 EXTERN_C LRESULT WINAPI
2347 ImeEscape(
2348     _In_ HIMC hIMC,
2349     _In_ UINT uEscape,
2350     _Inout_opt_ LPVOID lpData)
2351 {
2352     TRACE("(%p, %u, %p)\n", hIMC, uEscape, lpData);
2353     return 0;
2354 }
2355 
2356 EXTERN_C BOOL WINAPI
2357 ImeProcessKey(
2358     _In_ HIMC hIMC,
2359     _In_ UINT uVirKey,
2360     _In_ LPARAM lParam,
2361     _In_ CONST LPBYTE lpbKeyState)
2362 {
2363     FIXME("stub:(%p, %u, %p, lpbKeyState)\n", hIMC, uVirKey, lParam, lpbKeyState);
2364     return FALSE;
2365 }
2366 
2367 /***********************************************************************
2368  *      ImeSelect (MSCTFIME.@)
2369  *
2370  * MSCTFIME's ImeSelect does nothing.
2371  *
2372  * @implemented
2373  * @see CtfImeSelectEx
2374  */
2375 EXTERN_C BOOL WINAPI
2376 ImeSelect(
2377     _In_ HIMC hIMC,
2378     _In_ BOOL fSelect)
2379 {
2380     TRACE("(%p, %u)\n", hIMC, fSelect);
2381     return FALSE;
2382 }
2383 
2384 /***********************************************************************
2385  *      ImeSetActiveContext (MSCTFIME.@)
2386  *
2387  * MSCTFIME's ImeSetActiveContext does nothing.
2388  *
2389  * @implemented
2390  * @see CtfImeSetActiveContextAlways
2391  */
2392 EXTERN_C BOOL WINAPI
2393 ImeSetActiveContext(
2394     _In_ HIMC hIMC,
2395     _In_ BOOL fFlag)
2396 {
2397     TRACE("(%p, %u)\n", hIMC, fFlag);
2398     return FALSE;
2399 }
2400 
2401 EXTERN_C UINT WINAPI
2402 ImeToAsciiEx(
2403     _In_ UINT uVirKey,
2404     _In_ UINT uScanCode,
2405     _In_ CONST LPBYTE lpbKeyState,
2406     _Out_ LPTRANSMSGLIST lpTransMsgList,
2407     _In_ UINT fuState,
2408     _In_ HIMC hIMC)
2409 {
2410     FIXME("stub:(%u, %u, %p, %p, %u, %p)\n", uVirKey, uScanCode, lpbKeyState, lpTransMsgList,
2411           fuState, hIMC);
2412     return 0;
2413 }
2414 
2415 EXTERN_C BOOL WINAPI
2416 NotifyIME(
2417     _In_ HIMC hIMC,
2418     _In_ DWORD dwAction,
2419     _In_ DWORD dwIndex,
2420     _In_ DWORD_PTR dwValue)
2421 {
2422     FIXME("stub:(%p, 0x%lX, 0x%lX, %p)\n", hIMC, dwAction, dwIndex, dwValue);
2423     return FALSE;
2424 }
2425 
2426 EXTERN_C BOOL WINAPI
2427 ImeSetCompositionString(
2428     _In_ HIMC hIMC,
2429     _In_ DWORD dwIndex,
2430     _In_opt_ LPCVOID lpComp,
2431     _In_ DWORD dwCompLen,
2432     _In_opt_ LPCVOID lpRead,
2433     _In_ DWORD dwReadLen)
2434 {
2435     FIXME("stub:(%p, 0x%lX, %p, 0x%lX, %p, 0x%lX)\n", hIMC, dwIndex, lpComp, dwCompLen,
2436           lpRead, dwReadLen);
2437     return FALSE;
2438 }
2439 
2440 EXTERN_C DWORD WINAPI
2441 ImeGetImeMenuItems(
2442     _In_ HIMC hIMC,
2443     _In_ DWORD dwFlags,
2444     _In_ DWORD dwType,
2445     _Inout_opt_ LPIMEMENUITEMINFOW lpImeParentMenu,
2446     _Inout_opt_ LPIMEMENUITEMINFOW lpImeMenu,
2447     _In_ DWORD dwSize)
2448 {
2449     FIXME("stub:(%p, 0x%lX, 0x%lX, %p, %p, 0x%lX)\n", hIMC, dwFlags, dwType, lpImeParentMenu,
2450           lpImeMenu, dwSize);
2451     return 0;
2452 }
2453 
2454 /***********************************************************************
2455  *      CtfImeInquireExW (MSCTFIME.@)
2456  *
2457  * @implemented
2458  */
2459 EXTERN_C HRESULT WINAPI
2460 CtfImeInquireExW(
2461     _Out_ LPIMEINFO lpIMEInfo,
2462     _Out_ LPWSTR lpszWndClass,
2463     _In_ DWORD dwSystemInfoFlags,
2464     _In_ HKL hKL)
2465 {
2466     TRACE("(%p, %p, 0x%lX, %p)\n", lpIMEInfo, lpszWndClass, dwSystemInfoFlags, hKL);
2467 
2468     TLS *pTLS = TLS::GetTLS();
2469     if (!pTLS)
2470         return E_OUTOFMEMORY;
2471 
2472     if (!IsInteractiveUserLogon())
2473     {
2474         dwSystemInfoFlags |= IME_SYSINFO_WINLOGON;
2475         g_bWinLogon = TRUE;
2476     }
2477 
2478     pTLS->m_dwSystemInfoFlags = dwSystemInfoFlags;
2479 
2480     return Inquire(lpIMEInfo, lpszWndClass, dwSystemInfoFlags, hKL);
2481 }
2482 
2483 EXTERN_C BOOL WINAPI
2484 CtfImeSelectEx(
2485     _In_ HIMC hIMC,
2486     _In_ BOOL fSelect,
2487     _In_ HKL hKL)
2488 {
2489     FIXME("stub:(%p, %d, %p)\n", hIMC, fSelect, hKL);
2490     return FALSE;
2491 }
2492 
2493 EXTERN_C LRESULT WINAPI
2494 CtfImeEscapeEx(
2495     _In_ HIMC hIMC,
2496     _In_ UINT uSubFunc,
2497     _Inout_opt_ LPVOID lpData,
2498     _In_ HKL hKL)
2499 {
2500     FIXME("stub:(%p, %u, %p, %p)\n", hIMC, uSubFunc, lpData, hKL);
2501     return 0;
2502 }
2503 
2504 /***********************************************************************
2505  *      CtfImeGetGuidAtom (MSCTFIME.@)
2506  *
2507  * @implemented
2508  */
2509 EXTERN_C HRESULT WINAPI
2510 CtfImeGetGuidAtom(
2511     _In_ HIMC hIMC,
2512     _In_ DWORD dwUnknown,
2513     _Out_opt_ LPDWORD pdwGuidAtom)
2514 {
2515     TRACE("(%p, 0x%lX, %p)\n", hIMC, dwUnknown, pdwGuidAtom);
2516 
2517     IMCLock imcLock(hIMC);
2518 
2519     HRESULT hr = imcLock.m_hr;
2520     if (!imcLock)
2521         hr = E_FAIL;
2522     if (FAILED(hr))
2523         return hr;
2524 
2525     IMCCLock<CTFIMECONTEXT> imccLock(imcLock.get().hCtfImeContext);
2526     hr = imccLock.m_hr;
2527     if (!imccLock)
2528         hr = E_FAIL;
2529     if (FAILED(hr))
2530         return hr;
2531 
2532     if (!imccLock.get().m_pCicIC)
2533         return E_OUTOFMEMORY;
2534 
2535     hr = imccLock.get().m_pCicIC->GetGuidAtom(imcLock, dwUnknown, pdwGuidAtom);
2536     return hr;
2537 }
2538 
2539 /***********************************************************************
2540  *      CtfImeIsGuidMapEnable (MSCTFIME.@)
2541  *
2542  * @implemented
2543  */
2544 EXTERN_C BOOL WINAPI
2545 CtfImeIsGuidMapEnable(
2546     _In_ HIMC hIMC)
2547 {
2548     TRACE("(%p)\n", hIMC);
2549 
2550     BOOL ret = FALSE;
2551     HRESULT hr;
2552     IMCLock imcLock(hIMC);
2553 
2554     hr = imcLock.m_hr;
2555     if (!imcLock)
2556         hr = E_FAIL;
2557     if (SUCCEEDED(hr))
2558         ret = !!(imcLock.get().fdwInit & INIT_GUIDMAP);
2559 
2560     return ret;
2561 }
2562 
2563 /***********************************************************************
2564  *      CtfImeCreateThreadMgr (MSCTFIME.@)
2565  *
2566  * @implemented
2567  */
2568 EXTERN_C HRESULT WINAPI
2569 CtfImeCreateThreadMgr(VOID)
2570 {
2571     TRACE("()\n");
2572 
2573     TLS *pTLS = TLS::GetTLS();
2574     if (!pTLS)
2575         return E_OUTOFMEMORY;
2576 
2577     if (!pTLS->m_pBridge)
2578     {
2579         pTLS->m_pBridge = new CicBridge();
2580         if (!pTLS->m_pBridge)
2581             return E_OUTOFMEMORY;
2582     }
2583 
2584     HRESULT hr = S_OK;
2585     if (!g_bWinLogon && !(pTLS->m_dwSystemInfoFlags & IME_SYSINFO_WINLOGON))
2586     {
2587         hr = pTLS->m_pBridge->InitIMMX(pTLS);
2588         if (SUCCEEDED(hr))
2589         {
2590             if (!pTLS->m_pThreadMgr)
2591                 return E_OUTOFMEMORY;
2592 
2593             hr = pTLS->m_pBridge->ActivateIMMX(pTLS, pTLS->m_pThreadMgr);
2594             if (FAILED(hr))
2595                 pTLS->m_pBridge->UnInitIMMX(pTLS);
2596         }
2597     }
2598 
2599     return hr;
2600 }
2601 
2602 
2603 /***********************************************************************
2604  *      CtfImeDestroyThreadMgr (MSCTFIME.@)
2605  *
2606  * @implemented
2607  */
2608 EXTERN_C HRESULT WINAPI
2609 CtfImeDestroyThreadMgr(VOID)
2610 {
2611     TRACE("()\n");
2612 
2613     TLS *pTLS = TLS::PeekTLS();
2614     if (!pTLS)
2615         return E_OUTOFMEMORY;
2616 
2617     if (pTLS->m_pBridge)
2618     {
2619         pTLS->m_pBridge = new CicBridge();
2620         if (!pTLS->m_pBridge)
2621             return E_OUTOFMEMORY;
2622     }
2623 
2624     if (!pTLS->m_pThreadMgr)
2625         return E_OUTOFMEMORY;
2626 
2627     if (pTLS->m_dwSystemInfoFlags & IME_SYSINFO_WINLOGON)
2628         return S_OK;
2629 
2630     HRESULT hr = pTLS->m_pBridge->DeactivateIMMX(pTLS, pTLS->m_pThreadMgr);
2631     if (hr == S_OK)
2632         pTLS->m_pBridge->UnInitIMMX(pTLS);
2633 
2634     return hr;
2635 }
2636 
2637 EXTERN_C HRESULT WINAPI
2638 CtfImeCreateInputContext(
2639     _In_ HIMC hIMC)
2640 {
2641     return E_NOTIMPL;
2642 }
2643 
2644 /***********************************************************************
2645  *      CtfImeDestroyInputContext (MSCTFIME.@)
2646  *
2647  * @implemented
2648  */
2649 EXTERN_C HRESULT WINAPI
2650 CtfImeDestroyInputContext(
2651     _In_ HIMC hIMC)
2652 {
2653     TRACE("(%p)\n", hIMC);
2654 
2655     TLS *pTLS = TLS::PeekTLS();
2656     if (!pTLS || !pTLS->m_pBridge)
2657         return E_OUTOFMEMORY;
2658 
2659     return pTLS->m_pBridge->DestroyInputContext(pTLS, hIMC);
2660 }
2661 
2662 EXTERN_C HRESULT WINAPI
2663 CtfImeSetActiveContextAlways(
2664     _In_ HIMC hIMC,
2665     _In_ BOOL fActive,
2666     _In_ HWND hWnd,
2667     _In_ HKL hKL)
2668 {
2669     FIXME("stub:(%p, %d, %p, %p)\n", hIMC, fActive, hWnd, hKL);
2670     return E_NOTIMPL;
2671 }
2672 
2673 EXTERN_C HRESULT WINAPI
2674 CtfImeProcessCicHotkey(
2675     _In_ HIMC hIMC,
2676     _In_ UINT vKey,
2677     _In_ LPARAM lParam)
2678 {
2679     FIXME("stub:(%p, %u, %p)\n", hIMC, vKey, lParam);
2680     return E_NOTIMPL;
2681 }
2682 
2683 /***********************************************************************
2684  *      CtfImeDispatchDefImeMessage (MSCTFIME.@)
2685  *
2686  * @implemented
2687  */
2688 EXTERN_C LRESULT WINAPI
2689 CtfImeDispatchDefImeMessage(
2690     _In_ HWND hWnd,
2691     _In_ UINT uMsg,
2692     _In_ WPARAM wParam,
2693     _In_ LPARAM lParam)
2694 {
2695     TRACE("(%p, %u, %p, %p)\n", hWnd, uMsg, wParam, lParam);
2696 
2697     TLS *pTLS = TLS::GetTLS();
2698     if (pTLS)
2699     {
2700         if (uMsg == WM_CREATE)
2701             ++pTLS->m_cWnds;
2702         else if (uMsg == WM_DESTROY)
2703             --pTLS->m_cWnds;
2704     }
2705 
2706     if (!IsMsImeMessage(uMsg))
2707         return 0;
2708 
2709     HKL hKL = GetKeyboardLayout(0);
2710     if (IS_IME_HKL(hKL))
2711         return 0;
2712 
2713     HWND hImeWnd = (HWND)SendMessageW(hWnd, WM_IME_NOTIFY, 0x17, 0);
2714     if (!IsWindow(hImeWnd))
2715         return 0;
2716 
2717     return SendMessageW(hImeWnd, uMsg, wParam, lParam);
2718 }
2719 
2720 EXTERN_C BOOL WINAPI
2721 CtfImeIsIME(
2722     _In_ HKL hKL)
2723 {
2724     FIXME("stub:(%p)\n", hKL);
2725     return FALSE;
2726 }
2727 
2728 /**
2729  * @implemented
2730  */
2731 EXTERN_C HRESULT WINAPI
2732 CtfImeThreadDetach(VOID)
2733 {
2734     ImeDestroy(0);
2735     return S_OK;
2736 }
2737 
2738 /**
2739  * @unimplemented
2740  */
2741 EXTERN_C LRESULT CALLBACK
2742 UIWndProc(
2743     _In_ HWND hWnd,
2744     _In_ UINT uMsg,
2745     _In_ WPARAM wParam,
2746     _In_ LPARAM lParam)
2747 {
2748     if (uMsg == WM_CREATE)
2749     {
2750         FIXME("stub\n");
2751         return -1;
2752     }
2753     return 0;
2754 }
2755 
2756 /**
2757  * @unimplemented
2758  */
2759 BOOL RegisterImeClass(VOID)
2760 {
2761     WNDCLASSEXW wcx;
2762 
2763     if (!GetClassInfoExW(g_hInst, L"MSCTFIME UI", &wcx))
2764     {
2765         ZeroMemory(&wcx, sizeof(wcx));
2766         wcx.cbSize          = sizeof(WNDCLASSEXW);
2767         wcx.cbWndExtra      = sizeof(DWORD) * 2;
2768         wcx.hIcon           = LoadIconW(0, (LPCWSTR)IDC_ARROW);
2769         wcx.hInstance       = g_hInst;
2770         wcx.hCursor         = LoadCursorW(NULL, (LPCWSTR)IDC_ARROW);
2771         wcx.hbrBackground   = (HBRUSH)GetStockObject(NULL_BRUSH);
2772         wcx.style           = CS_IME | CS_GLOBALCLASS;
2773         wcx.lpfnWndProc     = UIWndProc;
2774         wcx.lpszClassName   = L"MSCTFIME UI";
2775         if (!RegisterClassExW(&wcx))
2776             return FALSE;
2777     }
2778 
2779     if (!GetClassInfoExW(g_hInst, L"MSCTFIME Composition", &wcx))
2780     {
2781         ZeroMemory(&wcx, sizeof(wcx));
2782         wcx.cbSize          = sizeof(WNDCLASSEXW);
2783         wcx.cbWndExtra      = sizeof(DWORD);
2784         wcx.hIcon           = NULL;
2785         wcx.hInstance       = g_hInst;
2786         wcx.hCursor         = LoadCursorW(NULL, (LPCWSTR)IDC_IBEAM);
2787         wcx.hbrBackground   = (HBRUSH)GetStockObject(NULL_BRUSH);
2788         wcx.style           = CS_IME | CS_HREDRAW | CS_VREDRAW;
2789         //wcx.lpfnWndProc     = UIComposition::CompWndProc; // FIXME
2790         wcx.lpszClassName   = L"MSCTFIME Composition";
2791         if (!RegisterClassExW(&wcx))
2792             return FALSE;
2793     }
2794 
2795     return TRUE;
2796 }
2797 
2798 /**
2799  * @implemented
2800  */
2801 VOID UnregisterImeClass(VOID)
2802 {
2803     WNDCLASSEXW wcx;
2804 
2805     GetClassInfoExW(g_hInst, L"MSCTFIME UI", &wcx);
2806     UnregisterClassW(L"MSCTFIME UI", g_hInst);
2807     DestroyIcon(wcx.hIcon);
2808     DestroyIcon(wcx.hIconSm);
2809 
2810     GetClassInfoExW(g_hInst, L"MSCTFIME Composition", &wcx);
2811     UnregisterClassW(L"MSCTFIME Composition", g_hInst);
2812     DestroyIcon(wcx.hIcon);
2813     DestroyIcon(wcx.hIconSm);
2814 }
2815 
2816 /**
2817  * @implemented
2818  */
2819 BOOL AttachIME(VOID)
2820 {
2821     return RegisterImeClass() && RegisterMSIMEMessage();
2822 }
2823 
2824 /**
2825  * @implemented
2826  */
2827 VOID DetachIME(VOID)
2828 {
2829     UnregisterImeClass();
2830 }
2831 
2832 /**
2833  * @unimplemented
2834  */
2835 BOOL ProcessAttach(HINSTANCE hinstDLL)
2836 {
2837     g_hInst = hinstDLL;
2838 
2839     InitializeCriticalSectionAndSpinCount(&g_csLock, 0);
2840 
2841     if (!TLS::Initialize())
2842         return FALSE;
2843 
2844     g_dwOSInfo = cicGetOSInfo();
2845 
2846     // FIXME
2847 
2848     gfTFInitLib = TRUE;
2849     return AttachIME();
2850 }
2851 
2852 /**
2853  * @unimplemented
2854  */
2855 VOID ProcessDetach(HINSTANCE hinstDLL)
2856 {
2857     // FIXME
2858 
2859     if (gfTFInitLib)
2860         DetachIME();
2861 
2862     DeleteCriticalSection(&g_csLock);
2863     TLS::InternalDestroyTLS();
2864     TLS::Uninitialize();
2865 
2866     // FIXME
2867 }
2868 
2869 /**
2870  * @implemented
2871  */
2872 EXTERN_C BOOL WINAPI
2873 DllMain(
2874     _In_ HINSTANCE hinstDLL,
2875     _In_ DWORD dwReason,
2876     _Inout_opt_ LPVOID lpvReserved)
2877 {
2878     switch (dwReason)
2879     {
2880         case DLL_PROCESS_ATTACH:
2881         {
2882             TRACE("(%p, %lu, %p)\n", hinstDLL, dwReason, lpvReserved);
2883             if (!ProcessAttach(hinstDLL))
2884             {
2885                 ProcessDetach(hinstDLL);
2886                 return FALSE;
2887             }
2888             break;
2889         }
2890         case DLL_PROCESS_DETACH:
2891         {
2892             ProcessDetach(hinstDLL);
2893             break;
2894         }
2895         case DLL_THREAD_DETACH:
2896         {
2897             TF_DllDetachInOther();
2898             CtfImeThreadDetach();
2899             TLS::InternalDestroyTLS();
2900             break;
2901         }
2902     }
2903     return TRUE;
2904 }
2905