Reactos
1/*
2 * PROJECT: ReactOS CTF
3 * LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later)
4 * PURPOSE: ITfCategoryMgr 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
16class CCategoryMgr
17 : public ITfCategoryMgr
18{
19public:
20 CCategoryMgr();
21 virtual ~CCategoryMgr();
22
23 // ** IUnknown methods **
24 STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj) override;
25 STDMETHODIMP_(ULONG) AddRef() override;
26 STDMETHODIMP_(ULONG) Release() override;
27
28 // ** ITfCategoryMgr methods **
29 STDMETHODIMP RegisterCategory(
30 _In_ REFCLSID rclsid,
31 _In_ REFGUID rcatid,
32 _In_ REFGUID rguid) override;
33 STDMETHODIMP UnregisterCategory(
34 _In_ REFCLSID rclsid,
35 _In_ REFGUID rcatid,
36 _In_ REFGUID rguid) override;
37 STDMETHODIMP EnumCategoriesInItem(
38 _In_ REFGUID rguid,
39 _Out_ IEnumGUID **ppEnum) override;
40 STDMETHODIMP EnumItemsInCategory(
41 _In_ REFGUID rcatid,
42 _Out_ IEnumGUID **ppEnum) override;
43 STDMETHODIMP FindClosestCategory(
44 _In_ REFGUID rguid,
45 _Out_ GUID *pcatid,
46 _In_ const GUID **ppcatidList,
47 _In_ ULONG ulCount) override;
48 STDMETHODIMP RegisterGUIDDescription(
49 _In_ REFCLSID rclsid,
50 _In_ REFGUID rguid,
51 _In_ const WCHAR *pchDesc,
52 _In_ ULONG cch) override;
53 STDMETHODIMP UnregisterGUIDDescription(
54 _In_ REFCLSID rclsid,
55 _In_ REFGUID rguid) override;
56 STDMETHODIMP GetGUIDDescription(
57 _In_ REFGUID rguid,
58 _Out_ BSTR *pbstrDesc) override;
59 STDMETHODIMP RegisterGUIDDWORD(
60 _In_ REFCLSID rclsid,
61 _In_ REFGUID rguid,
62 _In_ DWORD dw) override;
63 STDMETHODIMP UnregisterGUIDDWORD(
64 _In_ REFCLSID rclsid,
65 _In_ REFGUID rguid) override;
66 STDMETHODIMP GetGUIDDWORD(
67 _In_ REFGUID rguid,
68 _Out_ DWORD *pdw) override;
69 STDMETHODIMP RegisterGUID(
70 _In_ REFGUID rguid,
71 _Out_ TfGuidAtom *pguidatom) override;
72 STDMETHODIMP GetGUID(
73 _In_ TfGuidAtom guidatom,
74 _Out_ GUID *pguid) override;
75 STDMETHODIMP IsEqualTfGuidAtom(
76 _In_ TfGuidAtom guidatom,
77 _In_ REFGUID rguid,
78 _Out_ BOOL *pfEqual) override;
79
80protected:
81 LONG m_cRefs;
82};
83
84////////////////////////////////////////////////////////////////////////////
85
86CCategoryMgr::CCategoryMgr()
87 : m_cRefs(1)
88{
89}
90
91CCategoryMgr::~CCategoryMgr()
92{
93 TRACE("destroying %p\n", this);
94}
95
96STDMETHODIMP CCategoryMgr::QueryInterface(REFIID riid, void **ppvObj)
97{
98 if (!ppvObj)
99 return E_INVALIDARG;
100
101 *ppvObj = NULL;
102
103 if (riid == IID_IUnknown || riid == IID_ITfCategoryMgr)
104 *ppvObj = this;
105
106 if (*ppvObj)
107 {
108 AddRef();
109 return S_OK;
110 }
111
112 WARN("unsupported interface: %s\n", debugstr_guid(&riid));
113 return E_NOINTERFACE;
114}
115
116STDMETHODIMP_(ULONG) CCategoryMgr::AddRef()
117{
118 return ::InterlockedIncrement(&m_cRefs);
119}
120
121STDMETHODIMP_(ULONG) CCategoryMgr::Release()
122{
123 ULONG ret = ::InterlockedDecrement(&m_cRefs);
124 if (!ret)
125 delete this;
126 return ret;
127}
128
129STDMETHODIMP CCategoryMgr::RegisterCategory(
130 _In_ REFCLSID rclsid,
131 _In_ REFGUID rcatid,
132 _In_ REFGUID rguid)
133{
134 WCHAR szFullKey[110], szClsid[39], szCatid[39], szGuid[39];
135 HKEY hTipKey = NULL, hCatKey = NULL, hItemKey = NULL;
136 LSTATUS error;
137 HRESULT hr = E_FAIL;
138
139 TRACE("%p -> (%s, %s, %s)\n", this, debugstr_guid(&rclsid), debugstr_guid(&rcatid),
140 debugstr_guid(&rguid));
141
142 StringFromGUID2(rclsid, szClsid, _countof(szClsid));
143 StringFromGUID2(rcatid, szCatid, _countof(szCatid));
144 StringFromGUID2(rguid, szGuid, _countof(szGuid));
145
146 StringCchPrintfW(szFullKey, _countof(szFullKey), L"%s\\%s", szwSystemTIPKey, szClsid);
147 error = RegOpenKeyExW(HKEY_LOCAL_MACHINE, szFullKey, 0, KEY_READ | KEY_WRITE, &hTipKey);
148 if (error != ERROR_SUCCESS)
149 return E_FAIL;
150
151 StringCchPrintfW(szFullKey, _countof(szFullKey), L"Category\\Category\\%s\\%s", szCatid, szGuid);
152 error = RegCreateKeyExW(hTipKey, szFullKey, 0, NULL, 0, KEY_READ | KEY_WRITE, NULL,
153 &hCatKey, NULL);
154 if (error == ERROR_SUCCESS)
155 {
156 RegCloseKey(hCatKey);
157
158 StringCchPrintfW(szFullKey, _countof(szFullKey), L"Category\\Item\\%s\\%s", szGuid, szCatid);
159 error = RegCreateKeyExW(hTipKey, szFullKey, 0, NULL, 0, KEY_READ | KEY_WRITE, NULL,
160 &hItemKey, NULL);
161 if (error == ERROR_SUCCESS)
162 {
163 RegCloseKey(hItemKey);
164 hr = S_OK;
165 }
166 }
167
168 RegCloseKey(hTipKey);
169 return hr;
170}
171
172STDMETHODIMP CCategoryMgr::UnregisterCategory(
173 _In_ REFCLSID rclsid,
174 _In_ REFGUID rcatid,
175 _In_ REFGUID rguid)
176{
177 WCHAR szFullKey[110], szClsid[39], szCatid[39], szGuid[39];
178 HKEY hTipKey = NULL;
179 LSTATUS error;
180
181 TRACE("%p -> (%s %s %s)\n", this, debugstr_guid(&rclsid), debugstr_guid(&rcatid),
182 debugstr_guid(&rguid));
183
184 StringFromGUID2(rclsid, szClsid, _countof(szClsid));
185 StringFromGUID2(rcatid, szCatid, _countof(szCatid));
186 StringFromGUID2(rguid, szGuid, _countof(szGuid));
187
188 StringCchPrintfW(szFullKey, _countof(szFullKey), L"%s\\%s", szwSystemTIPKey, szClsid);
189 error = RegOpenKeyExW(HKEY_LOCAL_MACHINE, szFullKey, 0, KEY_READ | KEY_WRITE, &hTipKey);
190 if (error != ERROR_SUCCESS)
191 return E_FAIL;
192
193 StringCchPrintfW(szFullKey, _countof(szFullKey), L"Category\\Category\\%s\\%s", szCatid, szGuid);
194 RegDeleteTreeW(hTipKey, szFullKey);
195
196 StringCchPrintfW(szFullKey, _countof(szFullKey), L"Category\\Item\\%s\\%s", szGuid, szCatid);
197 RegDeleteTreeW(hTipKey, szFullKey);
198
199 RegCloseKey(hTipKey);
200 return S_OK;
201}
202
203STDMETHODIMP CCategoryMgr::EnumCategoriesInItem(
204 _In_ REFGUID rguid,
205 _Out_ IEnumGUID **ppEnum)
206{
207 FIXME("STUB:(%p)\n", this);
208 return E_NOTIMPL;
209}
210
211STDMETHODIMP CCategoryMgr::EnumItemsInCategory(
212 _In_ REFGUID rcatid,
213 _Out_ IEnumGUID **ppEnum)
214{
215 FIXME("STUB:(%p)\n", this);
216 return E_NOTIMPL;
217}
218
219STDMETHODIMP CCategoryMgr::FindClosestCategory(
220 _In_ REFGUID rguid,
221 _Out_ GUID *pcatid,
222 _In_ const GUID **ppcatidList,
223 _In_ ULONG ulCount)
224{
225 WCHAR szFullKey[120], szGuid[39];
226 HKEY hKey = NULL;
227 HRESULT hr = S_FALSE;
228 DWORD dwIndex = 0;
229 LSTATUS error;
230
231 TRACE("(%p)\n", this);
232
233 if (!pcatid || (ulCount && !ppcatidList))
234 return E_INVALIDARG;
235
236 StringFromGUID2(rguid, szGuid, _countof(szGuid));
237 StringCchPrintfW(szFullKey, _countof(szFullKey), L"%s\\%s\\Category\\Item\\%s", szwSystemTIPKey, szGuid, szGuid);
238 *pcatid = GUID_NULL;
239
240 error = RegOpenKeyExW(HKEY_LOCAL_MACHINE, szFullKey, 0, KEY_READ, &hKey);
241 if (error != ERROR_SUCCESS)
242 return S_FALSE;
243
244 // Enumerate subkeys to find the closest category
245 while (TRUE)
246 {
247 WCHAR szCatidName[39];
248 DWORD cchCatidName = _countof(szCatidName);
249 GUID currentCatid;
250
251 error = RegEnumKeyExW(hKey, dwIndex, szCatidName, &cchCatidName, NULL, NULL, NULL, NULL);
252 if (error != ERROR_SUCCESS || error == ERROR_NO_MORE_ITEMS)
253 break;
254
255 dwIndex++;
256
257 HRESULT hr2 = CLSIDFromString(szCatidName, ¤tCatid);
258 if (FAILED(hr2))
259 continue; // Skip invalid GUID strings
260
261 if (ulCount <= 0)
262 {
263 *pcatid = currentCatid;
264 hr = S_OK; // Found a category
265 break;
266 }
267
268 // If a list of categories is provided, check if the current one is in the list
269 BOOL bFound = FALSE;
270 for (ULONG j = 0; j < ulCount; j++)
271 {
272 if (currentCatid == *ppcatidList[j])
273 {
274 bFound = TRUE;
275 *pcatid = currentCatid;
276 hr = S_OK; // Found a matching category
277 break;
278 }
279 }
280 if (bFound)
281 break; // Found and matched, so stop searching
282 }
283
284 RegCloseKey(hKey);
285 return hr;
286}
287
288STDMETHODIMP CCategoryMgr::RegisterGUIDDescription(
289 _In_ REFCLSID rclsid,
290 _In_ REFGUID rguid,
291 _In_ const WCHAR *pchDesc,
292 _In_ ULONG cch)
293{
294 FIXME("STUB:(%p)\n", this);
295 return E_NOTIMPL;
296}
297
298STDMETHODIMP CCategoryMgr::UnregisterGUIDDescription(
299 _In_ REFCLSID rclsid,
300 _In_ REFGUID rguid)
301{
302 FIXME("STUB:(%p)\n", this);
303 return E_NOTIMPL;
304}
305
306STDMETHODIMP CCategoryMgr::GetGUIDDescription(
307 _In_ REFGUID rguid,
308 _Out_ BSTR *pbstrDesc)
309{
310 FIXME("STUB:(%p)\n", this);
311 return E_NOTIMPL;
312}
313
314STDMETHODIMP CCategoryMgr::RegisterGUIDDWORD(
315 _In_ REFCLSID rclsid,
316 _In_ REFGUID rguid,
317 _In_ DWORD dw)
318{
319 FIXME("STUB:(%p)\n", this);
320 return E_NOTIMPL;
321}
322
323STDMETHODIMP CCategoryMgr::UnregisterGUIDDWORD(
324 _In_ REFCLSID rclsid,
325 _In_ REFGUID rguid)
326{
327 FIXME("STUB:(%p)\n", this);
328 return E_NOTIMPL;
329}
330
331STDMETHODIMP CCategoryMgr::GetGUIDDWORD(
332 _In_ REFGUID rguid,
333 _Out_ DWORD *pdw)
334{
335 FIXME("STUB:(%p)\n", this);
336 return E_NOTIMPL;
337}
338
339STDMETHODIMP CCategoryMgr::RegisterGUID(
340 _In_ REFGUID rguid,
341 _Out_ TfGuidAtom *pguidatom)
342{
343 TRACE("%p -> (%s, %p)\n", this, debugstr_guid(&rguid), pguidatom);
344
345 if (!pguidatom)
346 return E_INVALIDARG;
347
348 DWORD dwCookieId = 0, dwEnumIndex = 0;
349 do
350 {
351 dwCookieId = enumerate_Cookie(COOKIE_MAGIC_GUIDATOM, &dwEnumIndex);
352 if (dwCookieId && rguid == *(const GUID *)get_Cookie_data(dwCookieId))
353 {
354 *pguidatom = dwCookieId;
355 return S_OK;
356 }
357 } while (dwCookieId != 0);
358
359 GUID *pNewGuid = (GUID *)cicMemAlloc(sizeof(GUID));
360 if (!pNewGuid)
361 return E_OUTOFMEMORY;
362
363 *pNewGuid = rguid;
364
365 dwCookieId = generate_Cookie(COOKIE_MAGIC_GUIDATOM, pNewGuid);
366 if (dwCookieId == 0)
367 {
368 cicMemFree(pNewGuid);
369 return E_FAIL;
370 }
371
372 *pguidatom = dwCookieId;
373 return S_OK;
374}
375
376STDMETHODIMP CCategoryMgr::GetGUID(
377 _In_ TfGuidAtom guidatom,
378 _Out_ GUID *pguid)
379{
380 TRACE("%p -> (%d, %p)\n", this, guidatom, pguid);
381
382 if (!pguid)
383 return E_INVALIDARG;
384
385 *pguid = GUID_NULL;
386
387 if (get_Cookie_magic(guidatom) == COOKIE_MAGIC_GUIDATOM)
388 *pguid = *(const GUID *)get_Cookie_data(guidatom);
389
390 return S_OK;
391}
392
393STDMETHODIMP CCategoryMgr::IsEqualTfGuidAtom(
394 _In_ TfGuidAtom guidatom,
395 _In_ REFGUID rguid,
396 _Out_ BOOL *pfEqual)
397{
398 TRACE("%p -> (%d %s %p)\n", this, guidatom, debugstr_guid(&rguid), pfEqual);
399
400 if (!pfEqual)
401 return E_INVALIDARG;
402
403 *pfEqual = FALSE;
404 if (get_Cookie_magic(guidatom) == COOKIE_MAGIC_GUIDATOM)
405 {
406 if (rguid == *(const GUID *)get_Cookie_data(guidatom))
407 *pfEqual = TRUE;
408 }
409
410 return S_OK;
411}
412
413////////////////////////////////////////////////////////////////////////////
414
415EXTERN_C
416HRESULT CategoryMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut)
417{
418 if (pUnkOuter)
419 return CLASS_E_NOAGGREGATION;
420
421 CCategoryMgr *This = new(cicNoThrow) CCategoryMgr();
422 if (!This)
423 return E_OUTOFMEMORY;
424
425 *ppOut = static_cast<ITfCategoryMgr *>(This);
426 TRACE("returning %p\n", *ppOut);
427 return S_OK;
428}