Reactos
at master 563 lines 14 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: ITfCompartmentMgr implementation 5 * COPYRIGHT: Copyright 2009 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 14//////////////////////////////////////////////////////////////////////////// 15 16typedef struct tagCompartmentValue 17{ 18 struct list entry; 19 GUID guid; 20 TfClientId owner; 21 ITfCompartment *compartment; 22} CompartmentValue; 23 24//////////////////////////////////////////////////////////////////////////// 25 26class CCompartmentMgr 27 : public ITfCompartmentMgr 28{ 29public: 30 CCompartmentMgr(IUnknown *pUnkOuter = NULL); 31 virtual ~CCompartmentMgr(); 32 33 static HRESULT CreateInstance(IUnknown *pUnkOuter, REFIID riid, IUnknown **ppOut); 34 35 // ** IUnknown methods ** 36 STDMETHODIMP QueryInterface(REFIID iid, LPVOID *ppvObj) override; 37 STDMETHODIMP_(ULONG) AddRef() override; 38 STDMETHODIMP_(ULONG) Release() override; 39 40 // ** ITfCompartmentMgr methods ** 41 STDMETHODIMP GetCompartment(_In_ REFGUID rguid, _Out_ ITfCompartment **ppcomp) override; 42 STDMETHODIMP ClearCompartment(_In_ TfClientId tid, _In_ REFGUID rguid) override; 43 STDMETHODIMP EnumCompartments(_Out_ IEnumGUID **ppEnum) override; 44 45protected: 46 LONG m_cRefs; 47 IUnknown *m_pUnkOuter; 48 struct list m_values; 49}; 50 51//////////////////////////////////////////////////////////////////////////// 52 53class CCompartmentEnumGuid 54 : public IEnumGUID 55{ 56public: 57 CCompartmentEnumGuid(); 58 virtual ~CCompartmentEnumGuid(); 59 60 static HRESULT CreateInstance(struct list *values, IEnumGUID **ppOut); 61 static HRESULT CreateInstance(struct list *values, IEnumGUID **ppOut, struct list *cursor); 62 63 // ** IUnknown methods ** 64 STDMETHODIMP QueryInterface(REFIID iid, LPVOID *ppvObj) override; 65 STDMETHODIMP_(ULONG) AddRef() override; 66 STDMETHODIMP_(ULONG) Release() override; 67 68 // ** IEnumGUID methods ** 69 STDMETHODIMP Next( 70 _In_ ULONG celt, 71 _Out_ GUID *rgelt, 72 _Out_ ULONG *pceltFetched) override; 73 STDMETHODIMP Skip(_In_ ULONG celt) override; 74 STDMETHODIMP Reset() override; 75 STDMETHODIMP Clone(_Out_ IEnumGUID **ppenum) override; 76 77protected: 78 LONG m_cRefs; 79 struct list *m_values; 80 struct list *m_cursor; 81}; 82 83//////////////////////////////////////////////////////////////////////////// 84 85class CCompartment 86 : public ITfCompartment 87 , public ITfSource 88{ 89public: 90 CCompartment(CompartmentValue *valueData); 91 virtual ~CCompartment(); 92 93 static HRESULT CreateInstance(CompartmentValue *valueData, ITfCompartment **ppOut); 94 95 // ** IUnknown methods ** 96 STDMETHODIMP QueryInterface(REFIID iid, LPVOID *ppvObj) override; 97 STDMETHODIMP_(ULONG) AddRef() override; 98 STDMETHODIMP_(ULONG) Release() override; 99 100 // ** ITfCompartment methods ** 101 STDMETHODIMP SetValue(_In_ TfClientId tid, _In_ const VARIANT *pvarValue) override; 102 STDMETHODIMP GetValue(_Out_ VARIANT *pvarValue) override; 103 104 // ** ITfSource methods ** 105 STDMETHODIMP AdviseSink( 106 _In_ REFIID riid, 107 _In_ IUnknown *punk, 108 _Out_ DWORD *pdwCookie) override; 109 STDMETHODIMP UnadviseSink(_In_ DWORD dwCookie) override; 110 111protected: 112 LONG m_cRefs; 113 VARIANT m_variant; // Only VT_I4, VT_UNKNOWN and VT_BSTR data types are allowed 114 CompartmentValue *m_valueData; 115 struct list m_CompartmentEventSink; 116}; 117 118//////////////////////////////////////////////////////////////////////////// 119 120CCompartmentMgr::CCompartmentMgr(IUnknown *pUnkOuter) 121 : m_cRefs(1) 122 , m_pUnkOuter(pUnkOuter) 123{ 124 list_init(&m_values); 125} 126 127CCompartmentMgr::~CCompartmentMgr() 128{ 129 struct list *cursor, *cursor2; 130 131 LIST_FOR_EACH_SAFE(cursor, cursor2, &m_values) 132 { 133 CompartmentValue *value = LIST_ENTRY(cursor, CompartmentValue, entry); 134 list_remove(cursor); 135 value->compartment->Release(); 136 cicMemFree(value); 137 } 138} 139 140HRESULT CCompartmentMgr::CreateInstance(IUnknown *pUnkOuter, REFIID riid, IUnknown **ppOut) 141{ 142 if (!ppOut) 143 return E_POINTER; 144 145 if (pUnkOuter && riid != IID_IUnknown) 146 return CLASS_E_NOAGGREGATION; 147 148 CCompartmentMgr *This = new(cicNoThrow) CCompartmentMgr(pUnkOuter); 149 if (!This) 150 return E_OUTOFMEMORY; 151 152 if (pUnkOuter) 153 { 154 *ppOut = static_cast<ITfCompartmentMgr *>(This); 155 TRACE("returning %p\n", *ppOut); 156 return S_OK; 157 } 158 else 159 { 160 HRESULT hr; 161 hr = This->QueryInterface(riid, (void **)ppOut); 162 if (FAILED(hr)) 163 delete This; 164 return hr; 165 } 166} 167 168STDMETHODIMP CCompartmentMgr::QueryInterface(REFIID iid, LPVOID *ppvOut) 169{ 170 if (m_pUnkOuter) 171 return m_pUnkOuter->QueryInterface(iid, ppvOut); 172 173 *ppvOut = NULL; 174 175 if (iid == IID_IUnknown || iid == IID_ITfCompartmentMgr) 176 *ppvOut = static_cast<ITfCompartmentMgr *>(this); 177 178 if (*ppvOut) 179 { 180 AddRef(); 181 return S_OK; 182 } 183 184 WARN("unsupported interface: %s\n", debugstr_guid(&iid)); 185 return E_NOINTERFACE; 186} 187 188STDMETHODIMP_(ULONG) CCompartmentMgr::AddRef() 189{ 190 if (m_pUnkOuter) 191 return m_pUnkOuter->AddRef(); 192 return ::InterlockedIncrement(&m_cRefs); 193} 194 195STDMETHODIMP_(ULONG) CCompartmentMgr::Release() 196{ 197 if (m_pUnkOuter) 198 return m_pUnkOuter->Release(); 199 ULONG ret = ::InterlockedDecrement(&m_cRefs); 200 if (!ret) 201 delete this; 202 return ret; 203} 204 205HRESULT CCompartmentMgr::GetCompartment(_In_ REFGUID rguid, _Out_ ITfCompartment **ppcomp) 206{ 207 if (!ppcomp) 208 return E_POINTER; 209 CompartmentValue* value; 210 struct list *cursor; 211 HRESULT hr; 212 213 TRACE("(%p) %s %p\n", this, debugstr_guid(&rguid), ppcomp); 214 215 LIST_FOR_EACH(cursor, &m_values) 216 { 217 value = LIST_ENTRY(cursor, CompartmentValue, entry); 218 if (rguid == value->guid) 219 { 220 value->compartment->AddRef(); 221 *ppcomp = value->compartment; 222 return S_OK; 223 } 224 } 225 226 value = (CompartmentValue *)cicMemAlloc(sizeof(CompartmentValue)); 227 if (!value) 228 return E_OUTOFMEMORY; 229 230 value->guid = rguid; 231 value->owner = 0; 232 hr = CCompartment::CreateInstance(value, &value->compartment); 233 if (SUCCEEDED(hr)) 234 { 235 list_add_head(&m_values, &value->entry); 236 value->compartment->AddRef(); 237 *ppcomp = value->compartment; 238 } 239 else 240 { 241 cicMemFree(value); 242 *ppcomp = NULL; 243 } 244 245 return hr; 246} 247 248HRESULT CCompartmentMgr::ClearCompartment(_In_ TfClientId tid, _In_ REFGUID rguid) 249{ 250 struct list *cursor; 251 252 TRACE("(%p) %i %s\n", this, tid, debugstr_guid(&rguid)); 253 254 LIST_FOR_EACH(cursor, &m_values) 255 { 256 CompartmentValue *value = LIST_ENTRY(cursor,CompartmentValue, entry); 257 if (rguid == value->guid) 258 { 259 if (value->owner && tid != value->owner) 260 return E_UNEXPECTED; 261 list_remove(cursor); 262 value->compartment->Release(); 263 cicMemFree(value); 264 return S_OK; 265 } 266 } 267 268 return CONNECT_E_NOCONNECTION; 269} 270 271HRESULT CCompartmentMgr::EnumCompartments(_Out_ IEnumGUID **ppEnum) 272{ 273 TRACE("(%p) %p\n", this, ppEnum); 274 if (!ppEnum) 275 return E_INVALIDARG; 276 277 return CCompartmentEnumGuid::CreateInstance(&m_values, ppEnum); 278} 279 280//////////////////////////////////////////////////////////////////////////// 281 282CCompartmentEnumGuid::CCompartmentEnumGuid() 283 : m_cRefs(1) 284 , m_values(NULL) 285 , m_cursor(NULL) 286{ 287} 288 289CCompartmentEnumGuid::~CCompartmentEnumGuid() 290{ 291 TRACE("destroying %p\n", this); 292} 293 294HRESULT 295CCompartmentEnumGuid::CreateInstance(struct list *values, IEnumGUID **ppOut) 296{ 297 CCompartmentEnumGuid *This = new(cicNoThrow) CCompartmentEnumGuid(); 298 if (!This) 299 return E_OUTOFMEMORY; 300 301 This->m_values = values; 302 This->m_cursor = list_head(values); 303 304 *ppOut = static_cast<IEnumGUID *>(This); 305 TRACE("returning %p\n", *ppOut); 306 return S_OK; 307} 308 309HRESULT 310CCompartmentEnumGuid::CreateInstance(struct list *values, IEnumGUID **ppOut, struct list *cursor) 311{ 312 CCompartmentEnumGuid *This = new(cicNoThrow) CCompartmentEnumGuid(); 313 if (!This) 314 return E_OUTOFMEMORY; 315 316 This->m_values = values; 317 This->m_cursor = cursor; 318 319 *ppOut = static_cast<IEnumGUID *>(This); 320 TRACE("returning %p\n", *ppOut); 321 return S_OK; 322} 323 324STDMETHODIMP CCompartmentEnumGuid::QueryInterface(REFIID iid, LPVOID *ppvObj) 325{ 326 *ppvObj = NULL; 327 328 if (iid == IID_IUnknown || iid == IID_IEnumGUID) 329 *ppvObj = static_cast<IEnumGUID *>(this); 330 331 if (*ppvObj) 332 { 333 AddRef(); 334 return S_OK; 335 } 336 337 WARN("unsupported interface: %s\n", debugstr_guid(&iid)); 338 return E_NOINTERFACE; 339} 340 341STDMETHODIMP_(ULONG) CCompartmentEnumGuid::AddRef() 342{ 343 return ::InterlockedIncrement(&m_cRefs); 344} 345 346STDMETHODIMP_(ULONG) CCompartmentEnumGuid::Release() 347{ 348 ULONG ret = ::InterlockedDecrement(&m_cRefs); 349 if (!ret) 350 delete this; 351 return ret; 352} 353 354STDMETHODIMP CCompartmentEnumGuid::Next( 355 _In_ ULONG celt, 356 _Out_ GUID *rgelt, 357 _Out_ ULONG *pceltFetched) 358{ 359 ULONG fetched = 0; 360 361 TRACE("(%p)\n", this); 362 363 if (!rgelt) 364 return E_POINTER; 365 366 while (fetched < celt && m_cursor) 367 { 368 CompartmentValue* value = LIST_ENTRY(m_cursor, CompartmentValue, entry); 369 if (!value) 370 break; 371 372 m_cursor = list_next(m_values, m_cursor); 373 *rgelt = value->guid; 374 375 ++fetched; 376 ++rgelt; 377 } 378 379 if (pceltFetched) 380 *pceltFetched = fetched; 381 return fetched == celt ? S_OK : S_FALSE; 382} 383 384STDMETHODIMP CCompartmentEnumGuid::Skip(_In_ ULONG celt) 385{ 386 TRACE("(%p)\n", this); 387 m_cursor = list_next(m_values, m_cursor); 388 return S_OK; 389} 390 391STDMETHODIMP CCompartmentEnumGuid::Reset() 392{ 393 TRACE("(%p)\n", this); 394 m_cursor = list_head(m_values); 395 return S_OK; 396} 397 398STDMETHODIMP CCompartmentEnumGuid::Clone(_Out_ IEnumGUID **ppenum) 399{ 400 TRACE("(%p)\n", this); 401 402 if (!ppenum) 403 return E_POINTER; 404 405 return CCompartmentEnumGuid::CreateInstance(m_values, ppenum, m_cursor); 406} 407 408//////////////////////////////////////////////////////////////////////////// 409 410CCompartment::CCompartment(CompartmentValue *valueData) 411 : m_cRefs(1) 412 , m_valueData(valueData) 413{ 414 VariantInit(&m_variant); 415 list_init(&m_CompartmentEventSink); 416} 417 418CCompartment::~CCompartment() 419{ 420 TRACE("destroying %p\n", this); 421 VariantClear(&m_variant); 422 free_sinks(&m_CompartmentEventSink); 423} 424 425HRESULT CCompartment::CreateInstance(CompartmentValue *valueData, ITfCompartment **ppOut) 426{ 427 CCompartment *This = new(cicNoThrow) CCompartment(valueData); 428 if (!This) 429 return E_OUTOFMEMORY; 430 431 *ppOut = static_cast<ITfCompartment *>(This); 432 TRACE("returning %p\n", *ppOut); 433 return S_OK; 434} 435 436STDMETHODIMP CCompartment::QueryInterface(REFIID iid, LPVOID *ppvObj) 437{ 438 *ppvObj = NULL; 439 440 if (iid == IID_IUnknown || iid == IID_ITfCompartment) 441 *ppvObj = static_cast<ITfCompartment *>(this); 442 else if (iid == IID_ITfSource) 443 *ppvObj = static_cast<ITfSource *>(this); 444 445 if (*ppvObj) 446 { 447 AddRef(); 448 return S_OK; 449 } 450 451 WARN("unsupported interface: %s\n", debugstr_guid(&iid)); 452 return E_NOINTERFACE; 453} 454 455STDMETHODIMP_(ULONG) CCompartment::AddRef() 456{ 457 return ::InterlockedIncrement(&m_cRefs); 458} 459 460STDMETHODIMP_(ULONG) CCompartment::Release() 461{ 462 ULONG ret = ::InterlockedDecrement(&m_cRefs); 463 if (!ret) 464 delete this; 465 return ret; 466} 467 468STDMETHODIMP CCompartment::SetValue(_In_ TfClientId tid, _In_ const VARIANT *pvarValue) 469{ 470 ITfCompartmentEventSink *sink; 471 struct list *cursor; 472 473 TRACE("(%p) %i %p\n", this, tid, pvarValue); 474 475 if (!pvarValue) 476 return E_INVALIDARG; 477 478 if (!(V_VT(pvarValue) == VT_BSTR || V_VT(pvarValue) == VT_I4 || V_VT(pvarValue) == VT_UNKNOWN)) 479 return E_INVALIDARG; 480 481 if (!m_valueData->owner) 482 m_valueData->owner = tid; 483 484 ::VariantClear(&m_variant); 485 486 /* Shallow copy of value and type */ 487 m_variant = *pvarValue; 488 489 if (V_VT(pvarValue) == VT_BSTR) 490 { 491 V_BSTR(&m_variant) = ::SysAllocStringByteLen((char*)V_BSTR(pvarValue), 492 ::SysStringByteLen(V_BSTR(pvarValue))); 493 } 494 else if (V_VT(pvarValue) == VT_UNKNOWN) 495 { 496 V_UNKNOWN(&m_variant)->AddRef(); 497 } 498 499 SINK_FOR_EACH(cursor, &m_CompartmentEventSink, ITfCompartmentEventSink, sink) 500 { 501 sink->OnChange(m_valueData->guid); 502 } 503 504 return S_OK; 505} 506 507STDMETHODIMP CCompartment::GetValue(_Out_ VARIANT *pvarValue) 508{ 509 TRACE("(%p) %p\n", this, pvarValue); 510 511 if (!pvarValue) 512 return E_INVALIDARG; 513 514 ::VariantInit(pvarValue); 515 if (V_VT(&m_variant) == VT_EMPTY) 516 return S_FALSE; 517 return ::VariantCopy(pvarValue, &m_variant); 518} 519 520STDMETHODIMP CCompartment::AdviseSink( 521 _In_ REFIID riid, 522 _In_ IUnknown *punk, 523 _Out_ DWORD *pdwCookie) 524{ 525 TRACE("(%p) %s %p %p\n", this, debugstr_guid(&riid), punk, pdwCookie); 526 527 if (cicIsNullPtr(&riid) || !punk || !pdwCookie) 528 return E_INVALIDARG; 529 530 if (riid == IID_ITfCompartmentEventSink) 531 { 532 return advise_sink(&m_CompartmentEventSink, IID_ITfCompartmentEventSink, 533 COOKIE_MAGIC_COMPARTMENTSINK, punk, pdwCookie); 534 } 535 536 FIXME("(%p) Unhandled Sink: %s\n", this, debugstr_guid(&riid)); 537 return E_NOTIMPL; 538} 539 540STDMETHODIMP CCompartment::UnadviseSink(_In_ DWORD dwCookie) 541{ 542 TRACE("(%p) %x\n", this, dwCookie); 543 544 if (get_Cookie_magic(dwCookie) != COOKIE_MAGIC_COMPARTMENTSINK) 545 return E_INVALIDARG; 546 547 return unadvise_sink(dwCookie); 548} 549 550//////////////////////////////////////////////////////////////////////////// 551 552EXTERN_C 553HRESULT CompartmentMgr_Constructor(IUnknown *pUnkOuter, REFIID riid, IUnknown **ppOut) 554{ 555 return CCompartmentMgr::CreateInstance(pUnkOuter, riid, ppOut); 556} 557 558EXTERN_C 559HRESULT CompartmentMgr_Destructor(ITfCompartmentMgr *iface) 560{ 561 iface->Release(); 562 return S_OK; 563}