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