Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685/* * PROJECT: ReactOS CTF * LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later) * PURPOSE: MSCTF Server DLL * COPYRIGHT: Copyright 2008 Aric Stewart, CodeWeavers * Copyright 2025 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com> */
#include "precomp.h"
#include <wine/debug.h>WINE_DEFAULT_DEBUG_CHANNEL(msctf);
static HINSTANCE MSCTF_hinstance;
typedef struct{ DWORD id; DWORD magic; LPVOID data;} CookieInternal;
typedef struct { TF_LANGUAGEPROFILE LanguageProfile; ITfTextInputProcessor *pITfTextInputProcessor; ITfThreadMgrEx *pITfThreadMgrEx; ITfKeyEventSink *pITfKeyEventSink; TfClientId tid;} ActivatedTextService;
typedef struct{ struct list entry; ActivatedTextService *ats;} AtsEntry;
static CookieInternal *cookies;static UINT id_last;static UINT array_size;
static struct list AtsList = LIST_INIT(AtsList);static UINT activated = 0;
TfClientId g_processId = 0;ITfCompartmentMgr *g_globalCompartmentMgr = NULL;
typedef HRESULT (*LPFNCONSTRUCTOR)(IUnknown *pUnkOuter, IUnknown **ppvOut);
static const struct { const CLSID *clsid; LPFNCONSTRUCTOR ctor;} ClassesTable[] = { {&CLSID_TF_ThreadMgr, ThreadMgr_Constructor}, {&CLSID_TF_InputProcessorProfiles, InputProcessorProfiles_Constructor}, {&CLSID_TF_CategoryMgr, CategoryMgr_Constructor}, {&CLSID_TF_LangBarMgr, LangBarMgr_Constructor}, {&CLSID_TF_DisplayAttributeMgr, DisplayAttributeMgr_Constructor}, {NULL, NULL}};
class CClassFactory : public IClassFactory{public: CClassFactory(LPFNCONSTRUCTOR ctor); virtual ~CClassFactory();
// ** IUnknown methods ** STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj) override; STDMETHODIMP_(ULONG) AddRef() override; STDMETHODIMP_(ULONG) Release() override;
// ** IClassFactory methods ** STDMETHODIMP CreateInstance( _In_ IUnknown *pUnkOuter, _In_ REFIID riid, _Out_ void **ppvObject) override; STDMETHODIMP LockServer(_In_ BOOL fLock) override;
protected: LONG m_cRefs; LPFNCONSTRUCTOR m_ctor;};
CClassFactory::CClassFactory(LPFNCONSTRUCTOR ctor) : m_cRefs(1) , m_ctor(ctor){}
CClassFactory::~CClassFactory(){ TRACE("Destroying class factory %p\n", this);}
STDMETHODIMP CClassFactory::QueryInterface(REFIID riid, void **ppvObj){ *ppvObj = NULL; if (riid == IID_IClassFactory || riid == IID_IUnknown) { AddRef(); *ppvObj = static_cast<IClassFactory *>(this); return S_OK; }
WARN("Unknown interface %s\n", debugstr_guid(&riid)); return E_NOINTERFACE;}
STDMETHODIMP_(ULONG) CClassFactory::AddRef(){ return ::InterlockedIncrement(&m_cRefs);}
STDMETHODIMP_(ULONG) CClassFactory::Release(){ ULONG ret = InterlockedDecrement(&m_cRefs); if (!ret) delete this; return ret;}
STDMETHODIMP CClassFactory::CreateInstance( _In_ IUnknown *pUnkOuter, _In_ REFIID riid, _Out_ void **ppvObject){ TRACE("(%p, %p, %s, %p)\n", this, pUnkOuter, debugstr_guid(&riid), ppvObject);
IUnknown *obj; HRESULT ret = m_ctor(pUnkOuter, &obj); if (FAILED(ret)) return ret; ret = obj->QueryInterface(riid, ppvObject); obj->Release(); return ret;}
STDMETHODIMP CClassFactory::LockServer(_In_ BOOL fLock){ TRACE("(%p)->(%x)\n", this, fLock); return S_OK;}
static HRESULT ClassFactory_Constructor(LPFNCONSTRUCTOR ctor, LPVOID *ppvOut){ CClassFactory *This = new(cicNoThrow) CClassFactory(ctor); *ppvOut = static_cast<IClassFactory *>(This); TRACE("Created class factory %p\n", This); return S_OK;}
/************************************************************************* * DWORD Cookie Management */EXTERN_CDWORD generate_Cookie(DWORD magic, LPVOID data){ UINT i;
/* try to reuse IDs if possible */ for (i = 0; i < id_last; i++) if (cookies[i].id == 0) break;
if (i == array_size) { if (!array_size) { cookies = (CookieInternal *)cicMemAllocClear(10 * sizeof(CookieInternal)); if (!cookies) { ERR("Out of memory, Unable to alloc cookies array\n"); return 0; } array_size = 10; } else { ERR("cookies: %p, array_size: %d\n", cookies, array_size); CookieInternal *new_cookies = (CookieInternal *) cicMemReCalloc(cookies, array_size * 2, sizeof(CookieInternal)); if (!new_cookies) { ERR("Out of memory, Unable to realloc cookies array\n"); return 0; } cookies = new_cookies; array_size *= 2; } }
cookies[i].id = i + 1; /* a return of 0 is used for failure */ cookies[i].magic = magic; cookies[i].data = data;
if (i == id_last) id_last++;
return cookies[i].id;}
EXTERN_CDWORD get_Cookie_magic(DWORD id){ UINT index = id - 1;
if (index >= id_last) return 0;
if (cookies[index].id == 0) return 0;
return cookies[index].magic;}
EXTERN_CLPVOID get_Cookie_data(DWORD id){ UINT index = id - 1;
if (index >= id_last) return NULL;
if (cookies[index].id == 0) return NULL;
return cookies[index].data;}
EXTERN_CLPVOID remove_Cookie(DWORD id){ UINT index = id - 1;
if (index >= id_last) return NULL;
if (cookies[index].id == 0) return NULL;
cookies[index].id = 0; return cookies[index].data;}
EXTERN_CDWORD enumerate_Cookie(DWORD magic, DWORD *index){ unsigned int i; for (i = *index; i < id_last; i++) if (cookies[i].id != 0 && cookies[i].magic == magic) { *index = (i+1); return cookies[i].id; } return 0x0;}
EXTERN_CHRESULT advise_sink(struct list *sink_list, REFIID riid, DWORD cookie_magic, IUnknown *unk, DWORD *cookie){ Sink *sink = (Sink *)cicMemAlloc(sizeof(*sink)); if (!sink) return E_OUTOFMEMORY;
HRESULT hr = unk->QueryInterface(riid, (void **)&sink->interfaces.pIUnknown); if (FAILED(hr)) { cicMemFree(sink); return CONNECT_E_CANNOTCONNECT; }
list_add_head(sink_list, &sink->entry); *cookie = generate_Cookie(cookie_magic, sink); TRACE("cookie %x\n", *cookie); return S_OK;}
static void free_sink(Sink *sink){ list_remove(&sink->entry); sink->interfaces.pIUnknown->Release(); cicMemFree(sink);}
EXTERN_CHRESULT unadvise_sink(DWORD cookie){ Sink *sink = (Sink *)remove_Cookie(cookie); if (!sink) return CONNECT_E_NOCONNECTION;
free_sink(sink); return S_OK;}
EXTERN_Cvoid free_sinks(struct list *sink_list){ while(!list_empty(sink_list)) { Sink* sink = LIST_ENTRY(sink_list->next, Sink, entry); free_sink(sink); }}
/***************************************************************************** * Active Text Service Management *****************************************************************************/static HRESULT activate_given_ts(ActivatedTextService *actsvr, ITfThreadMgrEx *tm){ HRESULT hr;
/* Already Active? */ if (actsvr->pITfTextInputProcessor) return S_OK;
hr = CoCreateInstance(actsvr->LanguageProfile.clsid, NULL, CLSCTX_INPROC_SERVER, IID_ITfTextInputProcessor, (void **)&actsvr->pITfTextInputProcessor); if (FAILED(hr)) return hr;
hr = actsvr->pITfTextInputProcessor->Activate((ITfThreadMgr *)tm, actsvr->tid); if (FAILED(hr)) { actsvr->pITfTextInputProcessor->Release(); actsvr->pITfTextInputProcessor = NULL; return hr; }
actsvr->pITfThreadMgrEx = tm; tm->AddRef(); return hr;}
static HRESULT deactivate_given_ts(ActivatedTextService *actsvr){ HRESULT hr = S_OK;
if (actsvr->pITfTextInputProcessor) { hr = actsvr->pITfTextInputProcessor->Deactivate(); actsvr->pITfTextInputProcessor->Release(); actsvr->pITfThreadMgrEx->Release(); actsvr->pITfTextInputProcessor = NULL; actsvr->pITfThreadMgrEx = NULL; }
return hr;}
static void deactivate_remove_conflicting_ts(REFCLSID catid){ AtsEntry *ats, *cursor2;
LIST_FOR_EACH_ENTRY_SAFE(ats, cursor2, &AtsList, AtsEntry, entry) { if (catid == ats->ats->LanguageProfile.catid) { deactivate_given_ts(ats->ats); list_remove(&ats->entry); cicMemFree(ats->ats); cicMemFree(ats); /* we are guaranteeing there is only 1 */ break; } }}
EXTERN_CHRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp){ ActivatedTextService *actsvr; ITfCategoryMgr *catmgr; AtsEntry *entry; ITfThreadMgrEx *tm = (ITfThreadMgrEx *)TlsGetValue(g_dwTLSIndex); ITfClientId *clientid;
if (!tm) return E_UNEXPECTED;
actsvr = (ActivatedTextService *)cicMemAlloc(sizeof(ActivatedTextService)); if (!actsvr) return E_OUTOFMEMORY;
tm->QueryInterface(IID_ITfClientId, (void **)&clientid); clientid->GetClientId(lp->clsid, &actsvr->tid); clientid->Release();
if (!actsvr->tid) { cicMemFree(actsvr); return E_OUTOFMEMORY; }
actsvr->pITfTextInputProcessor = NULL; actsvr->LanguageProfile = *lp; actsvr->pITfKeyEventSink = NULL;
/* get TIP category */ if (SUCCEEDED(CategoryMgr_Constructor(NULL, (IUnknown**)&catmgr))) { static const GUID *list[3] = {&GUID_TFCAT_TIP_SPEECH, &GUID_TFCAT_TIP_KEYBOARD, &GUID_TFCAT_TIP_HANDWRITING};
catmgr->FindClosestCategory(actsvr->LanguageProfile.clsid, &actsvr->LanguageProfile.catid, list, 3); catmgr->Release(); } else { ERR("CategoryMgr construction failed\n"); actsvr->LanguageProfile.catid = GUID_NULL; }
if (actsvr->LanguageProfile.catid != GUID_NULL) deactivate_remove_conflicting_ts(actsvr->LanguageProfile.catid);
if (activated > 0) activate_given_ts(actsvr, tm);
entry = (AtsEntry *)cicMemAlloc(sizeof(AtsEntry)); if (!entry) { cicMemFree(actsvr); return E_OUTOFMEMORY; }
entry->ats = actsvr; list_add_head(&AtsList, &entry->entry);
return S_OK;}
EXTERN_CBOOL get_active_textservice(REFCLSID rclsid, TF_LANGUAGEPROFILE *profile){ AtsEntry *ats;
LIST_FOR_EACH_ENTRY(ats, &AtsList, AtsEntry, entry) { if (rclsid == ats->ats->LanguageProfile.clsid) { if (profile) *profile = ats->ats->LanguageProfile; return TRUE; } } return FALSE;}
EXTERN_CHRESULT activate_textservices(ITfThreadMgrEx *tm){ HRESULT hr = S_OK; AtsEntry *ats;
activated ++; if (activated > 1) return S_OK;
LIST_FOR_EACH_ENTRY(ats, &AtsList, AtsEntry, entry) { hr = activate_given_ts(ats->ats, tm); if (FAILED(hr)) FIXME("Failed to activate text service\n"); } return hr;}
EXTERN_CHRESULT deactivate_textservices(void){ AtsEntry *ats;
if (activated > 0) activated --;
if (activated == 0) { LIST_FOR_EACH_ENTRY(ats, &AtsList, AtsEntry, entry) deactivate_given_ts(ats->ats); } return S_OK;}
EXTERN_CCLSID get_textservice_clsid(TfClientId tid){ AtsEntry *ats;
LIST_FOR_EACH_ENTRY(ats, &AtsList, AtsEntry, entry) { if (ats->ats->tid == tid) return ats->ats->LanguageProfile.clsid; } return GUID_NULL;}
EXTERN_CHRESULT get_textservice_sink(TfClientId tid, REFCLSID iid, IUnknown **sink){ AtsEntry *ats;
if (iid != IID_ITfKeyEventSink) return E_NOINTERFACE;
LIST_FOR_EACH_ENTRY(ats, &AtsList, AtsEntry, entry) { if (ats->ats->tid == tid) { *sink = (IUnknown*)ats->ats->pITfKeyEventSink; return S_OK; } }
return E_FAIL;}
EXTERN_CHRESULT set_textservice_sink(TfClientId tid, REFCLSID iid, IUnknown* sink){ AtsEntry *ats;
if (iid != IID_ITfKeyEventSink) return E_NOINTERFACE;
LIST_FOR_EACH_ENTRY(ats, &AtsList, AtsEntry, entry) { if (ats->ats->tid == tid) { ats->ats->pITfKeyEventSink = (ITfKeyEventSink*)sink; return S_OK; } }
return E_FAIL;}
/************************************************************************* * MSCTF DllMain */BOOL WINAPI DllMain(HINSTANCE hinst, DWORD fdwReason, LPVOID fImpLoad){ TRACE("%p 0x%x %p\n", hinst, fdwReason, fImpLoad); switch (fdwReason) { case DLL_PROCESS_ATTACH: MSCTF_hinstance = hinst; return ProcessAttach(hinst);
case DLL_PROCESS_DETACH: ProcessDetach(hinst); break; } return TRUE;}
/************************************************************************* * DllCanUnloadNow (MSCTF.@) */HRESULT WINAPI DllCanUnloadNow(void){ return S_FALSE;}
/*********************************************************************** * DllGetClassObject (MSCTF.@) */HRESULT WINAPI DllGetClassObject(REFCLSID clsid, REFIID iid, LPVOID *ppvOut){ int i;
*ppvOut = NULL; if (iid != IID_IUnknown && iid != IID_IClassFactory) return E_NOINTERFACE;
for (i = 0; ClassesTable[i].clsid; i++) { if (*ClassesTable[i].clsid == clsid) return ClassFactory_Constructor(ClassesTable[i].ctor, ppvOut); } FIXME("CLSID %s not supported\n", debugstr_guid(&clsid)); return CLASS_E_CLASSNOTAVAILABLE;}
/*********************************************************************** * DllRegisterServer (MSCTF.@) */HRESULT WINAPI DllRegisterServer(void){ return __wine_register_resources( MSCTF_hinstance );}
/*********************************************************************** * DllUnregisterServer (MSCTF.@) */HRESULT WINAPI DllUnregisterServer(void){ return __wine_unregister_resources( MSCTF_hinstance );}
/*********************************************************************** * TF_CreateThreadMgr (MSCTF.@) */HRESULT WINAPI TF_CreateThreadMgr(ITfThreadMgr **pptim){ TRACE("\n"); return ThreadMgr_Constructor(NULL, (IUnknown**)pptim);}
/*********************************************************************** * TF_GetThreadMgr (MSCTF.@) */HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim){ TRACE("\n"); *pptim = (ITfThreadMgr *)TlsGetValue(g_dwTLSIndex);
if (*pptim) (*pptim)->AddRef();
return S_OK;}
/*********************************************************************** * SetInputScope(MSCTF.@) */HRESULT WINAPI SetInputScope(HWND hwnd, InputScope inputscope){ FIXME("STUB: %p %i\n", hwnd, inputscope); return S_OK;}
/*********************************************************************** * SetInputScopes(MSCTF.@) */HRESULT WINAPI SetInputScopes(HWND hwnd, const InputScope *pInputScopes, UINT cInputScopes, WCHAR **ppszPhraseList, UINT cPhrases, WCHAR *pszRegExp, WCHAR *pszSRGS){ UINT i; FIXME("STUB: %p ... %s %s\n", hwnd, debugstr_w(pszRegExp), debugstr_w(pszSRGS)); for (i = 0; i < cInputScopes; i++) TRACE("\tScope[%u] = %i\n", i, pInputScopes[i]); for (i = 0; i < cPhrases; i++) TRACE("\tPhrase[%u] = %s\n", i, debugstr_w(ppszPhraseList[i]));
return S_OK;}
/*********************************************************************** * TF_CreateInputProcessorProfiles(MSCTF.@) */HRESULT WINAPI TF_CreateInputProcessorProfiles( ITfInputProcessorProfiles **ppipr){ return InputProcessorProfiles_Constructor(NULL, (IUnknown**)ppipr);}
/*********************************************************************** * TF_InvalidAssemblyListCacheIfExist(MSCTF.@) */HRESULT WINAPI TF_InvalidAssemblyListCacheIfExist(void){ FIXME("Stub\n"); return S_OK;}
/*********************************************************************** * TF_CreateLangBarMgr (MSCTF.@) */HRESULT WINAPI TF_CreateLangBarMgr(ITfLangBarMgr **pppbm){ TRACE("\n"); return LangBarMgr_Constructor(NULL, (IUnknown**)pppbm);}
/*********************************************************************** * TF_CreateLangBarItemMgr (MSCTF.@) */HRESULT WINAPI TF_CreateLangBarItemMgr(ITfLangBarItemMgr **pplbim){ FIXME("stub %p\n", pplbim); *pplbim = NULL;
return E_NOTIMPL;}