1 /* 2 * ITfCompartmentMgr implementation 3 * 4 * Copyright 2009 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 "msctf_internal.h" 22 23 #include <oleauto.h> 24 25 typedef struct tagCompartmentValue { 26 struct list entry; 27 GUID guid; 28 TfClientId owner; 29 ITfCompartment *compartment; 30 } CompartmentValue; 31 32 typedef struct tagCompartmentMgr { 33 ITfCompartmentMgr ITfCompartmentMgr_iface; 34 LONG refCount; 35 36 IUnknown *pUnkOuter; 37 38 struct list values; 39 } CompartmentMgr; 40 41 typedef struct tagCompartmentEnumGuid { 42 IEnumGUID IEnumGUID_iface; 43 LONG refCount; 44 45 struct list *values; 46 struct list *cursor; 47 } CompartmentEnumGuid; 48 49 typedef struct tagCompartment { 50 ITfCompartment ITfCompartment_iface; 51 ITfSource ITfSource_iface; 52 LONG refCount; 53 54 /* Only VT_I4, VT_UNKNOWN and VT_BSTR data types are allowed */ 55 VARIANT variant; 56 CompartmentValue *valueData; 57 struct list CompartmentEventSink; 58 } Compartment; 59 60 static HRESULT CompartmentEnumGuid_Constructor(struct list* values, IEnumGUID **ppOut); 61 static HRESULT Compartment_Constructor(CompartmentValue *value, ITfCompartment **ppOut); 62 63 static inline CompartmentMgr *impl_from_ITfCompartmentMgr(ITfCompartmentMgr *iface) 64 { 65 return CONTAINING_RECORD(iface, CompartmentMgr, ITfCompartmentMgr_iface); 66 } 67 68 static inline Compartment *impl_from_ITfCompartment(ITfCompartment *iface) 69 { 70 return CONTAINING_RECORD(iface, Compartment, ITfCompartment_iface); 71 } 72 73 static inline Compartment *impl_from_ITfSource(ITfSource *iface) 74 { 75 return CONTAINING_RECORD(iface, Compartment, ITfSource_iface); 76 } 77 78 static inline CompartmentEnumGuid *impl_from_IEnumGUID(IEnumGUID *iface) 79 { 80 return CONTAINING_RECORD(iface, CompartmentEnumGuid, IEnumGUID_iface); 81 } 82 83 HRESULT CompartmentMgr_Destructor(ITfCompartmentMgr *iface) 84 { 85 CompartmentMgr *This = impl_from_ITfCompartmentMgr(iface); 86 struct list *cursor, *cursor2; 87 88 LIST_FOR_EACH_SAFE(cursor, cursor2, &This->values) 89 { 90 CompartmentValue* value = LIST_ENTRY(cursor,CompartmentValue,entry); 91 list_remove(cursor); 92 ITfCompartment_Release(value->compartment); 93 HeapFree(GetProcessHeap(),0,value); 94 } 95 96 HeapFree(GetProcessHeap(),0,This); 97 return S_OK; 98 } 99 100 /***************************************************** 101 * ITfCompartmentMgr functions 102 *****************************************************/ 103 static HRESULT WINAPI CompartmentMgr_QueryInterface(ITfCompartmentMgr *iface, REFIID iid, LPVOID *ppvOut) 104 { 105 CompartmentMgr *This = impl_from_ITfCompartmentMgr(iface); 106 if (This->pUnkOuter) 107 return IUnknown_QueryInterface(This->pUnkOuter, iid, ppvOut); 108 else 109 { 110 *ppvOut = NULL; 111 112 if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ITfCompartmentMgr)) 113 { 114 *ppvOut = &This->ITfCompartmentMgr_iface; 115 } 116 117 if (*ppvOut) 118 { 119 ITfCompartmentMgr_AddRef(iface); 120 return S_OK; 121 } 122 123 WARN("unsupported interface: %s\n", debugstr_guid(iid)); 124 return E_NOINTERFACE; 125 } 126 } 127 128 static ULONG WINAPI CompartmentMgr_AddRef(ITfCompartmentMgr *iface) 129 { 130 CompartmentMgr *This = impl_from_ITfCompartmentMgr(iface); 131 if (This->pUnkOuter) 132 return IUnknown_AddRef(This->pUnkOuter); 133 else 134 return InterlockedIncrement(&This->refCount); 135 } 136 137 static ULONG WINAPI CompartmentMgr_Release(ITfCompartmentMgr *iface) 138 { 139 CompartmentMgr *This = impl_from_ITfCompartmentMgr(iface); 140 if (This->pUnkOuter) 141 return IUnknown_Release(This->pUnkOuter); 142 else 143 { 144 ULONG ret; 145 146 ret = InterlockedDecrement(&This->refCount); 147 if (ret == 0) 148 CompartmentMgr_Destructor(iface); 149 return ret; 150 } 151 } 152 153 static HRESULT WINAPI CompartmentMgr_GetCompartment(ITfCompartmentMgr *iface, 154 REFGUID rguid, ITfCompartment **ppcomp) 155 { 156 CompartmentMgr *This = impl_from_ITfCompartmentMgr(iface); 157 CompartmentValue* value; 158 struct list *cursor; 159 HRESULT hr; 160 161 TRACE("(%p) %s %p\n",This,debugstr_guid(rguid),ppcomp); 162 163 LIST_FOR_EACH(cursor, &This->values) 164 { 165 value = LIST_ENTRY(cursor,CompartmentValue,entry); 166 if (IsEqualGUID(rguid,&value->guid)) 167 { 168 ITfCompartment_AddRef(value->compartment); 169 *ppcomp = value->compartment; 170 return S_OK; 171 } 172 } 173 174 value = HeapAlloc(GetProcessHeap(),0,sizeof(CompartmentValue)); 175 value->guid = *rguid; 176 value->owner = 0; 177 hr = Compartment_Constructor(value,&value->compartment); 178 if (SUCCEEDED(hr)) 179 { 180 list_add_head(&This->values,&value->entry); 181 ITfCompartment_AddRef(value->compartment); 182 *ppcomp = value->compartment; 183 } 184 else 185 { 186 HeapFree(GetProcessHeap(),0,value); 187 *ppcomp = NULL; 188 } 189 return hr; 190 } 191 192 static HRESULT WINAPI CompartmentMgr_ClearCompartment(ITfCompartmentMgr *iface, 193 TfClientId tid, REFGUID rguid) 194 { 195 CompartmentMgr *This = impl_from_ITfCompartmentMgr(iface); 196 struct list *cursor; 197 198 TRACE("(%p) %i %s\n",This,tid,debugstr_guid(rguid)); 199 200 LIST_FOR_EACH(cursor, &This->values) 201 { 202 CompartmentValue* value = LIST_ENTRY(cursor,CompartmentValue,entry); 203 if (IsEqualGUID(rguid,&value->guid)) 204 { 205 if (value->owner && tid != value->owner) 206 return E_UNEXPECTED; 207 list_remove(cursor); 208 ITfCompartment_Release(value->compartment); 209 HeapFree(GetProcessHeap(),0,value); 210 return S_OK; 211 } 212 } 213 214 return CONNECT_E_NOCONNECTION; 215 } 216 217 static HRESULT WINAPI CompartmentMgr_EnumCompartments(ITfCompartmentMgr *iface, 218 IEnumGUID **ppEnum) 219 { 220 CompartmentMgr *This = impl_from_ITfCompartmentMgr(iface); 221 222 TRACE("(%p) %p\n",This,ppEnum); 223 if (!ppEnum) 224 return E_INVALIDARG; 225 return CompartmentEnumGuid_Constructor(&This->values, ppEnum); 226 } 227 228 static const ITfCompartmentMgrVtbl CompartmentMgrVtbl = 229 { 230 CompartmentMgr_QueryInterface, 231 CompartmentMgr_AddRef, 232 CompartmentMgr_Release, 233 CompartmentMgr_GetCompartment, 234 CompartmentMgr_ClearCompartment, 235 CompartmentMgr_EnumCompartments 236 }; 237 238 HRESULT CompartmentMgr_Constructor(IUnknown *pUnkOuter, REFIID riid, IUnknown **ppOut) 239 { 240 CompartmentMgr *This; 241 242 if (!ppOut) 243 return E_POINTER; 244 245 if (pUnkOuter && !IsEqualIID (riid, &IID_IUnknown)) 246 return CLASS_E_NOAGGREGATION; 247 248 This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(CompartmentMgr)); 249 if (This == NULL) 250 return E_OUTOFMEMORY; 251 252 This->ITfCompartmentMgr_iface.lpVtbl = &CompartmentMgrVtbl; 253 This->pUnkOuter = pUnkOuter; 254 list_init(&This->values); 255 256 if (pUnkOuter) 257 { 258 *ppOut = (IUnknown*)&This->ITfCompartmentMgr_iface; 259 TRACE("returning %p\n", *ppOut); 260 return S_OK; 261 } 262 else 263 { 264 HRESULT hr; 265 hr = ITfCompartmentMgr_QueryInterface(&This->ITfCompartmentMgr_iface, riid, (void**)ppOut); 266 if (FAILED(hr)) 267 HeapFree(GetProcessHeap(),0,This); 268 return hr; 269 } 270 } 271 272 /************************************************** 273 * IEnumGUID implementation for ITfCompartmentMgr::EnumCompartments 274 **************************************************/ 275 static void CompartmentEnumGuid_Destructor(CompartmentEnumGuid *This) 276 { 277 TRACE("destroying %p\n", This); 278 HeapFree(GetProcessHeap(),0,This); 279 } 280 281 static HRESULT WINAPI CompartmentEnumGuid_QueryInterface(IEnumGUID *iface, REFIID iid, LPVOID *ppvOut) 282 { 283 CompartmentEnumGuid *This = impl_from_IEnumGUID(iface); 284 *ppvOut = NULL; 285 286 if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_IEnumGUID)) 287 { 288 *ppvOut = &This->IEnumGUID_iface; 289 } 290 291 if (*ppvOut) 292 { 293 IEnumGUID_AddRef(iface); 294 return S_OK; 295 } 296 297 WARN("unsupported interface: %s\n", debugstr_guid(iid)); 298 return E_NOINTERFACE; 299 } 300 301 static ULONG WINAPI CompartmentEnumGuid_AddRef(IEnumGUID *iface) 302 { 303 CompartmentEnumGuid *This = impl_from_IEnumGUID(iface); 304 return InterlockedIncrement(&This->refCount); 305 } 306 307 static ULONG WINAPI CompartmentEnumGuid_Release(IEnumGUID *iface) 308 { 309 CompartmentEnumGuid *This = impl_from_IEnumGUID(iface); 310 ULONG ret; 311 312 ret = InterlockedDecrement(&This->refCount); 313 if (ret == 0) 314 CompartmentEnumGuid_Destructor(This); 315 return ret; 316 } 317 318 /***************************************************** 319 * IEnumGuid functions 320 *****************************************************/ 321 static HRESULT WINAPI CompartmentEnumGuid_Next(IEnumGUID *iface, 322 ULONG celt, GUID *rgelt, ULONG *pceltFetched) 323 { 324 CompartmentEnumGuid *This = impl_from_IEnumGUID(iface); 325 ULONG fetched = 0; 326 327 TRACE("(%p)\n",This); 328 329 if (rgelt == NULL) return E_POINTER; 330 331 while (fetched < celt && This->cursor) 332 { 333 CompartmentValue* value = LIST_ENTRY(This->cursor,CompartmentValue,entry); 334 if (!value) 335 break; 336 337 This->cursor = list_next(This->values,This->cursor); 338 *rgelt = value->guid; 339 340 ++fetched; 341 ++rgelt; 342 } 343 344 if (pceltFetched) *pceltFetched = fetched; 345 return fetched == celt ? S_OK : S_FALSE; 346 } 347 348 static HRESULT WINAPI CompartmentEnumGuid_Skip(IEnumGUID *iface, ULONG celt) 349 { 350 CompartmentEnumGuid *This = impl_from_IEnumGUID(iface); 351 TRACE("(%p)\n",This); 352 353 This->cursor = list_next(This->values,This->cursor); 354 return S_OK; 355 } 356 357 static HRESULT WINAPI CompartmentEnumGuid_Reset(IEnumGUID *iface) 358 { 359 CompartmentEnumGuid *This = impl_from_IEnumGUID(iface); 360 TRACE("(%p)\n",This); 361 This->cursor = list_head(This->values); 362 return S_OK; 363 } 364 365 static HRESULT WINAPI CompartmentEnumGuid_Clone(IEnumGUID *iface, 366 IEnumGUID **ppenum) 367 { 368 CompartmentEnumGuid *This = impl_from_IEnumGUID(iface); 369 HRESULT res; 370 371 TRACE("(%p)\n",This); 372 373 if (ppenum == NULL) return E_POINTER; 374 375 res = CompartmentEnumGuid_Constructor(This->values, ppenum); 376 if (SUCCEEDED(res)) 377 { 378 CompartmentEnumGuid *new_This = impl_from_IEnumGUID(*ppenum); 379 new_This->cursor = This->cursor; 380 } 381 return res; 382 } 383 384 static const IEnumGUIDVtbl EnumGUIDVtbl = 385 { 386 CompartmentEnumGuid_QueryInterface, 387 CompartmentEnumGuid_AddRef, 388 CompartmentEnumGuid_Release, 389 CompartmentEnumGuid_Next, 390 CompartmentEnumGuid_Skip, 391 CompartmentEnumGuid_Reset, 392 CompartmentEnumGuid_Clone 393 }; 394 395 static HRESULT CompartmentEnumGuid_Constructor(struct list *values, IEnumGUID **ppOut) 396 { 397 CompartmentEnumGuid *This; 398 399 This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(CompartmentEnumGuid)); 400 if (This == NULL) 401 return E_OUTOFMEMORY; 402 403 This->IEnumGUID_iface.lpVtbl= &EnumGUIDVtbl; 404 This->refCount = 1; 405 406 This->values = values; 407 This->cursor = list_head(values); 408 409 *ppOut = &This->IEnumGUID_iface; 410 TRACE("returning %p\n", *ppOut); 411 return S_OK; 412 } 413 414 /************************************************** 415 * ITfCompartment 416 **************************************************/ 417 static void Compartment_Destructor(Compartment *This) 418 { 419 TRACE("destroying %p\n", This); 420 VariantClear(&This->variant); 421 free_sinks(&This->CompartmentEventSink); 422 HeapFree(GetProcessHeap(),0,This); 423 } 424 425 static HRESULT WINAPI Compartment_QueryInterface(ITfCompartment *iface, REFIID iid, LPVOID *ppvOut) 426 { 427 Compartment *This = impl_from_ITfCompartment(iface); 428 429 *ppvOut = NULL; 430 431 if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ITfCompartment)) 432 { 433 *ppvOut = &This->ITfCompartment_iface; 434 } 435 else if (IsEqualIID(iid, &IID_ITfSource)) 436 { 437 *ppvOut = &This->ITfSource_iface; 438 } 439 440 if (*ppvOut) 441 { 442 ITfCompartment_AddRef(iface); 443 return S_OK; 444 } 445 446 WARN("unsupported interface: %s\n", debugstr_guid(iid)); 447 return E_NOINTERFACE; 448 } 449 450 static ULONG WINAPI Compartment_AddRef(ITfCompartment *iface) 451 { 452 Compartment *This = impl_from_ITfCompartment(iface); 453 return InterlockedIncrement(&This->refCount); 454 } 455 456 static ULONG WINAPI Compartment_Release(ITfCompartment *iface) 457 { 458 Compartment *This = impl_from_ITfCompartment(iface); 459 ULONG ret; 460 461 ret = InterlockedDecrement(&This->refCount); 462 if (ret == 0) 463 Compartment_Destructor(This); 464 return ret; 465 } 466 467 static HRESULT WINAPI Compartment_SetValue(ITfCompartment *iface, 468 TfClientId tid, const VARIANT *pvarValue) 469 { 470 Compartment *This = impl_from_ITfCompartment(iface); 471 ITfCompartmentEventSink *sink; 472 struct list *cursor; 473 474 TRACE("(%p) %i %p\n",This,tid,pvarValue); 475 476 if (!pvarValue) 477 return E_INVALIDARG; 478 479 if (!(V_VT(pvarValue) == VT_BSTR || V_VT(pvarValue) == VT_I4 || 480 V_VT(pvarValue) == VT_UNKNOWN)) 481 return E_INVALIDARG; 482 483 if (!This->valueData->owner) 484 This->valueData->owner = tid; 485 486 VariantClear(&This->variant); 487 488 /* Shallow copy of value and type */ 489 This->variant = *pvarValue; 490 491 if (V_VT(pvarValue) == VT_BSTR) 492 V_BSTR(&This->variant) = SysAllocStringByteLen((char*)V_BSTR(pvarValue), 493 SysStringByteLen(V_BSTR(pvarValue))); 494 else if (V_VT(pvarValue) == VT_UNKNOWN) 495 IUnknown_AddRef(V_UNKNOWN(&This->variant)); 496 497 SINK_FOR_EACH(cursor, &This->CompartmentEventSink, ITfCompartmentEventSink, sink) 498 { 499 ITfCompartmentEventSink_OnChange(sink, &This->valueData->guid); 500 } 501 502 return S_OK; 503 } 504 505 static HRESULT WINAPI Compartment_GetValue(ITfCompartment *iface, 506 VARIANT *pvarValue) 507 { 508 Compartment *This = impl_from_ITfCompartment(iface); 509 TRACE("(%p) %p\n",This, pvarValue); 510 511 if (!pvarValue) 512 return E_INVALIDARG; 513 514 VariantInit(pvarValue); 515 if (V_VT(&This->variant) == VT_EMPTY) return S_FALSE; 516 return VariantCopy(pvarValue,&This->variant); 517 } 518 519 static const ITfCompartmentVtbl CompartmentVtbl = 520 { 521 Compartment_QueryInterface, 522 Compartment_AddRef, 523 Compartment_Release, 524 Compartment_SetValue, 525 Compartment_GetValue 526 }; 527 528 /***************************************************** 529 * ITfSource functions 530 *****************************************************/ 531 532 static HRESULT WINAPI CompartmentSource_QueryInterface(ITfSource *iface, REFIID iid, LPVOID *ppvOut) 533 { 534 Compartment *This = impl_from_ITfSource(iface); 535 return ITfCompartment_QueryInterface(&This->ITfCompartment_iface, iid, ppvOut); 536 } 537 538 static ULONG WINAPI CompartmentSource_AddRef(ITfSource *iface) 539 { 540 Compartment *This = impl_from_ITfSource(iface); 541 return ITfCompartment_AddRef(&This->ITfCompartment_iface); 542 } 543 544 static ULONG WINAPI CompartmentSource_Release(ITfSource *iface) 545 { 546 Compartment *This = impl_from_ITfSource(iface); 547 return ITfCompartment_Release(&This->ITfCompartment_iface); 548 } 549 550 static HRESULT WINAPI CompartmentSource_AdviseSink(ITfSource *iface, 551 REFIID riid, IUnknown *punk, DWORD *pdwCookie) 552 { 553 Compartment *This = impl_from_ITfSource(iface); 554 555 TRACE("(%p) %s %p %p\n",This,debugstr_guid(riid),punk,pdwCookie); 556 557 if (!riid || !punk || !pdwCookie) 558 return E_INVALIDARG; 559 560 if (IsEqualIID(riid, &IID_ITfCompartmentEventSink)) 561 return advise_sink(&This->CompartmentEventSink, &IID_ITfCompartmentEventSink, 562 COOKIE_MAGIC_COMPARTMENTSINK, punk, pdwCookie); 563 564 FIXME("(%p) Unhandled Sink: %s\n",This,debugstr_guid(riid)); 565 return E_NOTIMPL; 566 } 567 568 static HRESULT WINAPI CompartmentSource_UnadviseSink(ITfSource *iface, DWORD pdwCookie) 569 { 570 Compartment *This = impl_from_ITfSource(iface); 571 572 TRACE("(%p) %x\n",This,pdwCookie); 573 574 if (get_Cookie_magic(pdwCookie)!=COOKIE_MAGIC_COMPARTMENTSINK) 575 return E_INVALIDARG; 576 577 return unadvise_sink(pdwCookie); 578 } 579 580 static const ITfSourceVtbl CompartmentSourceVtbl = 581 { 582 CompartmentSource_QueryInterface, 583 CompartmentSource_AddRef, 584 CompartmentSource_Release, 585 CompartmentSource_AdviseSink, 586 CompartmentSource_UnadviseSink, 587 }; 588 589 static HRESULT Compartment_Constructor(CompartmentValue *valueData, ITfCompartment **ppOut) 590 { 591 Compartment *This; 592 593 This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(Compartment)); 594 if (This == NULL) 595 return E_OUTOFMEMORY; 596 597 This->ITfCompartment_iface.lpVtbl= &CompartmentVtbl; 598 This->ITfSource_iface.lpVtbl = &CompartmentSourceVtbl; 599 This->refCount = 1; 600 601 This->valueData = valueData; 602 VariantInit(&This->variant); 603 604 list_init(&This->CompartmentEventSink); 605 606 *ppOut = &This->ITfCompartment_iface; 607 TRACE("returning %p\n", *ppOut); 608 return S_OK; 609 } 610