diff --git a/base/ctf/msctf/CMakeLists.txt b/base/ctf/msctf/CMakeLists.txt index b9a2b9fca94..655eac1916a 100644 --- a/base/ctf/msctf/CMakeLists.txt +++ b/base/ctf/msctf/CMakeLists.txt @@ -5,7 +5,6 @@ add_definitions(-D_WIN32_WINNT=0x600) spec2def(msctf.dll msctf.spec ADD_IMPORTLIB) list(APPEND SOURCE - compartmentmgr.c context.c inputprocessor.c msctf.c @@ -15,6 +14,7 @@ list(APPEND SOURCE list(APPEND PCH_SKIP_SOURCE categorymgr.cpp + compartmentmgr.cpp displayattributemgr.cpp documentmgr.cpp langbarmgr.cpp diff --git a/base/ctf/msctf/compartmentmgr.c b/base/ctf/msctf/compartmentmgr.c deleted file mode 100644 index 4cc25f3f9ab..00000000000 --- a/base/ctf/msctf/compartmentmgr.c +++ /dev/null @@ -1,625 +0,0 @@ -/* - * ITfCompartmentMgr implementation - * - * Copyright 2009 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 - -#define COBJMACROS - -#include "wine/debug.h" -#include "windef.h" -#include "winbase.h" -#include "winreg.h" -#include "winuser.h" -#include "shlwapi.h" -#include "winerror.h" -#include "objbase.h" -#include "oleauto.h" -#include "olectl.h" - -#include "msctf.h" -#include "msctf_internal.h" - -WINE_DEFAULT_DEBUG_CHANNEL(msctf); - -typedef struct tagCompartmentValue { - struct list entry; - GUID guid; - TfClientId owner; - ITfCompartment *compartment; -} CompartmentValue; - -typedef struct tagCompartmentMgr { - ITfCompartmentMgr ITfCompartmentMgr_iface; - LONG refCount; - - IUnknown *pUnkOuter; - - struct list values; -} CompartmentMgr; - -typedef struct tagCompartmentEnumGuid { - IEnumGUID IEnumGUID_iface; - LONG refCount; - - struct list *values; - struct list *cursor; -} CompartmentEnumGuid; - -typedef struct tagCompartment { - ITfCompartment ITfCompartment_iface; - ITfSource ITfSource_iface; - LONG refCount; - - /* Only VT_I4, VT_UNKNOWN and VT_BSTR data types are allowed */ - VARIANT variant; - CompartmentValue *valueData; - struct list CompartmentEventSink; -} Compartment; - -static HRESULT CompartmentEnumGuid_Constructor(struct list* values, IEnumGUID **ppOut); -static HRESULT Compartment_Constructor(CompartmentValue *value, ITfCompartment **ppOut); - -static inline CompartmentMgr *impl_from_ITfCompartmentMgr(ITfCompartmentMgr *iface) -{ - return CONTAINING_RECORD(iface, CompartmentMgr, ITfCompartmentMgr_iface); -} - -static inline Compartment *impl_from_ITfCompartment(ITfCompartment *iface) -{ - return CONTAINING_RECORD(iface, Compartment, ITfCompartment_iface); -} - -static inline Compartment *impl_from_ITfSource(ITfSource *iface) -{ - return CONTAINING_RECORD(iface, Compartment, ITfSource_iface); -} - -static inline CompartmentEnumGuid *impl_from_IEnumGUID(IEnumGUID *iface) -{ - return CONTAINING_RECORD(iface, CompartmentEnumGuid, IEnumGUID_iface); -} - -HRESULT CompartmentMgr_Destructor(ITfCompartmentMgr *iface) -{ - CompartmentMgr *This = impl_from_ITfCompartmentMgr(iface); - struct list *cursor, *cursor2; - - LIST_FOR_EACH_SAFE(cursor, cursor2, &This->values) - { - CompartmentValue* value = LIST_ENTRY(cursor,CompartmentValue,entry); - list_remove(cursor); - ITfCompartment_Release(value->compartment); - HeapFree(GetProcessHeap(),0,value); - } - - HeapFree(GetProcessHeap(),0,This); - return S_OK; -} - -/***************************************************** - * ITfCompartmentMgr functions - *****************************************************/ -static HRESULT WINAPI CompartmentMgr_QueryInterface(ITfCompartmentMgr *iface, REFIID iid, LPVOID *ppvOut) -{ - CompartmentMgr *This = impl_from_ITfCompartmentMgr(iface); - if (This->pUnkOuter) - return IUnknown_QueryInterface(This->pUnkOuter, iid, ppvOut); - else - { - *ppvOut = NULL; - - if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ITfCompartmentMgr)) - { - *ppvOut = &This->ITfCompartmentMgr_iface; - } - - if (*ppvOut) - { - ITfCompartmentMgr_AddRef(iface); - return S_OK; - } - - WARN("unsupported interface: %s\n", debugstr_guid(iid)); - return E_NOINTERFACE; - } -} - -static ULONG WINAPI CompartmentMgr_AddRef(ITfCompartmentMgr *iface) -{ - CompartmentMgr *This = impl_from_ITfCompartmentMgr(iface); - if (This->pUnkOuter) - return IUnknown_AddRef(This->pUnkOuter); - else - return InterlockedIncrement(&This->refCount); -} - -static ULONG WINAPI CompartmentMgr_Release(ITfCompartmentMgr *iface) -{ - CompartmentMgr *This = impl_from_ITfCompartmentMgr(iface); - if (This->pUnkOuter) - return IUnknown_Release(This->pUnkOuter); - else - { - ULONG ret; - - ret = InterlockedDecrement(&This->refCount); - if (ret == 0) - CompartmentMgr_Destructor(iface); - return ret; - } -} - -static HRESULT WINAPI CompartmentMgr_GetCompartment(ITfCompartmentMgr *iface, - REFGUID rguid, ITfCompartment **ppcomp) -{ - CompartmentMgr *This = impl_from_ITfCompartmentMgr(iface); - CompartmentValue* value; - struct list *cursor; - HRESULT hr; - - TRACE("(%p) %s %p\n",This,debugstr_guid(rguid),ppcomp); - - LIST_FOR_EACH(cursor, &This->values) - { - value = LIST_ENTRY(cursor,CompartmentValue,entry); - if (IsEqualGUID(rguid,&value->guid)) - { - ITfCompartment_AddRef(value->compartment); - *ppcomp = value->compartment; - return S_OK; - } - } - - value = HeapAlloc(GetProcessHeap(),0,sizeof(CompartmentValue)); - value->guid = *rguid; - value->owner = 0; - hr = Compartment_Constructor(value,&value->compartment); - if (SUCCEEDED(hr)) - { - list_add_head(&This->values,&value->entry); - ITfCompartment_AddRef(value->compartment); - *ppcomp = value->compartment; - } - else - { - HeapFree(GetProcessHeap(),0,value); - *ppcomp = NULL; - } - return hr; -} - -static HRESULT WINAPI CompartmentMgr_ClearCompartment(ITfCompartmentMgr *iface, - TfClientId tid, REFGUID rguid) -{ - CompartmentMgr *This = impl_from_ITfCompartmentMgr(iface); - struct list *cursor; - - TRACE("(%p) %i %s\n",This,tid,debugstr_guid(rguid)); - - LIST_FOR_EACH(cursor, &This->values) - { - CompartmentValue* value = LIST_ENTRY(cursor,CompartmentValue,entry); - if (IsEqualGUID(rguid,&value->guid)) - { - if (value->owner && tid != value->owner) - return E_UNEXPECTED; - list_remove(cursor); - ITfCompartment_Release(value->compartment); - HeapFree(GetProcessHeap(),0,value); - return S_OK; - } - } - - return CONNECT_E_NOCONNECTION; -} - -static HRESULT WINAPI CompartmentMgr_EnumCompartments(ITfCompartmentMgr *iface, - IEnumGUID **ppEnum) -{ - CompartmentMgr *This = impl_from_ITfCompartmentMgr(iface); - - TRACE("(%p) %p\n",This,ppEnum); - if (!ppEnum) - return E_INVALIDARG; - return CompartmentEnumGuid_Constructor(&This->values, ppEnum); -} - -static const ITfCompartmentMgrVtbl CompartmentMgrVtbl = -{ - CompartmentMgr_QueryInterface, - CompartmentMgr_AddRef, - CompartmentMgr_Release, - CompartmentMgr_GetCompartment, - CompartmentMgr_ClearCompartment, - CompartmentMgr_EnumCompartments -}; - -HRESULT CompartmentMgr_Constructor(IUnknown *pUnkOuter, REFIID riid, IUnknown **ppOut) -{ - CompartmentMgr *This; - - if (!ppOut) - return E_POINTER; - - if (pUnkOuter && !IsEqualIID (riid, &IID_IUnknown)) - return CLASS_E_NOAGGREGATION; - - This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(CompartmentMgr)); - if (This == NULL) - return E_OUTOFMEMORY; - - This->ITfCompartmentMgr_iface.lpVtbl = &CompartmentMgrVtbl; - This->pUnkOuter = pUnkOuter; - list_init(&This->values); - - if (pUnkOuter) - { - *ppOut = (IUnknown*)&This->ITfCompartmentMgr_iface; - TRACE("returning %p\n", *ppOut); - return S_OK; - } - else - { - HRESULT hr; - hr = ITfCompartmentMgr_QueryInterface(&This->ITfCompartmentMgr_iface, riid, (void**)ppOut); - if (FAILED(hr)) - HeapFree(GetProcessHeap(),0,This); - return hr; - } -} - -/************************************************** - * IEnumGUID implementation for ITfCompartmentMgr::EnumCompartments - **************************************************/ -static void CompartmentEnumGuid_Destructor(CompartmentEnumGuid *This) -{ - TRACE("destroying %p\n", This); - HeapFree(GetProcessHeap(),0,This); -} - -static HRESULT WINAPI CompartmentEnumGuid_QueryInterface(IEnumGUID *iface, REFIID iid, LPVOID *ppvOut) -{ - CompartmentEnumGuid *This = impl_from_IEnumGUID(iface); - *ppvOut = NULL; - - if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_IEnumGUID)) - { - *ppvOut = &This->IEnumGUID_iface; - } - - if (*ppvOut) - { - IEnumGUID_AddRef(iface); - return S_OK; - } - - WARN("unsupported interface: %s\n", debugstr_guid(iid)); - return E_NOINTERFACE; -} - -static ULONG WINAPI CompartmentEnumGuid_AddRef(IEnumGUID *iface) -{ - CompartmentEnumGuid *This = impl_from_IEnumGUID(iface); - return InterlockedIncrement(&This->refCount); -} - -static ULONG WINAPI CompartmentEnumGuid_Release(IEnumGUID *iface) -{ - CompartmentEnumGuid *This = impl_from_IEnumGUID(iface); - ULONG ret; - - ret = InterlockedDecrement(&This->refCount); - if (ret == 0) - CompartmentEnumGuid_Destructor(This); - return ret; -} - -/***************************************************** - * IEnumGuid functions - *****************************************************/ -static HRESULT WINAPI CompartmentEnumGuid_Next(IEnumGUID *iface, - ULONG celt, GUID *rgelt, ULONG *pceltFetched) -{ - CompartmentEnumGuid *This = impl_from_IEnumGUID(iface); - ULONG fetched = 0; - - TRACE("(%p)\n",This); - - if (rgelt == NULL) return E_POINTER; - - while (fetched < celt && This->cursor) - { - CompartmentValue* value = LIST_ENTRY(This->cursor,CompartmentValue,entry); - if (!value) - break; - - This->cursor = list_next(This->values,This->cursor); - *rgelt = value->guid; - - ++fetched; - ++rgelt; - } - - if (pceltFetched) *pceltFetched = fetched; - return fetched == celt ? S_OK : S_FALSE; -} - -static HRESULT WINAPI CompartmentEnumGuid_Skip(IEnumGUID *iface, ULONG celt) -{ - CompartmentEnumGuid *This = impl_from_IEnumGUID(iface); - TRACE("(%p)\n",This); - - This->cursor = list_next(This->values,This->cursor); - return S_OK; -} - -static HRESULT WINAPI CompartmentEnumGuid_Reset(IEnumGUID *iface) -{ - CompartmentEnumGuid *This = impl_from_IEnumGUID(iface); - TRACE("(%p)\n",This); - This->cursor = list_head(This->values); - return S_OK; -} - -static HRESULT WINAPI CompartmentEnumGuid_Clone(IEnumGUID *iface, - IEnumGUID **ppenum) -{ - CompartmentEnumGuid *This = impl_from_IEnumGUID(iface); - HRESULT res; - - TRACE("(%p)\n",This); - - if (ppenum == NULL) return E_POINTER; - - res = CompartmentEnumGuid_Constructor(This->values, ppenum); - if (SUCCEEDED(res)) - { - CompartmentEnumGuid *new_This = impl_from_IEnumGUID(*ppenum); - new_This->cursor = This->cursor; - } - return res; -} - -static const IEnumGUIDVtbl EnumGUIDVtbl = -{ - CompartmentEnumGuid_QueryInterface, - CompartmentEnumGuid_AddRef, - CompartmentEnumGuid_Release, - CompartmentEnumGuid_Next, - CompartmentEnumGuid_Skip, - CompartmentEnumGuid_Reset, - CompartmentEnumGuid_Clone -}; - -static HRESULT CompartmentEnumGuid_Constructor(struct list *values, IEnumGUID **ppOut) -{ - CompartmentEnumGuid *This; - - This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(CompartmentEnumGuid)); - if (This == NULL) - return E_OUTOFMEMORY; - - This->IEnumGUID_iface.lpVtbl= &EnumGUIDVtbl; - This->refCount = 1; - - This->values = values; - This->cursor = list_head(values); - - *ppOut = &This->IEnumGUID_iface; - TRACE("returning %p\n", *ppOut); - return S_OK; -} - -/************************************************** - * ITfCompartment - **************************************************/ -static void Compartment_Destructor(Compartment *This) -{ - TRACE("destroying %p\n", This); - VariantClear(&This->variant); - free_sinks(&This->CompartmentEventSink); - HeapFree(GetProcessHeap(),0,This); -} - -static HRESULT WINAPI Compartment_QueryInterface(ITfCompartment *iface, REFIID iid, LPVOID *ppvOut) -{ - Compartment *This = impl_from_ITfCompartment(iface); - - *ppvOut = NULL; - - if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ITfCompartment)) - { - *ppvOut = &This->ITfCompartment_iface; - } - else if (IsEqualIID(iid, &IID_ITfSource)) - { - *ppvOut = &This->ITfSource_iface; - } - - if (*ppvOut) - { - ITfCompartment_AddRef(iface); - return S_OK; - } - - WARN("unsupported interface: %s\n", debugstr_guid(iid)); - return E_NOINTERFACE; -} - -static ULONG WINAPI Compartment_AddRef(ITfCompartment *iface) -{ - Compartment *This = impl_from_ITfCompartment(iface); - return InterlockedIncrement(&This->refCount); -} - -static ULONG WINAPI Compartment_Release(ITfCompartment *iface) -{ - Compartment *This = impl_from_ITfCompartment(iface); - ULONG ret; - - ret = InterlockedDecrement(&This->refCount); - if (ret == 0) - Compartment_Destructor(This); - return ret; -} - -static HRESULT WINAPI Compartment_SetValue(ITfCompartment *iface, - TfClientId tid, const VARIANT *pvarValue) -{ - Compartment *This = impl_from_ITfCompartment(iface); - ITfCompartmentEventSink *sink; - struct list *cursor; - - TRACE("(%p) %i %p\n",This,tid,pvarValue); - - if (!pvarValue) - return E_INVALIDARG; - - if (!(V_VT(pvarValue) == VT_BSTR || V_VT(pvarValue) == VT_I4 || - V_VT(pvarValue) == VT_UNKNOWN)) - return E_INVALIDARG; - - if (!This->valueData->owner) - This->valueData->owner = tid; - - VariantClear(&This->variant); - - /* Shallow copy of value and type */ - This->variant = *pvarValue; - - if (V_VT(pvarValue) == VT_BSTR) - V_BSTR(&This->variant) = SysAllocStringByteLen((char*)V_BSTR(pvarValue), - SysStringByteLen(V_BSTR(pvarValue))); - else if (V_VT(pvarValue) == VT_UNKNOWN) - IUnknown_AddRef(V_UNKNOWN(&This->variant)); - - SINK_FOR_EACH(cursor, &This->CompartmentEventSink, ITfCompartmentEventSink, sink) - { - ITfCompartmentEventSink_OnChange(sink, &This->valueData->guid); - } - - return S_OK; -} - -static HRESULT WINAPI Compartment_GetValue(ITfCompartment *iface, - VARIANT *pvarValue) -{ - Compartment *This = impl_from_ITfCompartment(iface); - TRACE("(%p) %p\n",This, pvarValue); - - if (!pvarValue) - return E_INVALIDARG; - - VariantInit(pvarValue); - if (V_VT(&This->variant) == VT_EMPTY) return S_FALSE; - return VariantCopy(pvarValue,&This->variant); -} - -static const ITfCompartmentVtbl CompartmentVtbl = -{ - Compartment_QueryInterface, - Compartment_AddRef, - Compartment_Release, - Compartment_SetValue, - Compartment_GetValue -}; - -/***************************************************** - * ITfSource functions - *****************************************************/ - -static HRESULT WINAPI CompartmentSource_QueryInterface(ITfSource *iface, REFIID iid, LPVOID *ppvOut) -{ - Compartment *This = impl_from_ITfSource(iface); - return ITfCompartment_QueryInterface(&This->ITfCompartment_iface, iid, ppvOut); -} - -static ULONG WINAPI CompartmentSource_AddRef(ITfSource *iface) -{ - Compartment *This = impl_from_ITfSource(iface); - return ITfCompartment_AddRef(&This->ITfCompartment_iface); -} - -static ULONG WINAPI CompartmentSource_Release(ITfSource *iface) -{ - Compartment *This = impl_from_ITfSource(iface); - return ITfCompartment_Release(&This->ITfCompartment_iface); -} - -static HRESULT WINAPI CompartmentSource_AdviseSink(ITfSource *iface, - REFIID riid, IUnknown *punk, DWORD *pdwCookie) -{ - Compartment *This = impl_from_ITfSource(iface); - - TRACE("(%p) %s %p %p\n",This,debugstr_guid(riid),punk,pdwCookie); - - if (!riid || !punk || !pdwCookie) - return E_INVALIDARG; - - if (IsEqualIID(riid, &IID_ITfCompartmentEventSink)) - return advise_sink(&This->CompartmentEventSink, &IID_ITfCompartmentEventSink, - COOKIE_MAGIC_COMPARTMENTSINK, punk, pdwCookie); - - FIXME("(%p) Unhandled Sink: %s\n",This,debugstr_guid(riid)); - return E_NOTIMPL; -} - -static HRESULT WINAPI CompartmentSource_UnadviseSink(ITfSource *iface, DWORD pdwCookie) -{ - Compartment *This = impl_from_ITfSource(iface); - - TRACE("(%p) %x\n",This,pdwCookie); - - if (get_Cookie_magic(pdwCookie)!=COOKIE_MAGIC_COMPARTMENTSINK) - return E_INVALIDARG; - - return unadvise_sink(pdwCookie); -} - -static const ITfSourceVtbl CompartmentSourceVtbl = -{ - CompartmentSource_QueryInterface, - CompartmentSource_AddRef, - CompartmentSource_Release, - CompartmentSource_AdviseSink, - CompartmentSource_UnadviseSink, -}; - -static HRESULT Compartment_Constructor(CompartmentValue *valueData, ITfCompartment **ppOut) -{ - Compartment *This; - - This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(Compartment)); - if (This == NULL) - return E_OUTOFMEMORY; - - This->ITfCompartment_iface.lpVtbl= &CompartmentVtbl; - This->ITfSource_iface.lpVtbl = &CompartmentSourceVtbl; - This->refCount = 1; - - This->valueData = valueData; - VariantInit(&This->variant); - - list_init(&This->CompartmentEventSink); - - *ppOut = &This->ITfCompartment_iface; - TRACE("returning %p\n", *ppOut); - return S_OK; -} diff --git a/base/ctf/msctf/compartmentmgr.cpp b/base/ctf/msctf/compartmentmgr.cpp new file mode 100644 index 00000000000..a43a4d5661a --- /dev/null +++ b/base/ctf/msctf/compartmentmgr.cpp @@ -0,0 +1,595 @@ +/* + * PROJECT: ReactOS CTF + * LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later) + * PURPOSE: ITfCompartmentMgr implementation + * COPYRIGHT: Copyright 2009 Aric Stewart, CodeWeavers + * Copyright 2025 Katayama Hirofumi MZ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Cicero +#include +#include +#include + +#include "msctf_internal.h" + +#include +WINE_DEFAULT_DEBUG_CHANNEL(msctf); + +static inline bool cicIsNullPtr(LPCVOID ptr) +{ + return !ptr; +} + +//////////////////////////////////////////////////////////////////////////// + +typedef struct tagCompartmentValue +{ + struct list entry; + GUID guid; + TfClientId owner; + ITfCompartment *compartment; +} CompartmentValue; + +//////////////////////////////////////////////////////////////////////////// + +class CCompartmentMgr + : public ITfCompartmentMgr +{ +public: + CCompartmentMgr(IUnknown *pUnkOuter = NULL); + virtual ~CCompartmentMgr(); + + static HRESULT CreateInstance(IUnknown *pUnkOuter, REFIID riid, IUnknown **ppOut); + + // ** IUnknown methods ** + STDMETHODIMP QueryInterface(REFIID iid, LPVOID *ppvObj) override; + STDMETHODIMP_(ULONG) AddRef() override; + STDMETHODIMP_(ULONG) Release() override; + + // ** ITfCompartmentMgr methods ** + STDMETHODIMP GetCompartment(_In_ REFGUID rguid, _Out_ ITfCompartment **ppcomp) override; + STDMETHODIMP ClearCompartment(_In_ TfClientId tid, _In_ REFGUID rguid) override; + STDMETHODIMP EnumCompartments(_Out_ IEnumGUID **ppEnum) override; + +protected: + LONG m_cRefs; + IUnknown *m_pUnkOuter; + struct list m_values; +}; + +//////////////////////////////////////////////////////////////////////////// + +class CCompartmentEnumGuid + : public IEnumGUID +{ +public: + CCompartmentEnumGuid(); + virtual ~CCompartmentEnumGuid(); + + static HRESULT CreateInstance(struct list *values, IEnumGUID **ppOut); + static HRESULT CreateInstance(struct list *values, IEnumGUID **ppOut, struct list *cursor); + + // ** IUnknown methods ** + STDMETHODIMP QueryInterface(REFIID iid, LPVOID *ppvObj) override; + STDMETHODIMP_(ULONG) AddRef() override; + STDMETHODIMP_(ULONG) Release() override; + + // ** IEnumGUID methods ** + STDMETHODIMP Next( + _In_ ULONG celt, + _Out_ GUID *rgelt, + _Out_ ULONG *pceltFetched) override; + STDMETHODIMP Skip(_In_ ULONG celt) override; + STDMETHODIMP Reset() override; + STDMETHODIMP Clone(_Out_ IEnumGUID **ppenum) override; + +protected: + LONG m_cRefs; + struct list *m_values; + struct list *m_cursor; +}; + +//////////////////////////////////////////////////////////////////////////// + +class CCompartment + : public ITfCompartment + , public ITfSource +{ +public: + CCompartment(); + virtual ~CCompartment(); + + static HRESULT CreateInstance(CompartmentValue *valueData, ITfCompartment **ppOut); + + // ** IUnknown methods ** + STDMETHODIMP QueryInterface(REFIID iid, LPVOID *ppvObj) override; + STDMETHODIMP_(ULONG) AddRef() override; + STDMETHODIMP_(ULONG) Release() override; + + // ** ITfCompartment methods ** + STDMETHODIMP SetValue(_In_ TfClientId tid, _In_ const VARIANT *pvarValue) override; + STDMETHODIMP GetValue(_Out_ VARIANT *pvarValue) override; + + // ** ITfSource methods ** + STDMETHODIMP AdviseSink( + _In_ REFIID riid, + _In_ IUnknown *punk, + _Out_ DWORD *pdwCookie) override; + STDMETHODIMP UnadviseSink(_In_ DWORD dwCookie) override; + +protected: + LONG m_cRefs; + VARIANT m_variant; // Only VT_I4, VT_UNKNOWN and VT_BSTR data types are allowed + CompartmentValue *m_valueData; + struct list m_CompartmentEventSink; +}; + +//////////////////////////////////////////////////////////////////////////// + +CCompartmentMgr::CCompartmentMgr(IUnknown *pUnkOuter) + : m_cRefs(1) + , m_pUnkOuter(pUnkOuter) +{ + list_init(&m_values); +} + +CCompartmentMgr::~CCompartmentMgr() +{ + struct list *cursor, *cursor2; + + LIST_FOR_EACH_SAFE(cursor, cursor2, &m_values) + { + CompartmentValue *value = LIST_ENTRY(cursor, CompartmentValue, entry); + list_remove(cursor); + value->compartment->Release(); + HeapFree(GetProcessHeap(), 0, value); + } +} + +HRESULT CCompartmentMgr::CreateInstance(IUnknown *pUnkOuter, REFIID riid, IUnknown **ppOut) +{ + if (!ppOut) + return E_POINTER; + + if (pUnkOuter && riid != IID_IUnknown) + return CLASS_E_NOAGGREGATION; + + CCompartmentMgr *This = new(cicNoThrow) CCompartmentMgr(pUnkOuter); + if (This == NULL) + return E_OUTOFMEMORY; + + if (pUnkOuter) + { + *ppOut = static_cast(This); + TRACE("returning %p\n", *ppOut); + return S_OK; + } + else + { + HRESULT hr; + hr = This->QueryInterface(riid, (void **)ppOut); + if (FAILED(hr)) + delete This; + return hr; + } +} + +STDMETHODIMP CCompartmentMgr::QueryInterface(REFIID iid, LPVOID *ppvOut) +{ + if (m_pUnkOuter) + return m_pUnkOuter->QueryInterface(iid, ppvOut); + + *ppvOut = NULL; + + if (iid == IID_IUnknown || iid == IID_ITfCompartmentMgr) + *ppvOut = static_cast(this); + + if (*ppvOut) + { + AddRef(); + return S_OK; + } + + WARN("unsupported interface: %s\n", debugstr_guid(&iid)); + return E_NOINTERFACE; +} + +STDMETHODIMP_(ULONG) CCompartmentMgr::AddRef() +{ + if (m_pUnkOuter) + return m_pUnkOuter->AddRef(); + return ::InterlockedIncrement(&m_cRefs); +} + +STDMETHODIMP_(ULONG) CCompartmentMgr::Release() +{ + if (m_pUnkOuter) + return m_pUnkOuter->Release(); + if (::InterlockedDecrement(&m_cRefs) == 0) + { + delete this; + return 0; + } + return m_cRefs; +} + +HRESULT CCompartmentMgr::GetCompartment(_In_ REFGUID rguid, _Out_ ITfCompartment **ppcomp) +{ + if (!ppcomp) + return E_POINTER; + CompartmentValue* value; + struct list *cursor; + HRESULT hr; + + TRACE("(%p) %s %p\n", this, debugstr_guid(&rguid), ppcomp); + + LIST_FOR_EACH(cursor, &m_values) + { + value = LIST_ENTRY(cursor, CompartmentValue, entry); + if (rguid == value->guid) + { + value->compartment->AddRef(); + *ppcomp = value->compartment; + return S_OK; + } + } + + value = (CompartmentValue *)HeapAlloc(GetProcessHeap(), 0, sizeof(CompartmentValue)); + if (!value) + return E_OUTOFMEMORY; + + value->guid = rguid; + value->owner = 0; + hr = CCompartment::CreateInstance(value, &value->compartment); + if (SUCCEEDED(hr)) + { + list_add_head(&m_values, &value->entry); + value->compartment->AddRef(); + *ppcomp = value->compartment; + } + else + { + HeapFree(GetProcessHeap(), 0, value); + *ppcomp = NULL; + } + + return hr; +} + +HRESULT CCompartmentMgr::ClearCompartment(_In_ TfClientId tid, _In_ REFGUID rguid) +{ + struct list *cursor; + + TRACE("(%p) %i %s\n", this, tid, debugstr_guid(&rguid)); + + LIST_FOR_EACH(cursor, &m_values) + { + CompartmentValue *value = LIST_ENTRY(cursor,CompartmentValue, entry); + if (rguid == value->guid) + { + if (value->owner && tid != value->owner) + return E_UNEXPECTED; + list_remove(cursor); + value->compartment->Release(); + HeapFree(GetProcessHeap(), 0, value); + return S_OK; + } + } + + return CONNECT_E_NOCONNECTION; +} + +HRESULT CCompartmentMgr::EnumCompartments(_Out_ IEnumGUID **ppEnum) +{ + TRACE("(%p) %p\n", this, ppEnum); + if (!ppEnum) + return E_INVALIDARG; + + return CCompartmentEnumGuid::CreateInstance(&m_values, ppEnum); +} + +//////////////////////////////////////////////////////////////////////////// + +CCompartmentEnumGuid::CCompartmentEnumGuid() + : m_cRefs(1) + , m_values(NULL) + , m_cursor(NULL) +{ +} + +CCompartmentEnumGuid::~CCompartmentEnumGuid() +{ + TRACE("destroying %p\n", this); +} + +HRESULT +CCompartmentEnumGuid::CreateInstance(struct list *values, IEnumGUID **ppOut) +{ + CCompartmentEnumGuid *This = new(cicNoThrow) CCompartmentEnumGuid(); + if (This == NULL) + return E_OUTOFMEMORY; + + This->m_values = values; + This->m_cursor = list_head(values); + + *ppOut = static_cast(This); + TRACE("returning %p\n", *ppOut); + return S_OK; +} + +HRESULT +CCompartmentEnumGuid::CreateInstance(struct list *values, IEnumGUID **ppOut, struct list *cursor) +{ + CCompartmentEnumGuid *This = new(cicNoThrow) CCompartmentEnumGuid(); + if (This == NULL) + return E_OUTOFMEMORY; + + This->m_values = values; + This->m_cursor = cursor; + + *ppOut = static_cast(This); + TRACE("returning %p\n", *ppOut); + return S_OK; +} + +STDMETHODIMP CCompartmentEnumGuid::QueryInterface(REFIID iid, LPVOID *ppvObj) +{ + *ppvObj = NULL; + + if (iid == IID_IUnknown || iid == IID_IEnumGUID) + { + *ppvObj = static_cast(this); + } + + if (*ppvObj) + { + AddRef(); + return S_OK; + } + + WARN("unsupported interface: %s\n", debugstr_guid(&iid)); + return E_NOINTERFACE; +} + +STDMETHODIMP_(ULONG) CCompartmentEnumGuid::AddRef() +{ + return ::InterlockedIncrement(&m_cRefs); +} + +STDMETHODIMP_(ULONG) CCompartmentEnumGuid::Release() +{ + if (::InterlockedDecrement(&m_cRefs) == 0) + { + delete this; + return 0; + } + return m_cRefs; +} + +STDMETHODIMP CCompartmentEnumGuid::Next( + _In_ ULONG celt, + _Out_ GUID *rgelt, + _Out_ ULONG *pceltFetched) +{ + ULONG fetched = 0; + + TRACE("(%p)\n", this); + + if (rgelt == NULL) + return E_POINTER; + + while (fetched < celt && m_cursor) + { + CompartmentValue* value = LIST_ENTRY(m_cursor, CompartmentValue, entry); + if (!value) + break; + + m_cursor = list_next(m_values, m_cursor); + *rgelt = value->guid; + + ++fetched; + ++rgelt; + } + + if (pceltFetched) + *pceltFetched = fetched; + return fetched == celt ? S_OK : S_FALSE; +} + +STDMETHODIMP CCompartmentEnumGuid::Skip(_In_ ULONG celt) +{ + TRACE("(%p)\n", this); + m_cursor = list_next(m_values, m_cursor); + return S_OK; +} + +STDMETHODIMP CCompartmentEnumGuid::Reset() +{ + TRACE("(%p)\n", this); + m_cursor = list_head(m_values); + return S_OK; +} + +STDMETHODIMP CCompartmentEnumGuid::Clone(_Out_ IEnumGUID **ppenum) +{ + TRACE("(%p)\n", this); + + if (ppenum == NULL) + return E_POINTER; + + return CCompartmentEnumGuid::CreateInstance(m_values, ppenum, m_cursor); +} + +//////////////////////////////////////////////////////////////////////////// + +CCompartment::CCompartment() + : m_cRefs(1) + , m_valueData(NULL) +{ + VariantInit(&m_variant); +} + +CCompartment::~CCompartment() +{ + VariantClear(&m_variant); + free_sinks(&m_CompartmentEventSink); +} + +HRESULT CCompartment::CreateInstance(CompartmentValue *valueData, ITfCompartment **ppOut) +{ + CCompartment *This = new(cicNoThrow) CCompartment(); + if (This == NULL) + return E_OUTOFMEMORY; + + This->m_valueData = valueData; + VariantInit(&This->m_variant); + + list_init(&This->m_CompartmentEventSink); + + *ppOut = static_cast(This); + TRACE("returning %p\n", *ppOut); + return S_OK; +} + +STDMETHODIMP CCompartment::QueryInterface(REFIID iid, LPVOID *ppvObj) +{ + *ppvObj = NULL; + + if (iid == IID_IUnknown || iid == IID_ITfCompartment) + *ppvObj = static_cast(this); + else if (iid == IID_ITfSource) + *ppvObj = static_cast(this); + + if (*ppvObj) + { + AddRef(); + return S_OK; + } + + WARN("unsupported interface: %s\n", debugstr_guid(&iid)); + return E_NOINTERFACE; +} + +STDMETHODIMP_(ULONG) CCompartment::AddRef() +{ + return ::InterlockedIncrement(&m_cRefs); +} + +STDMETHODIMP_(ULONG) CCompartment::Release() +{ + if (::InterlockedDecrement(&m_cRefs) == 0) + { + delete this; + return 0; + } + return m_cRefs; +} + +STDMETHODIMP CCompartment::SetValue(_In_ TfClientId tid, _In_ const VARIANT *pvarValue) +{ + ITfCompartmentEventSink *sink; + struct list *cursor; + + TRACE("(%p) %i %p\n", this, tid, pvarValue); + + if (!pvarValue) + return E_INVALIDARG; + + if (!(V_VT(pvarValue) == VT_BSTR || V_VT(pvarValue) == VT_I4 || + V_VT(pvarValue) == VT_UNKNOWN)) + return E_INVALIDARG; + + if (!m_valueData->owner) + m_valueData->owner = tid; + + ::VariantClear(&m_variant); + + /* Shallow copy of value and type */ + m_variant = *pvarValue; + + if (V_VT(pvarValue) == VT_BSTR) + { + V_BSTR(&m_variant) = ::SysAllocStringByteLen((char*)V_BSTR(pvarValue), + ::SysStringByteLen(V_BSTR(pvarValue))); + } + else if (V_VT(pvarValue) == VT_UNKNOWN) + { + V_UNKNOWN(&m_variant)->AddRef(); + } + + SINK_FOR_EACH(cursor, &m_CompartmentEventSink, ITfCompartmentEventSink, sink) + { + sink->OnChange(m_valueData->guid); + } + + return S_OK; +} + +STDMETHODIMP CCompartment::GetValue(_Out_ VARIANT *pvarValue) +{ + TRACE("(%p) %p\n", this, pvarValue); + + if (!pvarValue) + return E_INVALIDARG; + + ::VariantInit(pvarValue); + if (V_VT(&m_variant) == VT_EMPTY) + return S_FALSE; + return ::VariantCopy(pvarValue, &m_variant); +} + +STDMETHODIMP CCompartment::AdviseSink( + _In_ REFIID riid, + _In_ IUnknown *punk, + _Out_ DWORD *pdwCookie) +{ + TRACE("(%p) %s %p %p\n", this, debugstr_guid(&riid), punk, pdwCookie); + + if (cicIsNullPtr(&riid) || !punk || !pdwCookie) + return E_INVALIDARG; + + if (riid == IID_ITfCompartmentEventSink) + { + return advise_sink(&m_CompartmentEventSink, IID_ITfCompartmentEventSink, + COOKIE_MAGIC_COMPARTMENTSINK, punk, pdwCookie); + } + + FIXME("(%p) Unhandled Sink: %s\n", this, debugstr_guid(&riid)); + return E_NOTIMPL; +} + +STDMETHODIMP CCompartment::UnadviseSink(_In_ DWORD dwCookie) +{ + TRACE("(%p) %x\n", this, dwCookie); + + if (get_Cookie_magic(dwCookie) != COOKIE_MAGIC_COMPARTMENTSINK) + return E_INVALIDARG; + + return unadvise_sink(dwCookie); +} + +//////////////////////////////////////////////////////////////////////////// + +EXTERN_C +HRESULT CompartmentMgr_Constructor(IUnknown *pUnkOuter, REFIID riid, IUnknown **ppOut) +{ + return CCompartmentMgr::CreateInstance(pUnkOuter, riid, ppOut); +} + +EXTERN_C +HRESULT CompartmentMgr_Destructor(ITfCompartmentMgr *iface) +{ + iface->Release(); + return S_OK; +}