From 06343fa9a5ba6c341f4b16615cafcbe0003ed128 Mon Sep 17 00:00:00 2001 From: Katayama Hirofumi MZ Date: Tue, 1 Jul 2025 21:39:26 +0900 Subject: [PATCH] [MSCTF] Make category manager C++ (#8210) Implementing missing features... JIRA issue: CORE-19361 - Delete categorymgr.c and add categorymgr.cpp. - Make category manager implementation C++. --- base/ctf/msctf/CMakeLists.txt | 2 +- base/ctf/msctf/categorymgr.c | 429 ------------------------------- base/ctf/msctf/categorymgr.cpp | 446 +++++++++++++++++++++++++++++++++ 3 files changed, 447 insertions(+), 430 deletions(-) delete mode 100644 base/ctf/msctf/categorymgr.c create mode 100644 base/ctf/msctf/categorymgr.cpp diff --git a/base/ctf/msctf/CMakeLists.txt b/base/ctf/msctf/CMakeLists.txt index 9619476ff78..b9a2b9fca94 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 - categorymgr.c compartmentmgr.c context.c inputprocessor.c @@ -15,6 +14,7 @@ list(APPEND SOURCE ${CMAKE_CURRENT_BINARY_DIR}/msctf_stubs.c) list(APPEND PCH_SKIP_SOURCE + categorymgr.cpp displayattributemgr.cpp documentmgr.cpp langbarmgr.cpp diff --git a/base/ctf/msctf/categorymgr.c b/base/ctf/msctf/categorymgr.c deleted file mode 100644 index ccba097331e..00000000000 --- a/base/ctf/msctf/categorymgr.c +++ /dev/null @@ -1,429 +0,0 @@ -/* - * ITfCategoryMgr 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 -#ifdef __REACTOS__ -#include -#endif - -#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 "msctf.h" -#include "msctf_internal.h" - -WINE_DEFAULT_DEBUG_CHANNEL(msctf); - -typedef struct tagCategoryMgr { - ITfCategoryMgr ITfCategoryMgr_iface; - LONG refCount; -} CategoryMgr; - -static inline CategoryMgr *impl_from_ITfCategoryMgr(ITfCategoryMgr *iface) -{ - return CONTAINING_RECORD(iface, CategoryMgr, ITfCategoryMgr_iface); -} - -static void CategoryMgr_Destructor(CategoryMgr *This) -{ - TRACE("destroying %p\n", This); - HeapFree(GetProcessHeap(),0,This); -} - -static HRESULT WINAPI CategoryMgr_QueryInterface(ITfCategoryMgr *iface, REFIID iid, LPVOID *ppvOut) -{ - CategoryMgr *This = impl_from_ITfCategoryMgr(iface); - *ppvOut = NULL; - - if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ITfCategoryMgr)) - { - *ppvOut = &This->ITfCategoryMgr_iface; - } - - if (*ppvOut) - { - ITfCategoryMgr_AddRef(iface); - return S_OK; - } - - WARN("unsupported interface: %s\n", debugstr_guid(iid)); - return E_NOINTERFACE; -} - -static ULONG WINAPI CategoryMgr_AddRef(ITfCategoryMgr *iface) -{ - CategoryMgr *This = impl_from_ITfCategoryMgr(iface); - return InterlockedIncrement(&This->refCount); -} - -static ULONG WINAPI CategoryMgr_Release(ITfCategoryMgr *iface) -{ - CategoryMgr *This = impl_from_ITfCategoryMgr(iface); - ULONG ret; - - ret = InterlockedDecrement(&This->refCount); - if (ret == 0) - CategoryMgr_Destructor(This); - return ret; -} - -/***************************************************** - * ITfCategoryMgr functions - *****************************************************/ - -static HRESULT WINAPI CategoryMgr_RegisterCategory ( ITfCategoryMgr *iface, - REFCLSID rclsid, REFGUID rcatid, REFGUID rguid) -{ - WCHAR fullkey[110]; - WCHAR buf[39]; - WCHAR buf2[39]; - ULONG res; - HKEY tipkey,catkey,itmkey; - CategoryMgr *This = impl_from_ITfCategoryMgr(iface); - - static const WCHAR ctg[] = {'C','a','t','e','g','o','r','y',0}; - static const WCHAR itm[] = {'I','t','e','m',0}; - static const WCHAR fmt[] = {'%','s','\\','%','s',0}; - static const WCHAR fmt2[] = {'%','s','\\','%','s','\\','%','s','\\','%','s',0}; - - TRACE("(%p) %s %s %s\n",This,debugstr_guid(rclsid), debugstr_guid(rcatid), debugstr_guid(rguid)); - - StringFromGUID2(rclsid, buf, 39); - swprintf(fullkey,fmt,szwSystemTIPKey,buf); - - if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,fullkey, 0, KEY_READ | KEY_WRITE, - &tipkey ) != ERROR_SUCCESS) - return E_FAIL; - - StringFromGUID2(rcatid, buf, 39); - StringFromGUID2(rguid, buf2, 39); - swprintf(fullkey,fmt2,ctg,ctg,buf,buf2); - - res = RegCreateKeyExW(tipkey, fullkey, 0, NULL, 0, KEY_READ | KEY_WRITE, - NULL, &catkey, NULL); - RegCloseKey(catkey); - - if (!res) - { - swprintf(fullkey,fmt2,ctg,itm,buf2,buf); - res = RegCreateKeyExW(tipkey, fullkey, 0, NULL, 0, KEY_READ | KEY_WRITE, - NULL, &itmkey, NULL); - - RegCloseKey(itmkey); - } - - RegCloseKey(tipkey); - - if (!res) - return S_OK; - else - return E_FAIL; -} - -static HRESULT WINAPI CategoryMgr_UnregisterCategory ( ITfCategoryMgr *iface, - REFCLSID rclsid, REFGUID rcatid, REFGUID rguid) -{ - WCHAR fullkey[110]; - WCHAR buf[39]; - WCHAR buf2[39]; - HKEY tipkey; - CategoryMgr *This = impl_from_ITfCategoryMgr(iface); - - static const WCHAR ctg[] = {'C','a','t','e','g','o','r','y',0}; - static const WCHAR itm[] = {'I','t','e','m',0}; - static const WCHAR fmt[] = {'%','s','\\','%','s',0}; - static const WCHAR fmt2[] = {'%','s','\\','%','s','\\','%','s','\\','%','s',0}; - - TRACE("(%p) %s %s %s\n",This,debugstr_guid(rclsid), debugstr_guid(rcatid), debugstr_guid(rguid)); - - StringFromGUID2(rclsid, buf, 39); - swprintf(fullkey,fmt,szwSystemTIPKey,buf); - - if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,fullkey, 0, KEY_READ | KEY_WRITE, - &tipkey ) != ERROR_SUCCESS) - return E_FAIL; - - StringFromGUID2(rcatid, buf, 39); - StringFromGUID2(rguid, buf2, 39); - swprintf(fullkey,fmt2,ctg,ctg,buf,buf2); - RegDeleteTreeW(tipkey, fullkey); - swprintf(fullkey,fmt2,ctg,itm,buf2,buf); - RegDeleteTreeW(tipkey, fullkey); - - RegCloseKey(tipkey); - return S_OK; -} - -static HRESULT WINAPI CategoryMgr_EnumCategoriesInItem ( ITfCategoryMgr *iface, - REFGUID rguid, IEnumGUID **ppEnum) -{ - CategoryMgr *This = impl_from_ITfCategoryMgr(iface); - FIXME("STUB:(%p)\n",This); - return E_NOTIMPL; -} - -static HRESULT WINAPI CategoryMgr_EnumItemsInCategory ( ITfCategoryMgr *iface, - REFGUID rcatid, IEnumGUID **ppEnum) -{ - CategoryMgr *This = impl_from_ITfCategoryMgr(iface); - FIXME("STUB:(%p)\n",This); - return E_NOTIMPL; -} - -static HRESULT WINAPI CategoryMgr_FindClosestCategory ( ITfCategoryMgr *iface, - REFGUID rguid, GUID *pcatid, const GUID **ppcatidList, ULONG ulCount) -{ - static const WCHAR fmt[] = { '%','s','\\','%','s','\\','C','a','t','e','g','o','r','y','\\','I','t','e','m','\\','%','s',0}; - - WCHAR fullkey[120]; - WCHAR buf[39]; - HKEY key; - HRESULT hr = S_FALSE; - INT index = 0; - CategoryMgr *This = impl_from_ITfCategoryMgr(iface); - - TRACE("(%p)\n",This); - - if (!pcatid || (ulCount && ppcatidList == NULL)) - return E_INVALIDARG; - - StringFromGUID2(rguid, buf, 39); - swprintf(fullkey,fmt,szwSystemTIPKey,buf,buf); - *pcatid = GUID_NULL; - - if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,fullkey, 0, KEY_READ, &key ) != - ERROR_SUCCESS) - return S_FALSE; - - while (1) - { - HRESULT hr2; - ULONG res; - GUID guid; - WCHAR catid[39]; - DWORD cName; - - cName = 39; - res = RegEnumKeyExW(key, index, catid, &cName, NULL, NULL, NULL, NULL); - if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA) break; - index ++; - - hr2 = CLSIDFromString(catid, &guid); - if (FAILED(hr2)) continue; - - if (ulCount) - { - ULONG j; - BOOL found = FALSE; - for (j = 0; j < ulCount; j++) - if (IsEqualGUID(&guid, ppcatidList[j])) - { - found = TRUE; - *pcatid = guid; - hr = S_OK; - break; - } - if (found) break; - } - else - { - *pcatid = guid; - hr = S_OK; - break; - } - } - - return hr; -} - -static HRESULT WINAPI CategoryMgr_RegisterGUIDDescription ( - ITfCategoryMgr *iface, REFCLSID rclsid, REFGUID rguid, - const WCHAR *pchDesc, ULONG cch) -{ - CategoryMgr *This = impl_from_ITfCategoryMgr(iface); - FIXME("STUB:(%p)\n",This); - return E_NOTIMPL; -} - -static HRESULT WINAPI CategoryMgr_UnregisterGUIDDescription ( - ITfCategoryMgr *iface, REFCLSID rclsid, REFGUID rguid) -{ - CategoryMgr *This = impl_from_ITfCategoryMgr(iface); - FIXME("STUB:(%p)\n",This); - return E_NOTIMPL; -} - -static HRESULT WINAPI CategoryMgr_GetGUIDDescription ( ITfCategoryMgr *iface, - REFGUID rguid, BSTR *pbstrDesc) -{ - CategoryMgr *This = impl_from_ITfCategoryMgr(iface); - FIXME("STUB:(%p)\n",This); - return E_NOTIMPL; -} - -static HRESULT WINAPI CategoryMgr_RegisterGUIDDWORD ( ITfCategoryMgr *iface, - REFCLSID rclsid, REFGUID rguid, DWORD dw) -{ - CategoryMgr *This = impl_from_ITfCategoryMgr(iface); - FIXME("STUB:(%p)\n",This); - return E_NOTIMPL; -} - -static HRESULT WINAPI CategoryMgr_UnregisterGUIDDWORD ( ITfCategoryMgr *iface, - REFCLSID rclsid, REFGUID rguid) -{ - CategoryMgr *This = impl_from_ITfCategoryMgr(iface); - FIXME("STUB:(%p)\n",This); - return E_NOTIMPL; -} - -static HRESULT WINAPI CategoryMgr_GetGUIDDWORD ( ITfCategoryMgr *iface, - REFGUID rguid, DWORD *pdw) -{ - CategoryMgr *This = impl_from_ITfCategoryMgr(iface); - FIXME("STUB:(%p)\n",This); - return E_NOTIMPL; -} - -static HRESULT WINAPI CategoryMgr_RegisterGUID ( ITfCategoryMgr *iface, - REFGUID rguid, TfGuidAtom *pguidatom -) -{ - DWORD index; - GUID *checkguid; - DWORD id; - CategoryMgr *This = impl_from_ITfCategoryMgr(iface); - - TRACE("(%p) %s %p\n",This,debugstr_guid(rguid),pguidatom); - - if (!pguidatom) - return E_INVALIDARG; - - index = 0; - do { - id = enumerate_Cookie(COOKIE_MAGIC_GUIDATOM,&index); - if (id && IsEqualGUID(rguid,get_Cookie_data(id))) - { - *pguidatom = id; - return S_OK; - } - } while(id); - - checkguid = HeapAlloc(GetProcessHeap(),0,sizeof(GUID)); - *checkguid = *rguid; - id = generate_Cookie(COOKIE_MAGIC_GUIDATOM,checkguid); - - if (!id) - { - HeapFree(GetProcessHeap(),0,checkguid); - return E_FAIL; - } - - *pguidatom = id; - - return S_OK; -} - -static HRESULT WINAPI CategoryMgr_GetGUID ( ITfCategoryMgr *iface, - TfGuidAtom guidatom, GUID *pguid) -{ - CategoryMgr *This = impl_from_ITfCategoryMgr(iface); - - TRACE("(%p) %i\n",This,guidatom); - - if (!pguid) - return E_INVALIDARG; - - *pguid = GUID_NULL; - - if (get_Cookie_magic(guidatom) == COOKIE_MAGIC_GUIDATOM) - *pguid = *((REFGUID)get_Cookie_data(guidatom)); - - return S_OK; -} - -static HRESULT WINAPI CategoryMgr_IsEqualTfGuidAtom ( ITfCategoryMgr *iface, - TfGuidAtom guidatom, REFGUID rguid, BOOL *pfEqual) -{ - CategoryMgr *This = impl_from_ITfCategoryMgr(iface); - - TRACE("(%p) %i %s %p\n",This,guidatom,debugstr_guid(rguid),pfEqual); - - if (!pfEqual) - return E_INVALIDARG; - - *pfEqual = FALSE; - if (get_Cookie_magic(guidatom) == COOKIE_MAGIC_GUIDATOM) - { - if (IsEqualGUID(rguid,get_Cookie_data(guidatom))) - *pfEqual = TRUE; - } - - return S_OK; -} - - -static const ITfCategoryMgrVtbl CategoryMgrVtbl = -{ - CategoryMgr_QueryInterface, - CategoryMgr_AddRef, - CategoryMgr_Release, - CategoryMgr_RegisterCategory, - CategoryMgr_UnregisterCategory, - CategoryMgr_EnumCategoriesInItem, - CategoryMgr_EnumItemsInCategory, - CategoryMgr_FindClosestCategory, - CategoryMgr_RegisterGUIDDescription, - CategoryMgr_UnregisterGUIDDescription, - CategoryMgr_GetGUIDDescription, - CategoryMgr_RegisterGUIDDWORD, - CategoryMgr_UnregisterGUIDDWORD, - CategoryMgr_GetGUIDDWORD, - CategoryMgr_RegisterGUID, - CategoryMgr_GetGUID, - CategoryMgr_IsEqualTfGuidAtom -}; - -HRESULT CategoryMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) -{ - CategoryMgr *This; - if (pUnkOuter) - return CLASS_E_NOAGGREGATION; - - This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(CategoryMgr)); - if (This == NULL) - return E_OUTOFMEMORY; - - This->ITfCategoryMgr_iface.lpVtbl = &CategoryMgrVtbl; - This->refCount = 1; - - *ppOut = (IUnknown *)&This->ITfCategoryMgr_iface; - TRACE("returning %p\n", *ppOut); - return S_OK; -} diff --git a/base/ctf/msctf/categorymgr.cpp b/base/ctf/msctf/categorymgr.cpp new file mode 100644 index 00000000000..688b7bee77d --- /dev/null +++ b/base/ctf/msctf/categorymgr.cpp @@ -0,0 +1,446 @@ +/* + * PROJECT: ReactOS CTF + * LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later) + * PURPOSE: ITfCategoryMgr implementation + * COPYRIGHT: Copyright 2009 Aric Stewart, CodeWeavers + * Copyright 2025 Katayama Hirofumi MZ + */ + +#include +#include +#include +#include +#include +#include +#include + +// Cicero +#include +#include +#include + +#include "msctf_internal.h" + +#include +WINE_DEFAULT_DEBUG_CHANNEL(msctf); + +//////////////////////////////////////////////////////////////////////////// + +class CCategoryMgr + : public ITfCategoryMgr +{ +public: + CCategoryMgr(); + virtual ~CCategoryMgr(); + + // ** IUnknown methods ** + STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj) override; + STDMETHODIMP_(ULONG) AddRef() override; + STDMETHODIMP_(ULONG) Release() override; + + // ** ITfCategoryMgr methods ** + STDMETHODIMP RegisterCategory( + _In_ REFCLSID rclsid, + _In_ REFGUID rcatid, + _In_ REFGUID rguid) override; + STDMETHODIMP UnregisterCategory( + _In_ REFCLSID rclsid, + _In_ REFGUID rcatid, + _In_ REFGUID rguid) override; + STDMETHODIMP EnumCategoriesInItem( + _In_ REFGUID rguid, + _Out_ IEnumGUID **ppEnum) override; + STDMETHODIMP EnumItemsInCategory( + _In_ REFGUID rcatid, + _Out_ IEnumGUID **ppEnum) override; + STDMETHODIMP FindClosestCategory( + _In_ REFGUID rguid, + _Out_ GUID *pcatid, + _In_ const GUID **ppcatidList, + _In_ ULONG ulCount) override; + STDMETHODIMP RegisterGUIDDescription( + _In_ REFCLSID rclsid, + _In_ REFGUID rguid, + _In_ const WCHAR *pchDesc, + _In_ ULONG cch) override; + STDMETHODIMP UnregisterGUIDDescription( + _In_ REFCLSID rclsid, + _In_ REFGUID rguid) override; + STDMETHODIMP GetGUIDDescription( + _In_ REFGUID rguid, + _Out_ BSTR *pbstrDesc) override; + STDMETHODIMP RegisterGUIDDWORD( + _In_ REFCLSID rclsid, + _In_ REFGUID rguid, + _In_ DWORD dw) override; + STDMETHODIMP UnregisterGUIDDWORD( + _In_ REFCLSID rclsid, + _In_ REFGUID rguid) override; + STDMETHODIMP GetGUIDDWORD( + _In_ REFGUID rguid, + _Out_ DWORD *pdw) override; + STDMETHODIMP RegisterGUID( + _In_ REFGUID rguid, + _Out_ TfGuidAtom *pguidatom) override; + STDMETHODIMP GetGUID( + _In_ TfGuidAtom guidatom, + _Out_ GUID *pguid) override; + STDMETHODIMP IsEqualTfGuidAtom( + _In_ TfGuidAtom guidatom, + _In_ REFGUID rguid, + _Out_ BOOL *pfEqual) override; + +protected: + LONG m_cRefs; +}; + +//////////////////////////////////////////////////////////////////////////// + +CCategoryMgr::CCategoryMgr() + : m_cRefs(1) +{ +} + +CCategoryMgr::~CCategoryMgr() +{ + TRACE("destroying %p\n", this); +} + +//////////////////////////////////////////////////////////////////////////// +// ** IUnknown methods ** + +STDMETHODIMP CCategoryMgr::QueryInterface(REFIID riid, void **ppvObj) +{ + if (!ppvObj) + return E_INVALIDARG; + + *ppvObj = NULL; + + if (riid == IID_IUnknown || riid == IID_ITfCategoryMgr) + *ppvObj = this; + + if (!*ppvObj) + return E_NOINTERFACE; + + AddRef(); + return S_OK; +} + +STDMETHODIMP_(ULONG) CCategoryMgr::AddRef() +{ + return ::InterlockedIncrement(&m_cRefs); +} + +STDMETHODIMP_(ULONG) CCategoryMgr::Release() +{ + if (::InterlockedDecrement(&m_cRefs) == 0) + { + delete this; + return 0; + } + return m_cRefs; +} + +//////////////////////////////////////////////////////////////////////////// +// ** ITfCategoryMgr methods ** + +STDMETHODIMP CCategoryMgr::RegisterCategory( + _In_ REFCLSID rclsid, + _In_ REFGUID rcatid, + _In_ REFGUID rguid) +{ + WCHAR szFullKey[110], szClsid[39], szCatid[39], szGuid[39]; + HKEY hTipKey = NULL, hCatKey = NULL, hItemKey = NULL; + LSTATUS error; + HRESULT hr = E_FAIL; + + TRACE("%p -> (%s, %s, %s)\n", this, debugstr_guid(&rclsid), debugstr_guid(&rcatid), + debugstr_guid(&rguid)); + + StringFromGUID2(rclsid, szClsid, _countof(szClsid)); + StringFromGUID2(rcatid, szCatid, _countof(szCatid)); + StringFromGUID2(rguid, szGuid, _countof(szGuid)); + + swprintf(szFullKey, L"%s\\%s", szwSystemTIPKey, szClsid); + error = RegOpenKeyExW(HKEY_LOCAL_MACHINE, szFullKey, 0, KEY_READ | KEY_WRITE, &hTipKey); + if (error != ERROR_SUCCESS) + return E_FAIL; + + swprintf(szFullKey, L"Category\\Category\\%s\\%s", szCatid, szGuid); + error = RegCreateKeyExW(hTipKey, szFullKey, 0, NULL, 0, KEY_READ | KEY_WRITE, NULL, + &hCatKey, NULL); + if (error == ERROR_SUCCESS) + { + RegCloseKey(hCatKey); + + swprintf(szFullKey, L"Category\\Item\\%s\\%s", szGuid, szCatid); + error = RegCreateKeyExW(hTipKey, szFullKey, 0, NULL, 0, KEY_READ | KEY_WRITE, NULL, + &hItemKey, NULL); + if (error == ERROR_SUCCESS) + { + RegCloseKey(hItemKey); + hr = S_OK; + } + } + + RegCloseKey(hTipKey); + return hr; +} + +STDMETHODIMP CCategoryMgr::UnregisterCategory( + _In_ REFCLSID rclsid, + _In_ REFGUID rcatid, + _In_ REFGUID rguid) +{ + WCHAR szFullKey[110], szClsid[39], szCatid[39], szGuid[39]; + HKEY hTipKey = NULL; + LSTATUS error; + + TRACE("%p -> (%s %s %s)\n", this, debugstr_guid(&rclsid), debugstr_guid(&rcatid), + debugstr_guid(&rguid)); + + StringFromGUID2(rclsid, szClsid, _countof(szClsid)); + StringFromGUID2(rcatid, szCatid, _countof(szCatid)); + StringFromGUID2(rguid, szGuid, _countof(szGuid)); + + swprintf(szFullKey, L"%s\\%s", szwSystemTIPKey, szClsid); + error = RegOpenKeyExW(HKEY_LOCAL_MACHINE, szFullKey, 0, KEY_READ | KEY_WRITE, &hTipKey); + if (error != ERROR_SUCCESS) + return E_FAIL; + + swprintf(szFullKey, L"Category\\Category\\%s\\%s", szCatid, szGuid); + RegDeleteTreeW(hTipKey, szFullKey); + + swprintf(szFullKey, L"Category\\Item\\%s\\%s", szGuid, szCatid); + RegDeleteTreeW(hTipKey, szFullKey); + + RegCloseKey(hTipKey); + return S_OK; +} + +STDMETHODIMP CCategoryMgr::EnumCategoriesInItem( + _In_ REFGUID rguid, + _Out_ IEnumGUID **ppEnum) +{ + FIXME("STUB:(%p)\n", this); + return E_NOTIMPL; +} + +STDMETHODIMP CCategoryMgr::EnumItemsInCategory( + _In_ REFGUID rcatid, + _Out_ IEnumGUID **ppEnum) +{ + FIXME("STUB:(%p)\n", this); + return E_NOTIMPL; +} + +STDMETHODIMP CCategoryMgr::FindClosestCategory( + _In_ REFGUID rguid, + _Out_ GUID *pcatid, + _In_ const GUID **ppcatidList, + _In_ ULONG ulCount) +{ + WCHAR szFullKey[120], szGuid[39]; + HKEY hKey = NULL; + HRESULT hr = S_FALSE; + DWORD dwIndex = 0; + LSTATUS error; + + TRACE("(%p)\n", this); + + if (!pcatid || (ulCount > 0 && ppcatidList == NULL)) + return E_INVALIDARG; + + StringFromGUID2(rguid, szGuid, _countof(szGuid)); + swprintf(szFullKey, L"%s\\%s\\Category\\Item\\%s", szwSystemTIPKey, szGuid, szGuid); + *pcatid = GUID_NULL; + + error = RegOpenKeyExW(HKEY_LOCAL_MACHINE, szFullKey, 0, KEY_READ, &hKey); + if (error != ERROR_SUCCESS) + return S_FALSE; + + // Enumerate subkeys to find the closest category + while (TRUE) + { + WCHAR szCatidName[39]; + DWORD cchCatidName = _countof(szCatidName); + GUID currentCatid; + + error = RegEnumKeyExW(hKey, dwIndex, szCatidName, &cchCatidName, NULL, NULL, NULL, NULL); + if (error != ERROR_SUCCESS || error == ERROR_NO_MORE_ITEMS) + break; + + dwIndex++; + + if (FAILED(CLSIDFromString(szCatidName, ¤tCatid))) + continue; // Skip invalid GUID strings + + if (ulCount <= 0) + { + *pcatid = currentCatid; + hr = S_OK; // Found a category + break; + } + + // If a list of categories is provided, check if the current one is in the list + BOOL bFound = FALSE; + for (ULONG j = 0; j < ulCount; j++) + { + if (currentCatid == *ppcatidList[j]) + { + bFound = TRUE; + *pcatid = currentCatid; + hr = S_OK; // Found a matching category + break; + } + } + if (bFound) + break; // Found and matched, so stop searching + } + + RegCloseKey(hKey); + return hr; +} + +STDMETHODIMP CCategoryMgr::RegisterGUIDDescription( + _In_ REFCLSID rclsid, + _In_ REFGUID rguid, + _In_ const WCHAR *pchDesc, + _In_ ULONG cch) +{ + FIXME("STUB:(%p)\n", this); + return E_NOTIMPL; +} + +STDMETHODIMP CCategoryMgr::UnregisterGUIDDescription( + _In_ REFCLSID rclsid, + _In_ REFGUID rguid) +{ + FIXME("STUB:(%p)\n", this); + return E_NOTIMPL; +} + +STDMETHODIMP CCategoryMgr::GetGUIDDescription( + _In_ REFGUID rguid, + _Out_ BSTR *pbstrDesc) +{ + FIXME("STUB:(%p)\n", this); + return E_NOTIMPL; +} + +STDMETHODIMP CCategoryMgr::RegisterGUIDDWORD( + _In_ REFCLSID rclsid, + _In_ REFGUID rguid, + _In_ DWORD dw) +{ + FIXME("STUB:(%p)\n", this); + return E_NOTIMPL; +} + +STDMETHODIMP CCategoryMgr::UnregisterGUIDDWORD( + _In_ REFCLSID rclsid, + _In_ REFGUID rguid) +{ + FIXME("STUB:(%p)\n", this); + return E_NOTIMPL; +} + +STDMETHODIMP CCategoryMgr::GetGUIDDWORD( + _In_ REFGUID rguid, + _Out_ DWORD *pdw) +{ + FIXME("STUB:(%p)\n", this); + return E_NOTIMPL; +} + +STDMETHODIMP CCategoryMgr::RegisterGUID( + _In_ REFGUID rguid, + _Out_ TfGuidAtom *pguidatom) +{ + TRACE("%p -> (%s, %p)\n", this, debugstr_guid(&rguid), pguidatom); + + if (!pguidatom) + return E_INVALIDARG; + + DWORD dwCookieId = 0; + DWORD dwEnumIndex = 0; + do + { + dwCookieId = enumerate_Cookie(COOKIE_MAGIC_GUIDATOM, &dwEnumIndex); + if (dwCookieId != 0 && rguid == *(const GUID *)get_Cookie_data(dwCookieId)) + { + *pguidatom = dwCookieId; + return S_OK; + } + } while (dwCookieId != 0); + + GUID *pNewGuid = (GUID *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(GUID)); + if (!pNewGuid) + return E_OUTOFMEMORY; + + *pNewGuid = rguid; + + dwCookieId = generate_Cookie(COOKIE_MAGIC_GUIDATOM, pNewGuid); + if (dwCookieId == 0) + { + HeapFree(GetProcessHeap(), 0, pNewGuid); + return E_FAIL; + } + + *pguidatom = dwCookieId; + return S_OK; +} + +STDMETHODIMP CCategoryMgr::GetGUID( + _In_ TfGuidAtom guidatom, + _Out_ GUID *pguid) +{ + TRACE("%p -> (%d, %p)\n", this, guidatom, pguid); + + if (!pguid) + return E_INVALIDARG; + + *pguid = GUID_NULL; + + if (get_Cookie_magic(guidatom) == COOKIE_MAGIC_GUIDATOM) + *pguid = *(const GUID *)get_Cookie_data(guidatom); + + return S_OK; +} + +STDMETHODIMP CCategoryMgr::IsEqualTfGuidAtom( + _In_ TfGuidAtom guidatom, + _In_ REFGUID rguid, + _Out_ BOOL *pfEqual) +{ + TRACE("%p -> (%d %s %p)\n", this, guidatom, debugstr_guid(&rguid), pfEqual); + + if (!pfEqual) + return E_INVALIDARG; + + *pfEqual = FALSE; + if (get_Cookie_magic(guidatom) == COOKIE_MAGIC_GUIDATOM) + { + if (rguid == *(const GUID *)get_Cookie_data(guidatom)) + *pfEqual = TRUE; + } + + return S_OK; +} + +//////////////////////////////////////////////////////////////////////////// + +EXTERN_C +HRESULT CategoryMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) +{ + if (pUnkOuter) + return CLASS_E_NOAGGREGATION; + + CCategoryMgr *This = new(cicNoThrow) CCategoryMgr(); + if (This == NULL) + return E_OUTOFMEMORY; + + *ppOut = static_cast(This); + TRACE("returning %p\n", *ppOut); + return S_OK; +}