/* * MSCTF Server DLL * * Copyright 2008 Aric Stewart, CodeWeavers * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #define COBJMACROS #include "wine/debug.h" #include "windef.h" #include "winbase.h" #include "winreg.h" #include "shlwapi.h" #include "shlguid.h" #include "comcat.h" #include "olectl.h" #include "rpcproxy.h" #include "msctf.h" #include "inputscope.h" #include "msctf_internal.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; DWORD tlsIndex = 0; TfClientId processId = 0; ITfCompartmentMgr *globalCompartmentMgr = NULL; const WCHAR szwSystemTIPKey[] = {'S','O','F','T','W','A','R','E','\\','M','i','c','r','o','s','o','f','t','\\','C','T','F','\\','T','I','P',0}; const WCHAR szwSystemCTFKey[] = {'S','O','F','T','W','A','R','E','\\','M','i','c','r','o','s','o','f','t','\\','C','T','F',0}; typedef HRESULT (*LPFNCONSTRUCTOR)(IUnknown *pUnkOuter, IUnknown **ppvOut); static const struct { REFCLSID 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} }; typedef struct tagClassFactory { IClassFactory IClassFactory_iface; LONG ref; LPFNCONSTRUCTOR ctor; } ClassFactory; static inline ClassFactory *impl_from_IClassFactory(IClassFactory *iface) { return CONTAINING_RECORD(iface, ClassFactory, IClassFactory_iface); } static void ClassFactory_Destructor(ClassFactory *This) { TRACE("Destroying class factory %p\n", This); HeapFree(GetProcessHeap(),0,This); } static HRESULT WINAPI ClassFactory_QueryInterface(IClassFactory *iface, REFIID riid, LPVOID *ppvOut) { *ppvOut = NULL; if (IsEqualIID(riid, &IID_IClassFactory) || IsEqualIID(riid, &IID_IUnknown)) { IClassFactory_AddRef(iface); *ppvOut = iface; return S_OK; } WARN("Unknown interface %s\n", debugstr_guid(riid)); return E_NOINTERFACE; } static ULONG WINAPI ClassFactory_AddRef(IClassFactory *iface) { ClassFactory *This = impl_from_IClassFactory(iface); return InterlockedIncrement(&This->ref); } static ULONG WINAPI ClassFactory_Release(IClassFactory *iface) { ClassFactory *This = impl_from_IClassFactory(iface); ULONG ret = InterlockedDecrement(&This->ref); if (ret == 0) ClassFactory_Destructor(This); return ret; } static HRESULT WINAPI ClassFactory_CreateInstance(IClassFactory *iface, IUnknown *punkOuter, REFIID iid, LPVOID *ppvOut) { ClassFactory *This = impl_from_IClassFactory(iface); HRESULT ret; IUnknown *obj; TRACE("(%p, %p, %s, %p)\n", iface, punkOuter, debugstr_guid(iid), ppvOut); ret = This->ctor(punkOuter, &obj); if (FAILED(ret)) return ret; ret = IUnknown_QueryInterface(obj, iid, ppvOut); IUnknown_Release(obj); return ret; } static HRESULT WINAPI ClassFactory_LockServer(IClassFactory *iface, BOOL fLock) { ClassFactory *This = impl_from_IClassFactory(iface); TRACE("(%p)->(%x)\n", This, fLock); return S_OK; } static const IClassFactoryVtbl ClassFactoryVtbl = { /* IUnknown */ ClassFactory_QueryInterface, ClassFactory_AddRef, ClassFactory_Release, /* IClassFactory*/ ClassFactory_CreateInstance, ClassFactory_LockServer }; static HRESULT ClassFactory_Constructor(LPFNCONSTRUCTOR ctor, LPVOID *ppvOut) { ClassFactory *This = HeapAlloc(GetProcessHeap(),0,sizeof(ClassFactory)); This->IClassFactory_iface.lpVtbl = &ClassFactoryVtbl; This->ref = 1; This->ctor = ctor; *ppvOut = &This->IClassFactory_iface; TRACE("Created class factory %p\n", This); return S_OK; } /************************************************************************* * DWORD Cookie Management */ DWORD 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 = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(CookieInternal) * 10); if (!cookies) { ERR("Out of memory, Unable to alloc cookies array\n"); return 0; } array_size = 10; } else { CookieInternal *new_cookies = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cookies, sizeof(CookieInternal) * (array_size * 2)); 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; } DWORD 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; } LPVOID 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; } LPVOID 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; } DWORD 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; } HRESULT advise_sink(struct list *sink_list, REFIID riid, DWORD cookie_magic, IUnknown *unk, DWORD *cookie) { Sink *sink; sink = HeapAlloc(GetProcessHeap(), 0, sizeof(*sink)); if (!sink) return E_OUTOFMEMORY; if (FAILED(IUnknown_QueryInterface(unk, riid, (void**)&sink->interfaces.pIUnknown))) { HeapFree(GetProcessHeap(), 0, 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); IUnknown_Release(sink->interfaces.pIUnknown); HeapFree(GetProcessHeap(), 0, sink); } HRESULT unadvise_sink(DWORD cookie) { Sink *sink; sink = remove_Cookie(cookie); if (!sink) return CONNECT_E_NOCONNECTION; free_sink(sink); return S_OK; } void 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 = ITfTextInputProcessor_Activate(actsvr->pITfTextInputProcessor, (ITfThreadMgr *)tm, actsvr->tid); if (FAILED(hr)) { ITfTextInputProcessor_Release(actsvr->pITfTextInputProcessor); actsvr->pITfTextInputProcessor = NULL; return hr; } actsvr->pITfThreadMgrEx = tm; ITfThreadMgrEx_AddRef(tm); return hr; } static HRESULT deactivate_given_ts(ActivatedTextService *actsvr) { HRESULT hr = S_OK; if (actsvr->pITfTextInputProcessor) { hr = ITfTextInputProcessor_Deactivate(actsvr->pITfTextInputProcessor); ITfTextInputProcessor_Release(actsvr->pITfTextInputProcessor); ITfThreadMgrEx_Release(actsvr->pITfThreadMgrEx); 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 (IsEqualCLSID(catid,&ats->ats->LanguageProfile.catid)) { deactivate_given_ts(ats->ats); list_remove(&ats->entry); HeapFree(GetProcessHeap(),0,ats->ats); HeapFree(GetProcessHeap(),0,ats); /* we are guaranteeing there is only 1 */ break; } } } HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) { ActivatedTextService *actsvr; ITfCategoryMgr *catmgr; AtsEntry *entry; ITfThreadMgrEx *tm = TlsGetValue(tlsIndex); ITfClientId *clientid; if (!tm) return E_UNEXPECTED; actsvr = HeapAlloc(GetProcessHeap(),0,sizeof(ActivatedTextService)); if (!actsvr) return E_OUTOFMEMORY; ITfThreadMgrEx_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid); ITfClientId_GetClientId(clientid, &lp->clsid, &actsvr->tid); ITfClientId_Release(clientid); if (!actsvr->tid) { HeapFree(GetProcessHeap(),0,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}; ITfCategoryMgr_FindClosestCategory(catmgr, &actsvr->LanguageProfile.clsid, &actsvr->LanguageProfile.catid, list, 3); ITfCategoryMgr_Release(catmgr); } else { ERR("CategoryMgr construction failed\n"); actsvr->LanguageProfile.catid = GUID_NULL; } if (!IsEqualGUID(&actsvr->LanguageProfile.catid,&GUID_NULL)) deactivate_remove_conflicting_ts(&actsvr->LanguageProfile.catid); if (activated > 0) activate_given_ts(actsvr, tm); entry = HeapAlloc(GetProcessHeap(),0,sizeof(AtsEntry)); if (!entry) { HeapFree(GetProcessHeap(),0,actsvr); return E_OUTOFMEMORY; } entry->ats = actsvr; list_add_head(&AtsList, &entry->entry); return S_OK; } BOOL get_active_textservice(REFCLSID rclsid, TF_LANGUAGEPROFILE *profile) { AtsEntry *ats; LIST_FOR_EACH_ENTRY(ats, &AtsList, AtsEntry, entry) { if (IsEqualCLSID(rclsid,&ats->ats->LanguageProfile.clsid)) { if (profile) *profile = ats->ats->LanguageProfile; return TRUE; } } return FALSE; } HRESULT 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; } HRESULT 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; } CLSID 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; } HRESULT get_textservice_sink(TfClientId tid, REFCLSID iid, IUnknown **sink) { AtsEntry *ats; if (!IsEqualCLSID(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; } HRESULT set_textservice_sink(TfClientId tid, REFCLSID iid, IUnknown* sink) { AtsEntry *ats; if (!IsEqualCLSID(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_WINE_PREATTACH: return FALSE; /* prefer native version */ case DLL_PROCESS_ATTACH: MSCTF_hinstance = hinst; tlsIndex = TlsAlloc(); break; case DLL_PROCESS_DETACH: if (fImpLoad) break; TlsFree(tlsIndex); 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 (!IsEqualIID(iid, &IID_IUnknown) && !IsEqualIID(iid, &IID_IClassFactory)) return E_NOINTERFACE; for (i = 0; ClassesTable[i].clsid != NULL; i++) if (IsEqualCLSID(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 = TlsGetValue(tlsIndex); if (*pptim) ITfThreadMgr_AddRef(*pptim); 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); } HRESULT WINAPI TF_CreateLangBarItemMgr(ITfLangBarItemMgr **pplbim) { FIXME("stub %p\n", pplbim); *pplbim = NULL; return E_NOTIMPL; } /*********************************************************************** * TF_InitMlngInfo (MSCTF.@) */ HRESULT WINAPI TF_InitMlngInfo(void) { FIXME("stub\n"); return S_OK; }