Reactos
at master 684 lines 17 kB view raw
1/* 2 * PROJECT: ReactOS CTF 3 * LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later) 4 * PURPOSE: MSCTF Server DLL 5 * COPYRIGHT: Copyright 2008 Aric Stewart, CodeWeavers 6 * Copyright 2025 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com> 7 */ 8 9#include "precomp.h" 10 11#include <wine/debug.h> 12WINE_DEFAULT_DEBUG_CHANNEL(msctf); 13 14static HINSTANCE MSCTF_hinstance; 15 16typedef struct 17{ 18 DWORD id; 19 DWORD magic; 20 LPVOID data; 21} CookieInternal; 22 23typedef struct { 24 TF_LANGUAGEPROFILE LanguageProfile; 25 ITfTextInputProcessor *pITfTextInputProcessor; 26 ITfThreadMgrEx *pITfThreadMgrEx; 27 ITfKeyEventSink *pITfKeyEventSink; 28 TfClientId tid; 29} ActivatedTextService; 30 31typedef struct 32{ 33 struct list entry; 34 ActivatedTextService *ats; 35} AtsEntry; 36 37static CookieInternal *cookies; 38static UINT id_last; 39static UINT array_size; 40 41static struct list AtsList = LIST_INIT(AtsList); 42static UINT activated = 0; 43 44TfClientId g_processId = 0; 45ITfCompartmentMgr *g_globalCompartmentMgr = NULL; 46 47typedef HRESULT (*LPFNCONSTRUCTOR)(IUnknown *pUnkOuter, IUnknown **ppvOut); 48 49static const struct { 50 const CLSID *clsid; 51 LPFNCONSTRUCTOR ctor; 52} ClassesTable[] = { 53 {&CLSID_TF_ThreadMgr, ThreadMgr_Constructor}, 54 {&CLSID_TF_InputProcessorProfiles, InputProcessorProfiles_Constructor}, 55 {&CLSID_TF_CategoryMgr, CategoryMgr_Constructor}, 56 {&CLSID_TF_LangBarMgr, LangBarMgr_Constructor}, 57 {&CLSID_TF_DisplayAttributeMgr, DisplayAttributeMgr_Constructor}, 58 {NULL, NULL} 59}; 60 61class CClassFactory 62 : public IClassFactory 63{ 64public: 65 CClassFactory(LPFNCONSTRUCTOR ctor); 66 virtual ~CClassFactory(); 67 68 // ** IUnknown methods ** 69 STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj) override; 70 STDMETHODIMP_(ULONG) AddRef() override; 71 STDMETHODIMP_(ULONG) Release() override; 72 73 // ** IClassFactory methods ** 74 STDMETHODIMP CreateInstance( 75 _In_ IUnknown *pUnkOuter, 76 _In_ REFIID riid, 77 _Out_ void **ppvObject) override; 78 STDMETHODIMP LockServer(_In_ BOOL fLock) override; 79 80protected: 81 LONG m_cRefs; 82 LPFNCONSTRUCTOR m_ctor; 83}; 84 85CClassFactory::CClassFactory(LPFNCONSTRUCTOR ctor) 86 : m_cRefs(1) 87 , m_ctor(ctor) 88{ 89} 90 91CClassFactory::~CClassFactory() 92{ 93 TRACE("Destroying class factory %p\n", this); 94} 95 96STDMETHODIMP CClassFactory::QueryInterface(REFIID riid, void **ppvObj) 97{ 98 *ppvObj = NULL; 99 if (riid == IID_IClassFactory || riid == IID_IUnknown) 100 { 101 AddRef(); 102 *ppvObj = static_cast<IClassFactory *>(this); 103 return S_OK; 104 } 105 106 WARN("Unknown interface %s\n", debugstr_guid(&riid)); 107 return E_NOINTERFACE; 108} 109 110STDMETHODIMP_(ULONG) CClassFactory::AddRef() 111{ 112 return ::InterlockedIncrement(&m_cRefs); 113} 114 115STDMETHODIMP_(ULONG) CClassFactory::Release() 116{ 117 ULONG ret = InterlockedDecrement(&m_cRefs); 118 if (!ret) 119 delete this; 120 return ret; 121} 122 123STDMETHODIMP CClassFactory::CreateInstance( 124 _In_ IUnknown *pUnkOuter, 125 _In_ REFIID riid, 126 _Out_ void **ppvObject) 127{ 128 TRACE("(%p, %p, %s, %p)\n", this, pUnkOuter, debugstr_guid(&riid), ppvObject); 129 130 IUnknown *obj; 131 HRESULT ret = m_ctor(pUnkOuter, &obj); 132 if (FAILED(ret)) 133 return ret; 134 ret = obj->QueryInterface(riid, ppvObject); 135 obj->Release(); 136 return ret; 137} 138 139STDMETHODIMP CClassFactory::LockServer(_In_ BOOL fLock) 140{ 141 TRACE("(%p)->(%x)\n", this, fLock); 142 return S_OK; 143} 144 145static HRESULT ClassFactory_Constructor(LPFNCONSTRUCTOR ctor, LPVOID *ppvOut) 146{ 147 CClassFactory *This = new(cicNoThrow) CClassFactory(ctor); 148 *ppvOut = static_cast<IClassFactory *>(This); 149 TRACE("Created class factory %p\n", This); 150 return S_OK; 151} 152 153/************************************************************************* 154 * DWORD Cookie Management 155 */ 156EXTERN_C 157DWORD generate_Cookie(DWORD magic, LPVOID data) 158{ 159 UINT i; 160 161 /* try to reuse IDs if possible */ 162 for (i = 0; i < id_last; i++) 163 if (cookies[i].id == 0) break; 164 165 if (i == array_size) 166 { 167 if (!array_size) 168 { 169 cookies = (CookieInternal *)cicMemAllocClear(10 * sizeof(CookieInternal)); 170 if (!cookies) 171 { 172 ERR("Out of memory, Unable to alloc cookies array\n"); 173 return 0; 174 } 175 array_size = 10; 176 } 177 else 178 { 179 ERR("cookies: %p, array_size: %d\n", cookies, array_size); 180 CookieInternal *new_cookies = (CookieInternal *) 181 cicMemReCalloc(cookies, array_size * 2, sizeof(CookieInternal)); 182 if (!new_cookies) 183 { 184 ERR("Out of memory, Unable to realloc cookies array\n"); 185 return 0; 186 } 187 cookies = new_cookies; 188 array_size *= 2; 189 } 190 } 191 192 cookies[i].id = i + 1; /* a return of 0 is used for failure */ 193 cookies[i].magic = magic; 194 cookies[i].data = data; 195 196 if (i == id_last) 197 id_last++; 198 199 return cookies[i].id; 200} 201 202EXTERN_C 203DWORD get_Cookie_magic(DWORD id) 204{ 205 UINT index = id - 1; 206 207 if (index >= id_last) 208 return 0; 209 210 if (cookies[index].id == 0) 211 return 0; 212 213 return cookies[index].magic; 214} 215 216EXTERN_C 217LPVOID get_Cookie_data(DWORD id) 218{ 219 UINT index = id - 1; 220 221 if (index >= id_last) 222 return NULL; 223 224 if (cookies[index].id == 0) 225 return NULL; 226 227 return cookies[index].data; 228} 229 230EXTERN_C 231LPVOID remove_Cookie(DWORD id) 232{ 233 UINT index = id - 1; 234 235 if (index >= id_last) 236 return NULL; 237 238 if (cookies[index].id == 0) 239 return NULL; 240 241 cookies[index].id = 0; 242 return cookies[index].data; 243} 244 245EXTERN_C 246DWORD enumerate_Cookie(DWORD magic, DWORD *index) 247{ 248 unsigned int i; 249 for (i = *index; i < id_last; i++) 250 if (cookies[i].id != 0 && cookies[i].magic == magic) 251 { 252 *index = (i+1); 253 return cookies[i].id; 254 } 255 return 0x0; 256} 257 258EXTERN_C 259HRESULT advise_sink(struct list *sink_list, REFIID riid, DWORD cookie_magic, IUnknown *unk, DWORD *cookie) 260{ 261 Sink *sink = (Sink *)cicMemAlloc(sizeof(*sink)); 262 if (!sink) 263 return E_OUTOFMEMORY; 264 265 HRESULT hr = unk->QueryInterface(riid, (void **)&sink->interfaces.pIUnknown); 266 if (FAILED(hr)) 267 { 268 cicMemFree(sink); 269 return CONNECT_E_CANNOTCONNECT; 270 } 271 272 list_add_head(sink_list, &sink->entry); 273 *cookie = generate_Cookie(cookie_magic, sink); 274 TRACE("cookie %x\n", *cookie); 275 return S_OK; 276} 277 278static void free_sink(Sink *sink) 279{ 280 list_remove(&sink->entry); 281 sink->interfaces.pIUnknown->Release(); 282 cicMemFree(sink); 283} 284 285EXTERN_C 286HRESULT unadvise_sink(DWORD cookie) 287{ 288 Sink *sink = (Sink *)remove_Cookie(cookie); 289 if (!sink) 290 return CONNECT_E_NOCONNECTION; 291 292 free_sink(sink); 293 return S_OK; 294} 295 296EXTERN_C 297void free_sinks(struct list *sink_list) 298{ 299 while(!list_empty(sink_list)) 300 { 301 Sink* sink = LIST_ENTRY(sink_list->next, Sink, entry); 302 free_sink(sink); 303 } 304} 305 306/***************************************************************************** 307 * Active Text Service Management 308 *****************************************************************************/ 309static HRESULT activate_given_ts(ActivatedTextService *actsvr, ITfThreadMgrEx *tm) 310{ 311 HRESULT hr; 312 313 /* Already Active? */ 314 if (actsvr->pITfTextInputProcessor) 315 return S_OK; 316 317 hr = CoCreateInstance(actsvr->LanguageProfile.clsid, NULL, CLSCTX_INPROC_SERVER, 318 IID_ITfTextInputProcessor, (void **)&actsvr->pITfTextInputProcessor); 319 if (FAILED(hr)) return hr; 320 321 hr = actsvr->pITfTextInputProcessor->Activate((ITfThreadMgr *)tm, actsvr->tid); 322 if (FAILED(hr)) 323 { 324 actsvr->pITfTextInputProcessor->Release(); 325 actsvr->pITfTextInputProcessor = NULL; 326 return hr; 327 } 328 329 actsvr->pITfThreadMgrEx = tm; 330 tm->AddRef(); 331 return hr; 332} 333 334static HRESULT deactivate_given_ts(ActivatedTextService *actsvr) 335{ 336 HRESULT hr = S_OK; 337 338 if (actsvr->pITfTextInputProcessor) 339 { 340 hr = actsvr->pITfTextInputProcessor->Deactivate(); 341 actsvr->pITfTextInputProcessor->Release(); 342 actsvr->pITfThreadMgrEx->Release(); 343 actsvr->pITfTextInputProcessor = NULL; 344 actsvr->pITfThreadMgrEx = NULL; 345 } 346 347 return hr; 348} 349 350static void deactivate_remove_conflicting_ts(REFCLSID catid) 351{ 352 AtsEntry *ats, *cursor2; 353 354 LIST_FOR_EACH_ENTRY_SAFE(ats, cursor2, &AtsList, AtsEntry, entry) 355 { 356 if (catid == ats->ats->LanguageProfile.catid) 357 { 358 deactivate_given_ts(ats->ats); 359 list_remove(&ats->entry); 360 cicMemFree(ats->ats); 361 cicMemFree(ats); 362 /* we are guaranteeing there is only 1 */ 363 break; 364 } 365 } 366} 367 368EXTERN_C 369HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp) 370{ 371 ActivatedTextService *actsvr; 372 ITfCategoryMgr *catmgr; 373 AtsEntry *entry; 374 ITfThreadMgrEx *tm = (ITfThreadMgrEx *)TlsGetValue(g_dwTLSIndex); 375 ITfClientId *clientid; 376 377 if (!tm) 378 return E_UNEXPECTED; 379 380 actsvr = (ActivatedTextService *)cicMemAlloc(sizeof(ActivatedTextService)); 381 if (!actsvr) 382 return E_OUTOFMEMORY; 383 384 tm->QueryInterface(IID_ITfClientId, (void **)&clientid); 385 clientid->GetClientId(lp->clsid, &actsvr->tid); 386 clientid->Release(); 387 388 if (!actsvr->tid) 389 { 390 cicMemFree(actsvr); 391 return E_OUTOFMEMORY; 392 } 393 394 actsvr->pITfTextInputProcessor = NULL; 395 actsvr->LanguageProfile = *lp; 396 actsvr->pITfKeyEventSink = NULL; 397 398 /* get TIP category */ 399 if (SUCCEEDED(CategoryMgr_Constructor(NULL, (IUnknown**)&catmgr))) 400 { 401 static const GUID *list[3] = {&GUID_TFCAT_TIP_SPEECH, &GUID_TFCAT_TIP_KEYBOARD, &GUID_TFCAT_TIP_HANDWRITING}; 402 403 catmgr->FindClosestCategory(actsvr->LanguageProfile.clsid, &actsvr->LanguageProfile.catid, list, 3); 404 catmgr->Release(); 405 } 406 else 407 { 408 ERR("CategoryMgr construction failed\n"); 409 actsvr->LanguageProfile.catid = GUID_NULL; 410 } 411 412 if (actsvr->LanguageProfile.catid != GUID_NULL) 413 deactivate_remove_conflicting_ts(actsvr->LanguageProfile.catid); 414 415 if (activated > 0) 416 activate_given_ts(actsvr, tm); 417 418 entry = (AtsEntry *)cicMemAlloc(sizeof(AtsEntry)); 419 if (!entry) 420 { 421 cicMemFree(actsvr); 422 return E_OUTOFMEMORY; 423 } 424 425 entry->ats = actsvr; 426 list_add_head(&AtsList, &entry->entry); 427 428 return S_OK; 429} 430 431EXTERN_C 432BOOL get_active_textservice(REFCLSID rclsid, TF_LANGUAGEPROFILE *profile) 433{ 434 AtsEntry *ats; 435 436 LIST_FOR_EACH_ENTRY(ats, &AtsList, AtsEntry, entry) 437 { 438 if (rclsid == ats->ats->LanguageProfile.clsid) 439 { 440 if (profile) 441 *profile = ats->ats->LanguageProfile; 442 return TRUE; 443 } 444 } 445 return FALSE; 446} 447 448EXTERN_C 449HRESULT activate_textservices(ITfThreadMgrEx *tm) 450{ 451 HRESULT hr = S_OK; 452 AtsEntry *ats; 453 454 activated ++; 455 if (activated > 1) 456 return S_OK; 457 458 LIST_FOR_EACH_ENTRY(ats, &AtsList, AtsEntry, entry) 459 { 460 hr = activate_given_ts(ats->ats, tm); 461 if (FAILED(hr)) 462 FIXME("Failed to activate text service\n"); 463 } 464 return hr; 465} 466 467EXTERN_C 468HRESULT deactivate_textservices(void) 469{ 470 AtsEntry *ats; 471 472 if (activated > 0) 473 activated --; 474 475 if (activated == 0) 476 { 477 LIST_FOR_EACH_ENTRY(ats, &AtsList, AtsEntry, entry) 478 deactivate_given_ts(ats->ats); 479 } 480 return S_OK; 481} 482 483EXTERN_C 484CLSID get_textservice_clsid(TfClientId tid) 485{ 486 AtsEntry *ats; 487 488 LIST_FOR_EACH_ENTRY(ats, &AtsList, AtsEntry, entry) 489 { 490 if (ats->ats->tid == tid) 491 return ats->ats->LanguageProfile.clsid; 492 } 493 return GUID_NULL; 494} 495 496EXTERN_C 497HRESULT get_textservice_sink(TfClientId tid, REFCLSID iid, IUnknown **sink) 498{ 499 AtsEntry *ats; 500 501 if (iid != IID_ITfKeyEventSink) 502 return E_NOINTERFACE; 503 504 LIST_FOR_EACH_ENTRY(ats, &AtsList, AtsEntry, entry) 505 { 506 if (ats->ats->tid == tid) 507 { 508 *sink = (IUnknown*)ats->ats->pITfKeyEventSink; 509 return S_OK; 510 } 511 } 512 513 return E_FAIL; 514} 515 516EXTERN_C 517HRESULT set_textservice_sink(TfClientId tid, REFCLSID iid, IUnknown* sink) 518{ 519 AtsEntry *ats; 520 521 if (iid != IID_ITfKeyEventSink) 522 return E_NOINTERFACE; 523 524 LIST_FOR_EACH_ENTRY(ats, &AtsList, AtsEntry, entry) 525 { 526 if (ats->ats->tid == tid) 527 { 528 ats->ats->pITfKeyEventSink = (ITfKeyEventSink*)sink; 529 return S_OK; 530 } 531 } 532 533 return E_FAIL; 534} 535 536/************************************************************************* 537 * MSCTF DllMain 538 */ 539BOOL WINAPI DllMain(HINSTANCE hinst, DWORD fdwReason, LPVOID fImpLoad) 540{ 541 TRACE("%p 0x%x %p\n", hinst, fdwReason, fImpLoad); 542 switch (fdwReason) 543 { 544 case DLL_PROCESS_ATTACH: 545 MSCTF_hinstance = hinst; 546 return ProcessAttach(hinst); 547 548 case DLL_PROCESS_DETACH: 549 ProcessDetach(hinst); 550 break; 551 } 552 return TRUE; 553} 554 555/************************************************************************* 556 * DllCanUnloadNow (MSCTF.@) 557 */ 558HRESULT WINAPI DllCanUnloadNow(void) 559{ 560 return S_FALSE; 561} 562 563/*********************************************************************** 564 * DllGetClassObject (MSCTF.@) 565 */ 566HRESULT WINAPI DllGetClassObject(REFCLSID clsid, REFIID iid, LPVOID *ppvOut) 567{ 568 int i; 569 570 *ppvOut = NULL; 571 if (iid != IID_IUnknown && iid != IID_IClassFactory) 572 return E_NOINTERFACE; 573 574 for (i = 0; ClassesTable[i].clsid; i++) 575 { 576 if (*ClassesTable[i].clsid == clsid) 577 return ClassFactory_Constructor(ClassesTable[i].ctor, ppvOut); 578 } 579 FIXME("CLSID %s not supported\n", debugstr_guid(&clsid)); 580 return CLASS_E_CLASSNOTAVAILABLE; 581} 582 583/*********************************************************************** 584 * DllRegisterServer (MSCTF.@) 585 */ 586HRESULT WINAPI DllRegisterServer(void) 587{ 588 return __wine_register_resources( MSCTF_hinstance ); 589} 590 591/*********************************************************************** 592 * DllUnregisterServer (MSCTF.@) 593 */ 594HRESULT WINAPI DllUnregisterServer(void) 595{ 596 return __wine_unregister_resources( MSCTF_hinstance ); 597} 598 599/*********************************************************************** 600 * TF_CreateThreadMgr (MSCTF.@) 601 */ 602HRESULT WINAPI TF_CreateThreadMgr(ITfThreadMgr **pptim) 603{ 604 TRACE("\n"); 605 return ThreadMgr_Constructor(NULL, (IUnknown**)pptim); 606} 607 608/*********************************************************************** 609 * TF_GetThreadMgr (MSCTF.@) 610 */ 611HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim) 612{ 613 TRACE("\n"); 614 *pptim = (ITfThreadMgr *)TlsGetValue(g_dwTLSIndex); 615 616 if (*pptim) 617 (*pptim)->AddRef(); 618 619 return S_OK; 620} 621 622/*********************************************************************** 623 * SetInputScope(MSCTF.@) 624 */ 625HRESULT WINAPI SetInputScope(HWND hwnd, InputScope inputscope) 626{ 627 FIXME("STUB: %p %i\n", hwnd, inputscope); 628 return S_OK; 629} 630 631/*********************************************************************** 632 * SetInputScopes(MSCTF.@) 633 */ 634HRESULT WINAPI SetInputScopes(HWND hwnd, const InputScope *pInputScopes, 635 UINT cInputScopes, WCHAR **ppszPhraseList, 636 UINT cPhrases, WCHAR *pszRegExp, WCHAR *pszSRGS) 637{ 638 UINT i; 639 FIXME("STUB: %p ... %s %s\n", hwnd, debugstr_w(pszRegExp), debugstr_w(pszSRGS)); 640 for (i = 0; i < cInputScopes; i++) 641 TRACE("\tScope[%u] = %i\n", i, pInputScopes[i]); 642 for (i = 0; i < cPhrases; i++) 643 TRACE("\tPhrase[%u] = %s\n", i, debugstr_w(ppszPhraseList[i])); 644 645 return S_OK; 646} 647 648/*********************************************************************** 649 * TF_CreateInputProcessorProfiles(MSCTF.@) 650 */ 651HRESULT WINAPI TF_CreateInputProcessorProfiles( 652 ITfInputProcessorProfiles **ppipr) 653{ 654 return InputProcessorProfiles_Constructor(NULL, (IUnknown**)ppipr); 655} 656 657/*********************************************************************** 658 * TF_InvalidAssemblyListCacheIfExist(MSCTF.@) 659 */ 660HRESULT WINAPI TF_InvalidAssemblyListCacheIfExist(void) 661{ 662 FIXME("Stub\n"); 663 return S_OK; 664} 665 666/*********************************************************************** 667 * TF_CreateLangBarMgr (MSCTF.@) 668 */ 669HRESULT WINAPI TF_CreateLangBarMgr(ITfLangBarMgr **pppbm) 670{ 671 TRACE("\n"); 672 return LangBarMgr_Constructor(NULL, (IUnknown**)pppbm); 673} 674 675/*********************************************************************** 676 * TF_CreateLangBarItemMgr (MSCTF.@) 677 */ 678HRESULT WINAPI TF_CreateLangBarItemMgr(ITfLangBarItemMgr **pplbim) 679{ 680 FIXME("stub %p\n", pplbim); 681 *pplbim = NULL; 682 683 return E_NOTIMPL; 684}