Stub implementation for GetLastInputInfo.
[wine] / dlls / comcat / information.c
1 /*
2  *      ComCatMgr ICatInformation implementation for comcat.dll
3  *
4  * Copyright (C) 2002 John K. Hohm
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <string.h>
22 #include "comcat_private.h"
23
24 #include "wine/debug.h"
25
26 WINE_DEFAULT_DEBUG_CHANNEL(ole);
27
28 static LPENUMCATEGORYINFO COMCAT_IEnumCATEGORYINFO_Construct(LCID lcid);
29 static HRESULT COMCAT_GetCategoryDesc(HKEY key, LCID lcid, PWCHAR pszDesc,
30                                       ULONG buf_wchars);
31
32 struct class_categories {
33     LPCWSTR impl_strings;
34     LPCWSTR req_strings;
35 };
36
37 static struct class_categories *COMCAT_PrepareClassCategories(
38     ULONG impl_count, CATID *impl_catids, ULONG req_count, CATID *req_catids);
39 static HRESULT COMCAT_IsClassOfCategories(
40     HKEY key, struct class_categories const* class_categories);
41 static LPENUMGUID COMCAT_CLSID_IEnumGUID_Construct(
42     struct class_categories const* class_categories);
43 static LPENUMGUID COMCAT_CATID_IEnumGUID_Construct(
44     REFCLSID rclsid, LPCWSTR impl_req);
45
46 /**********************************************************************
47  * COMCAT_ICatInformation_QueryInterface
48  */
49 static HRESULT WINAPI COMCAT_ICatInformation_QueryInterface(
50     LPCATINFORMATION iface,
51     REFIID riid,
52     LPVOID *ppvObj)
53 {
54     ICOM_THIS_MULTI(ComCatMgrImpl, infVtbl, iface);
55     TRACE("\n\tIID:\t%s\n",debugstr_guid(riid));
56
57     if (This == NULL || ppvObj == NULL) return E_POINTER;
58
59     return IUnknown_QueryInterface((LPUNKNOWN)&This->unkVtbl, riid, ppvObj);
60 }
61
62 /**********************************************************************
63  * COMCAT_ICatInformation_AddRef
64  */
65 static ULONG WINAPI COMCAT_ICatInformation_AddRef(LPCATINFORMATION iface)
66 {
67     ICOM_THIS_MULTI(ComCatMgrImpl, infVtbl, iface);
68     TRACE("\n");
69
70     if (This == NULL) return E_POINTER;
71
72     return IUnknown_AddRef((LPUNKNOWN)&This->unkVtbl);
73 }
74
75 /**********************************************************************
76  * COMCAT_ICatInformation_Release
77  */
78 static ULONG WINAPI COMCAT_ICatInformation_Release(LPCATINFORMATION iface)
79 {
80     ICOM_THIS_MULTI(ComCatMgrImpl, infVtbl, iface);
81     TRACE("\n");
82
83     if (This == NULL) return E_POINTER;
84
85     return IUnknown_Release((LPUNKNOWN)&This->unkVtbl);
86 }
87
88 /**********************************************************************
89  * COMCAT_ICatInformation_EnumCategories
90  */
91 static HRESULT WINAPI COMCAT_ICatInformation_EnumCategories(
92     LPCATINFORMATION iface,
93     LCID lcid,
94     LPENUMCATEGORYINFO *ppenumCatInfo)
95 {
96 /*     ICOM_THIS_MULTI(ComCatMgrImpl, infVtbl, iface); */
97     TRACE("\n");
98
99     if (iface == NULL || ppenumCatInfo == NULL) return E_POINTER;
100
101     *ppenumCatInfo = COMCAT_IEnumCATEGORYINFO_Construct(lcid);
102     if (*ppenumCatInfo == NULL) return E_OUTOFMEMORY;
103     IEnumCATEGORYINFO_AddRef(*ppenumCatInfo);
104     return S_OK;
105 }
106
107 /**********************************************************************
108  * COMCAT_ICatInformation_GetCategoryDesc
109  */
110 static HRESULT WINAPI COMCAT_ICatInformation_GetCategoryDesc(
111     LPCATINFORMATION iface,
112     REFCATID rcatid,
113     LCID lcid,
114     PWCHAR *ppszDesc)
115 {
116 /*     ICOM_THIS_MULTI(ComCatMgrImpl, infVtbl, iface); */
117     WCHAR keyname[60] = { 'C', 'o', 'm', 'p', 'o', 'n', 'e', 'n',
118                           't', ' ', 'C', 'a', 't', 'e', 'g', 'o',
119                           'r', 'i', 'e', 's', '\\', 0 };
120     HKEY key;
121     HRESULT res;
122
123     TRACE("\n\tCATID:\t%s\n\tLCID:\t%lX\n",debugstr_guid(rcatid), lcid);
124
125     if (rcatid == NULL || ppszDesc == NULL) return E_INVALIDARG;
126
127     /* Open the key for this category. */
128     if (!StringFromGUID2(rcatid, keyname + 21, 39)) return E_FAIL;
129     res = RegOpenKeyExW(HKEY_CLASSES_ROOT, keyname, 0, KEY_READ, &key);
130     if (res != ERROR_SUCCESS) return CAT_E_CATIDNOEXIST;
131
132     /* Allocate a sensible amount of memory for the description. */
133     *ppszDesc = (PWCHAR) CoTaskMemAlloc(128 * sizeof(WCHAR));
134     if (*ppszDesc == NULL) {
135         RegCloseKey(key);
136         return E_OUTOFMEMORY;
137     }
138
139     /* Get the description, and make sure it's null terminated. */
140     res = COMCAT_GetCategoryDesc(key, lcid, *ppszDesc, 128);
141     RegCloseKey(key);
142     if (FAILED(res)) {
143         CoTaskMemFree(*ppszDesc);
144         return res;
145     }
146
147     return S_OK;
148 }
149
150 /**********************************************************************
151  * COMCAT_ICatInformation_EnumClassesOfCategories
152  */
153 static HRESULT WINAPI COMCAT_ICatInformation_EnumClassesOfCategories(
154     LPCATINFORMATION iface,
155     ULONG cImplemented,
156     CATID *rgcatidImpl,
157     ULONG cRequired,
158     CATID *rgcatidReq,
159     LPENUMCLSID *ppenumCLSID)
160 {
161 /*     ICOM_THIS_MULTI(ComCatMgrImpl, infVtbl, iface); */
162     struct class_categories *categories;
163
164     TRACE("\n");
165
166     if (iface == NULL || ppenumCLSID == NULL ||
167         (cImplemented && rgcatidImpl == NULL) ||
168         (cRequired && rgcatidReq == NULL)) return E_POINTER;
169
170     categories = COMCAT_PrepareClassCategories(cImplemented, rgcatidImpl,
171                                                cRequired, rgcatidReq);
172     if (categories == NULL) return E_OUTOFMEMORY;
173     *ppenumCLSID = COMCAT_CLSID_IEnumGUID_Construct(categories);
174     if (*ppenumCLSID == NULL) {
175         HeapFree(GetProcessHeap(), 0, categories);
176         return E_OUTOFMEMORY;
177     }
178     IEnumGUID_AddRef(*ppenumCLSID);
179     return S_OK;
180 }
181
182 /**********************************************************************
183  * COMCAT_ICatInformation_IsClassOfCategories
184  */
185 static HRESULT WINAPI COMCAT_ICatInformation_IsClassOfCategories(
186     LPCATINFORMATION iface,
187     REFCLSID rclsid,
188     ULONG cImplemented,
189     CATID *rgcatidImpl,
190     ULONG cRequired,
191     CATID *rgcatidReq)
192 {
193 /*     ICOM_THIS_MULTI(ComCatMgrImpl, infVtbl, iface); */
194     WCHAR keyname[45] = { 'C', 'L', 'S', 'I', 'D', '\\', 0 };
195     HRESULT res;
196     struct class_categories *categories;
197     HKEY key;
198
199     if (WINE_TRACE_ON(ole)) {
200         ULONG count;
201         TRACE("\n\tCLSID:\t%s\n\tImplemented %lu\n",debugstr_guid(rclsid),cImplemented);
202         for (count = 0; count < cImplemented; ++count)
203             TRACE("\t\t%s\n",debugstr_guid(&rgcatidImpl[count]));
204         TRACE("\tRequired %lu\n",cRequired);
205         for (count = 0; count < cRequired; ++count)
206             TRACE("\t\t%s\n",debugstr_guid(&rgcatidReq[count]));
207     }
208
209     if ((cImplemented && rgcatidImpl == NULL) ||
210         (cRequired && rgcatidReq == NULL)) return E_POINTER;
211
212     res = StringFromGUID2(rclsid, keyname + 6, 39);
213     if (FAILED(res)) return res;
214
215     categories = COMCAT_PrepareClassCategories(cImplemented, rgcatidImpl,
216                                                cRequired, rgcatidReq);
217     if (categories == NULL) return E_OUTOFMEMORY;
218
219     res = RegOpenKeyExW(HKEY_CLASSES_ROOT, keyname, 0, KEY_READ, &key);
220     if (res == ERROR_SUCCESS) {
221         res = COMCAT_IsClassOfCategories(key, categories);
222         RegCloseKey(key);
223     } else res = S_FALSE;
224
225     HeapFree(GetProcessHeap(), 0, categories);
226
227     return res;
228 }
229
230 /**********************************************************************
231  * COMCAT_ICatInformation_EnumImplCategoriesOfClass
232  */
233 static HRESULT WINAPI COMCAT_ICatInformation_EnumImplCategoriesOfClass(
234     LPCATINFORMATION iface,
235     REFCLSID rclsid,
236     LPENUMCATID *ppenumCATID)
237 {
238 /*     ICOM_THIS_MULTI(ComCatMgrImpl, infVtbl, iface); */
239     WCHAR postfix[24] = { '\\', 'I', 'm', 'p', 'l', 'e', 'm', 'e',
240                           'n', 't', 'e', 'd', ' ', 'C', 'a', 't',
241                           'e', 'g', 'o', 'r', 'i', 'e', 's', 0 };
242
243     TRACE("\n\tCLSID:\t%s\n",debugstr_guid(rclsid));
244
245     if (iface == NULL || rclsid == NULL || ppenumCATID == NULL)
246         return E_POINTER;
247
248     *ppenumCATID = COMCAT_CATID_IEnumGUID_Construct(rclsid, postfix);
249     if (*ppenumCATID == NULL) return E_OUTOFMEMORY;
250     return S_OK;
251 }
252
253 /**********************************************************************
254  * COMCAT_ICatInformation_EnumReqCategoriesOfClass
255  */
256 static HRESULT WINAPI COMCAT_ICatInformation_EnumReqCategoriesOfClass(
257     LPCATINFORMATION iface,
258     REFCLSID rclsid,
259     LPENUMCATID *ppenumCATID)
260 {
261 /*     ICOM_THIS_MULTI(ComCatMgrImpl, infVtbl, iface); */
262     WCHAR postfix[21] = { '\\', 'R', 'e', 'q', 'u', 'i', 'r', 'e',
263                           'd', ' ', 'C', 'a', 't', 'e', 'g', 'o',
264                           'r', 'i', 'e', 's', 0 };
265
266     TRACE("\n\tCLSID:\t%s\n",debugstr_guid(rclsid));
267
268     if (iface == NULL || rclsid == NULL || ppenumCATID == NULL)
269         return E_POINTER;
270
271     *ppenumCATID = COMCAT_CATID_IEnumGUID_Construct(rclsid, postfix);
272     if (*ppenumCATID == NULL) return E_OUTOFMEMORY;
273     return S_OK;
274 }
275
276 /**********************************************************************
277  * COMCAT_ICatInformation_Vtbl
278  */
279 ICatInformationVtbl COMCAT_ICatInformation_Vtbl =
280 {
281     COMCAT_ICatInformation_QueryInterface,
282     COMCAT_ICatInformation_AddRef,
283     COMCAT_ICatInformation_Release,
284     COMCAT_ICatInformation_EnumCategories,
285     COMCAT_ICatInformation_GetCategoryDesc,
286     COMCAT_ICatInformation_EnumClassesOfCategories,
287     COMCAT_ICatInformation_IsClassOfCategories,
288     COMCAT_ICatInformation_EnumImplCategoriesOfClass,
289     COMCAT_ICatInformation_EnumReqCategoriesOfClass
290 };
291
292 /**********************************************************************
293  * IEnumCATEGORYINFO implementation
294  *
295  * This implementation is not thread-safe.  The manager itself is, but
296  * I can't imagine a valid use of an enumerator in several threads.
297  */
298 typedef struct
299 {
300     IEnumCATEGORYINFOVtbl *lpVtbl;
301     DWORD ref;
302     LCID  lcid;
303     HKEY  key;
304     DWORD next_index;
305 } IEnumCATEGORYINFOImpl;
306
307 static ULONG WINAPI COMCAT_IEnumCATEGORYINFO_AddRef(LPENUMCATEGORYINFO iface)
308 {
309     IEnumCATEGORYINFOImpl *This = (IEnumCATEGORYINFOImpl *)iface;
310     TRACE("\n");
311
312     if (This == NULL) return E_POINTER;
313
314     return ++(This->ref);
315 }
316
317 static HRESULT WINAPI COMCAT_IEnumCATEGORYINFO_QueryInterface(
318     LPENUMCATEGORYINFO iface,
319     REFIID riid,
320     LPVOID *ppvObj)
321 {
322     IEnumCATEGORYINFOImpl *This = (IEnumCATEGORYINFOImpl *)iface;
323     TRACE("\n\tIID:\t%s\n",debugstr_guid(riid));
324
325     if (This == NULL || ppvObj == NULL) return E_POINTER;
326
327     if (IsEqualGUID(riid, &IID_IUnknown) ||
328         IsEqualGUID(riid, &IID_IEnumCATEGORYINFO))
329     {
330         *ppvObj = (LPVOID)iface;
331         COMCAT_IEnumCATEGORYINFO_AddRef(iface);
332         return S_OK;
333     }
334
335     return E_NOINTERFACE;
336 }
337
338 static ULONG WINAPI COMCAT_IEnumCATEGORYINFO_Release(LPENUMCATEGORYINFO iface)
339 {
340     IEnumCATEGORYINFOImpl *This = (IEnumCATEGORYINFOImpl *)iface;
341     TRACE("\n");
342
343     if (This == NULL) return E_POINTER;
344
345     if (--(This->ref) == 0) {
346         if (This->key) RegCloseKey(This->key);
347         HeapFree(GetProcessHeap(), 0, This);
348         return 0;
349     }
350     return This->ref;
351 }
352
353 static HRESULT WINAPI COMCAT_IEnumCATEGORYINFO_Next(
354     LPENUMCATEGORYINFO iface,
355     ULONG celt,
356     CATEGORYINFO *rgelt,
357     ULONG *pceltFetched)
358 {
359     IEnumCATEGORYINFOImpl *This = (IEnumCATEGORYINFOImpl *)iface;
360     ULONG fetched = 0;
361
362     TRACE("\n");
363
364     if (This == NULL || rgelt == NULL) return E_POINTER;
365
366     if (This->key) while (fetched < celt) {
367         HRESULT res;
368         WCHAR catid[39];
369         DWORD cName = 39;
370         HKEY subkey;
371
372         res = RegEnumKeyExW(This->key, This->next_index, catid, &cName,
373                             NULL, NULL, NULL, NULL);
374         if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA) break;
375         ++(This->next_index);
376
377         res = CLSIDFromString(catid, &rgelt->catid);
378         if (FAILED(res)) continue;
379
380         res = RegOpenKeyExW(This->key, catid, 0, KEY_READ, &subkey);
381         if (res != ERROR_SUCCESS) continue;
382
383         res = COMCAT_GetCategoryDesc(subkey, This->lcid,
384                                      rgelt->szDescription, 128);
385         RegCloseKey(subkey);
386         if (FAILED(res)) continue;
387
388         rgelt->lcid = This->lcid;
389         ++fetched;
390         ++rgelt;
391     }
392
393     if (pceltFetched) *pceltFetched = fetched;
394     return fetched == celt ? S_OK : S_FALSE;
395 }
396
397 static HRESULT WINAPI COMCAT_IEnumCATEGORYINFO_Skip(
398     LPENUMCATEGORYINFO iface,
399     ULONG celt)
400 {
401     IEnumCATEGORYINFOImpl *This = (IEnumCATEGORYINFOImpl *)iface;
402
403     TRACE("\n");
404
405     if (This == NULL) return E_POINTER;
406     This->next_index += celt;
407     /* This should return S_FALSE when there aren't celt elems to skip. */
408     return S_OK;
409 }
410
411 static HRESULT WINAPI COMCAT_IEnumCATEGORYINFO_Reset(LPENUMCATEGORYINFO iface)
412 {
413     IEnumCATEGORYINFOImpl *This = (IEnumCATEGORYINFOImpl *)iface;
414
415     TRACE("\n");
416
417     if (This == NULL) return E_POINTER;
418     This->next_index = 0;
419     return S_OK;
420 }
421
422 static HRESULT WINAPI COMCAT_IEnumCATEGORYINFO_Clone(
423     LPENUMCATEGORYINFO iface,
424     IEnumCATEGORYINFO **ppenum)
425 {
426     IEnumCATEGORYINFOImpl *This = (IEnumCATEGORYINFOImpl *)iface;
427     WCHAR keyname[21] = { 'C', 'o', 'm', 'p', 'o', 'n', 'e', 'n',
428                           't', ' ', 'C', 'a', 't', 'e', 'g', 'o',
429                           'r', 'i', 'e', 's', 0 };
430     IEnumCATEGORYINFOImpl *new_this;
431
432     TRACE("\n");
433
434     if (This == NULL || ppenum == NULL) return E_POINTER;
435
436     new_this = (IEnumCATEGORYINFOImpl *) HeapAlloc(
437         GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IEnumCATEGORYINFOImpl));
438     if (new_this == NULL) return E_OUTOFMEMORY;
439
440     new_this->lpVtbl = This->lpVtbl;
441     new_this->ref = 1;
442     new_this->lcid = This->lcid;
443     /* FIXME: could we more efficiently use DuplicateHandle? */
444     RegOpenKeyExW(HKEY_CLASSES_ROOT, keyname, 0, KEY_READ, &new_this->key);
445     new_this->next_index = This->next_index;
446
447     *ppenum = (LPENUMCATEGORYINFO)new_this;
448     return S_OK;
449 }
450
451 IEnumCATEGORYINFOVtbl COMCAT_IEnumCATEGORYINFO_Vtbl =
452 {
453     COMCAT_IEnumCATEGORYINFO_QueryInterface,
454     COMCAT_IEnumCATEGORYINFO_AddRef,
455     COMCAT_IEnumCATEGORYINFO_Release,
456     COMCAT_IEnumCATEGORYINFO_Next,
457     COMCAT_IEnumCATEGORYINFO_Skip,
458     COMCAT_IEnumCATEGORYINFO_Reset,
459     COMCAT_IEnumCATEGORYINFO_Clone
460 };
461
462 static LPENUMCATEGORYINFO COMCAT_IEnumCATEGORYINFO_Construct(LCID lcid)
463 {
464     IEnumCATEGORYINFOImpl *This;
465
466     This = (IEnumCATEGORYINFOImpl *) HeapAlloc(
467         GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IEnumCATEGORYINFOImpl));
468     if (This) {
469         WCHAR keyname[21] = { 'C', 'o', 'm', 'p', 'o', 'n', 'e', 'n',
470                               't', ' ', 'C', 'a', 't', 'e', 'g', 'o',
471                               'r', 'i', 'e', 's', 0 };
472
473         This->lpVtbl = &COMCAT_IEnumCATEGORYINFO_Vtbl;
474         This->lcid = lcid;
475         RegOpenKeyExW(HKEY_CLASSES_ROOT, keyname, 0, KEY_READ, &This->key);
476     }
477     return (LPENUMCATEGORYINFO)This;
478 }
479
480 /**********************************************************************
481  * COMCAT_GetCategoryDesc
482  */
483 static HRESULT COMCAT_GetCategoryDesc(HKEY key, LCID lcid, PWCHAR pszDesc,
484                                       ULONG buf_wchars)
485 {
486     WCHAR fmt[4] = { '%', 'l', 'X', 0 };
487     WCHAR valname[5];
488     HRESULT res;
489     DWORD type, size = (buf_wchars - 1) * sizeof(WCHAR);
490
491     if (pszDesc == NULL) return E_INVALIDARG;
492
493     /* FIXME: lcid comparisons are more complex than this! */
494     wsprintfW(valname, fmt, lcid);
495     res = RegQueryValueExW(key, valname, 0, &type, (LPBYTE)pszDesc, &size);
496     if (res != ERROR_SUCCESS || type != REG_SZ) {
497         FIXME("Simplified lcid comparison\n");
498         return CAT_E_NODESCRIPTION;
499     }
500     pszDesc[size / sizeof(WCHAR)] = (WCHAR)0;
501
502     return S_OK;
503 }
504
505 /**********************************************************************
506  * COMCAT_PrepareClassCategories
507  */
508 static struct class_categories *COMCAT_PrepareClassCategories(
509     ULONG impl_count, CATID *impl_catids, ULONG req_count, CATID *req_catids)
510 {
511     struct class_categories *categories;
512     WCHAR *strings;
513
514     categories = (struct class_categories *)HeapAlloc(
515         GetProcessHeap(), HEAP_ZERO_MEMORY,
516         sizeof(struct class_categories) +
517         ((impl_count + req_count) * 39 + 2) * sizeof(WCHAR));
518     if (categories == NULL) return categories;
519
520     strings = (WCHAR *)(categories + 1);
521     categories->impl_strings = strings;
522     while (impl_count--) {
523         StringFromGUID2(impl_catids++, strings, 39);
524         strings += 39;
525     }
526     *strings++ = 0;
527
528     categories->req_strings = strings;
529     while (req_count--) {
530         StringFromGUID2(req_catids++, strings, 39);
531         strings += 39;
532     }
533     *strings++ = 0;
534
535     return categories;
536 }
537
538 /**********************************************************************
539  * COMCAT_IsClassOfCategories
540  */
541 static HRESULT COMCAT_IsClassOfCategories(
542     HKEY key,
543     struct class_categories const* categories)
544 {
545     WCHAR impl_keyname[23] = { 'I', 'm', 'p', 'l', 'e', 'm', 'e', 'n',
546                                't', 'e', 'd', ' ', 'C', 'a', 't', 'e',
547                                'g', 'o', 'r', 'i', 'e', 's', 0 };
548     WCHAR req_keyname[20] = { 'R', 'e', 'q', 'u', 'i', 'r', 'e', 'd',
549                               ' ', 'C', 'a', 't', 'e', 'g', 'o', 'r',
550                               'i', 'e', 's', 0 };
551     HKEY subkey;
552     HRESULT res;
553     DWORD index;
554     LPCWSTR string;
555
556     /* Check that every given category is implemented by class. */
557     res = RegOpenKeyExW(key, impl_keyname, 0, KEY_READ, &subkey);
558     if (res != ERROR_SUCCESS) return S_FALSE;
559     for (string = categories->impl_strings; *string; string += 39) {
560         HKEY catkey;
561         res = RegOpenKeyExW(subkey, string, 0, 0, &catkey);
562         if (res != ERROR_SUCCESS) {
563             RegCloseKey(subkey);
564             return S_FALSE;
565         }
566         RegCloseKey(catkey);
567     }
568     RegCloseKey(subkey);
569
570     /* Check that all categories required by class are given. */
571     res = RegOpenKeyExW(key, req_keyname, 0, KEY_READ, &subkey);
572     if (res == ERROR_SUCCESS) {
573         for (index = 0; ; ++index) {
574             WCHAR keyname[39];
575             DWORD size = 39;
576
577             res = RegEnumKeyExW(subkey, index, keyname, &size,
578                                 NULL, NULL, NULL, NULL);
579             if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA) break;
580             if (size != 38) continue; /* bogus catid in registry */
581             for (string = categories->req_strings; *string; string += 39)
582                 if (!strcmpiW(string, keyname)) break;
583             if (!*string) {
584                 RegCloseKey(subkey);
585                 return S_FALSE;
586             }
587         }
588         RegCloseKey(subkey);
589     }
590
591     return S_OK;
592 }
593
594 /**********************************************************************
595  * ClassesOfCategories IEnumCLSID (IEnumGUID) implementation
596  *
597  * This implementation is not thread-safe.  The manager itself is, but
598  * I can't imagine a valid use of an enumerator in several threads.
599  */
600 typedef struct
601 {
602     IEnumGUIDVtbl *lpVtbl;
603     DWORD ref;
604     struct class_categories const *categories;
605     HKEY  key;
606     DWORD next_index;
607 } CLSID_IEnumGUIDImpl;
608
609 static ULONG WINAPI COMCAT_CLSID_IEnumGUID_AddRef(LPENUMGUID iface)
610 {
611     CLSID_IEnumGUIDImpl *This = (CLSID_IEnumGUIDImpl *)iface;
612     TRACE("\n");
613
614     if (This == NULL) return E_POINTER;
615
616     return ++(This->ref);
617 }
618
619 static HRESULT WINAPI COMCAT_CLSID_IEnumGUID_QueryInterface(
620     LPENUMGUID iface,
621     REFIID riid,
622     LPVOID *ppvObj)
623 {
624     CLSID_IEnumGUIDImpl *This = (CLSID_IEnumGUIDImpl *)iface;
625     TRACE("\n\tIID:\t%s\n",debugstr_guid(riid));
626
627     if (This == NULL || ppvObj == NULL) return E_POINTER;
628
629     if (IsEqualGUID(riid, &IID_IUnknown) ||
630         IsEqualGUID(riid, &IID_IEnumGUID))
631     {
632         *ppvObj = (LPVOID)iface;
633         COMCAT_CLSID_IEnumGUID_AddRef(iface);
634         return S_OK;
635     }
636
637     return E_NOINTERFACE;
638 }
639
640 static ULONG WINAPI COMCAT_CLSID_IEnumGUID_Release(LPENUMGUID iface)
641 {
642     CLSID_IEnumGUIDImpl *This = (CLSID_IEnumGUIDImpl *)iface;
643     TRACE("\n");
644
645     if (This == NULL) return E_POINTER;
646
647     if (--(This->ref) == 0) {
648         if (This->key) RegCloseKey(This->key);
649         HeapFree(GetProcessHeap(), 0, (LPVOID)This->categories);
650         HeapFree(GetProcessHeap(), 0, This);
651         return 0;
652     }
653     return This->ref;
654 }
655
656 static HRESULT WINAPI COMCAT_CLSID_IEnumGUID_Next(
657     LPENUMGUID iface,
658     ULONG celt,
659     GUID *rgelt,
660     ULONG *pceltFetched)
661 {
662     CLSID_IEnumGUIDImpl *This = (CLSID_IEnumGUIDImpl *)iface;
663     ULONG fetched = 0;
664
665     TRACE("\n");
666
667     if (This == NULL || rgelt == NULL) return E_POINTER;
668
669     if (This->key) while (fetched < celt) {
670         HRESULT res;
671         WCHAR clsid[39];
672         DWORD cName = 39;
673         HKEY subkey;
674
675         res = RegEnumKeyExW(This->key, This->next_index, clsid, &cName,
676                             NULL, NULL, NULL, NULL);
677         if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA) break;
678         ++(This->next_index);
679
680         res = CLSIDFromString(clsid, rgelt);
681         if (FAILED(res)) continue;
682
683         res = RegOpenKeyExW(This->key, clsid, 0, KEY_READ, &subkey);
684         if (res != ERROR_SUCCESS) continue;
685
686         res = COMCAT_IsClassOfCategories(subkey, This->categories);
687         RegCloseKey(subkey);
688         if (res != S_OK) continue;
689
690         ++fetched;
691         ++rgelt;
692     }
693
694     if (pceltFetched) *pceltFetched = fetched;
695     return fetched == celt ? S_OK : S_FALSE;
696 }
697
698 static HRESULT WINAPI COMCAT_CLSID_IEnumGUID_Skip(
699     LPENUMGUID iface,
700     ULONG celt)
701 {
702     CLSID_IEnumGUIDImpl *This = (CLSID_IEnumGUIDImpl *)iface;
703
704     TRACE("\n");
705
706     if (This == NULL) return E_POINTER;
707     This->next_index += celt;
708     FIXME("Never returns S_FALSE\n");
709     return S_OK;
710 }
711
712 static HRESULT WINAPI COMCAT_CLSID_IEnumGUID_Reset(LPENUMGUID iface)
713 {
714     CLSID_IEnumGUIDImpl *This = (CLSID_IEnumGUIDImpl *)iface;
715
716     TRACE("\n");
717
718     if (This == NULL) return E_POINTER;
719     This->next_index = 0;
720     return S_OK;
721 }
722
723 static HRESULT WINAPI COMCAT_CLSID_IEnumGUID_Clone(
724     LPENUMGUID iface,
725     IEnumGUID **ppenum)
726 {
727     CLSID_IEnumGUIDImpl *This = (CLSID_IEnumGUIDImpl *)iface;
728     WCHAR keyname[6] = { 'C', 'L', 'S', 'I', 'D', 0 };
729     CLSID_IEnumGUIDImpl *new_this;
730     DWORD size;
731
732     TRACE("\n");
733
734     if (This == NULL || ppenum == NULL) return E_POINTER;
735
736     new_this = (CLSID_IEnumGUIDImpl *) HeapAlloc(
737         GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CLSID_IEnumGUIDImpl));
738     if (new_this == NULL) return E_OUTOFMEMORY;
739
740     new_this->lpVtbl = This->lpVtbl;
741     new_this->ref = 1;
742     size = HeapSize(GetProcessHeap(), 0, (LPVOID)This->categories);
743     new_this->categories = (struct class_categories *)
744         HeapAlloc(GetProcessHeap(), 0, size);
745     if (new_this->categories == NULL) {
746         HeapFree(GetProcessHeap(), 0, new_this);
747         return E_OUTOFMEMORY;
748     }
749     memcpy((LPVOID)new_this->categories, This->categories, size);
750     /* FIXME: could we more efficiently use DuplicateHandle? */
751     RegOpenKeyExW(HKEY_CLASSES_ROOT, keyname, 0, KEY_READ, &new_this->key);
752     new_this->next_index = This->next_index;
753
754     *ppenum = (LPENUMGUID)new_this;
755     return S_OK;
756 }
757
758 IEnumGUIDVtbl COMCAT_CLSID_IEnumGUID_Vtbl =
759 {
760     COMCAT_CLSID_IEnumGUID_QueryInterface,
761     COMCAT_CLSID_IEnumGUID_AddRef,
762     COMCAT_CLSID_IEnumGUID_Release,
763     COMCAT_CLSID_IEnumGUID_Next,
764     COMCAT_CLSID_IEnumGUID_Skip,
765     COMCAT_CLSID_IEnumGUID_Reset,
766     COMCAT_CLSID_IEnumGUID_Clone
767 };
768
769 static LPENUMGUID COMCAT_CLSID_IEnumGUID_Construct(
770     struct class_categories const* categories)
771 {
772     CLSID_IEnumGUIDImpl *This;
773
774     This = (CLSID_IEnumGUIDImpl *) HeapAlloc(
775         GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CLSID_IEnumGUIDImpl));
776     if (This) {
777         WCHAR keyname[6] = { 'C', 'L', 'S', 'I', 'D', 0 };
778
779         This->lpVtbl = &COMCAT_CLSID_IEnumGUID_Vtbl;
780         This->categories = categories;
781         RegOpenKeyExW(HKEY_CLASSES_ROOT, keyname, 0, KEY_READ, &This->key);
782     }
783     return (LPENUMGUID)This;
784 }
785
786 /**********************************************************************
787  * CategoriesOfClass IEnumCATID (IEnumGUID) implementation
788  *
789  * This implementation is not thread-safe.  The manager itself is, but
790  * I can't imagine a valid use of an enumerator in several threads.
791  */
792 typedef struct
793 {
794     IEnumGUIDVtbl *lpVtbl;
795     DWORD ref;
796     WCHAR keyname[68];
797     HKEY  key;
798     DWORD next_index;
799 } CATID_IEnumGUIDImpl;
800
801 static ULONG WINAPI COMCAT_CATID_IEnumGUID_AddRef(LPENUMGUID iface)
802 {
803     CATID_IEnumGUIDImpl *This = (CATID_IEnumGUIDImpl *)iface;
804     TRACE("\n");
805
806     if (This == NULL) return E_POINTER;
807
808     return ++(This->ref);
809 }
810
811 static HRESULT WINAPI COMCAT_CATID_IEnumGUID_QueryInterface(
812     LPENUMGUID iface,
813     REFIID riid,
814     LPVOID *ppvObj)
815 {
816     CATID_IEnumGUIDImpl *This = (CATID_IEnumGUIDImpl *)iface;
817     TRACE("\n\tIID:\t%s\n",debugstr_guid(riid));
818
819     if (This == NULL || ppvObj == NULL) return E_POINTER;
820
821     if (IsEqualGUID(riid, &IID_IUnknown) ||
822         IsEqualGUID(riid, &IID_IEnumGUID))
823     {
824         *ppvObj = (LPVOID)iface;
825         COMCAT_CATID_IEnumGUID_AddRef(iface);
826         return S_OK;
827     }
828
829     return E_NOINTERFACE;
830 }
831
832 static ULONG WINAPI COMCAT_CATID_IEnumGUID_Release(LPENUMGUID iface)
833 {
834     CATID_IEnumGUIDImpl *This = (CATID_IEnumGUIDImpl *)iface;
835     TRACE("\n");
836
837     if (This == NULL) return E_POINTER;
838
839     if (--(This->ref) == 0) {
840         if (This->key) RegCloseKey(This->key);
841         HeapFree(GetProcessHeap(), 0, This);
842         return 0;
843     }
844     return This->ref;
845 }
846
847 static HRESULT WINAPI COMCAT_CATID_IEnumGUID_Next(
848     LPENUMGUID iface,
849     ULONG celt,
850     GUID *rgelt,
851     ULONG *pceltFetched)
852 {
853     CATID_IEnumGUIDImpl *This = (CATID_IEnumGUIDImpl *)iface;
854     ULONG fetched = 0;
855
856     TRACE("\n");
857
858     if (This == NULL || rgelt == NULL) return E_POINTER;
859
860     if (This->key) while (fetched < celt) {
861         HRESULT res;
862         WCHAR catid[39];
863         DWORD cName = 39;
864
865         res = RegEnumKeyExW(This->key, This->next_index, catid, &cName,
866                             NULL, NULL, NULL, NULL);
867         if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA) break;
868         ++(This->next_index);
869
870         res = CLSIDFromString(catid, rgelt);
871         if (FAILED(res)) continue;
872
873         ++fetched;
874         ++rgelt;
875     }
876
877     if (pceltFetched) *pceltFetched = fetched;
878     return fetched == celt ? S_OK : S_FALSE;
879 }
880
881 static HRESULT WINAPI COMCAT_CATID_IEnumGUID_Skip(
882     LPENUMGUID iface,
883     ULONG celt)
884 {
885     CATID_IEnumGUIDImpl *This = (CATID_IEnumGUIDImpl *)iface;
886
887     TRACE("\n");
888
889     if (This == NULL) return E_POINTER;
890     This->next_index += celt;
891     FIXME("Never returns S_FALSE\n");
892     return S_OK;
893 }
894
895 static HRESULT WINAPI COMCAT_CATID_IEnumGUID_Reset(LPENUMGUID iface)
896 {
897     CATID_IEnumGUIDImpl *This = (CATID_IEnumGUIDImpl *)iface;
898
899     TRACE("\n");
900
901     if (This == NULL) return E_POINTER;
902     This->next_index = 0;
903     return S_OK;
904 }
905
906 static HRESULT WINAPI COMCAT_CATID_IEnumGUID_Clone(
907     LPENUMGUID iface,
908     IEnumGUID **ppenum)
909 {
910     CATID_IEnumGUIDImpl *This = (CATID_IEnumGUIDImpl *)iface;
911     CATID_IEnumGUIDImpl *new_this;
912
913     TRACE("\n");
914
915     if (This == NULL || ppenum == NULL) return E_POINTER;
916
917     new_this = (CATID_IEnumGUIDImpl *) HeapAlloc(
918         GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CATID_IEnumGUIDImpl));
919     if (new_this == NULL) return E_OUTOFMEMORY;
920
921     new_this->lpVtbl = This->lpVtbl;
922     new_this->ref = 1;
923     lstrcpyW(new_this->keyname, This->keyname);
924     /* FIXME: could we more efficiently use DuplicateHandle? */
925     RegOpenKeyExW(HKEY_CLASSES_ROOT, new_this->keyname, 0, KEY_READ, &new_this->key);
926     new_this->next_index = This->next_index;
927
928     *ppenum = (LPENUMGUID)new_this;
929     return S_OK;
930 }
931
932 IEnumGUIDVtbl COMCAT_CATID_IEnumGUID_Vtbl =
933 {
934     COMCAT_CATID_IEnumGUID_QueryInterface,
935     COMCAT_CATID_IEnumGUID_AddRef,
936     COMCAT_CATID_IEnumGUID_Release,
937     COMCAT_CATID_IEnumGUID_Next,
938     COMCAT_CATID_IEnumGUID_Skip,
939     COMCAT_CATID_IEnumGUID_Reset,
940     COMCAT_CATID_IEnumGUID_Clone
941 };
942
943 static LPENUMGUID COMCAT_CATID_IEnumGUID_Construct(
944     REFCLSID rclsid, LPCWSTR postfix)
945 {
946     CATID_IEnumGUIDImpl *This;
947
948     This = (CATID_IEnumGUIDImpl *) HeapAlloc(
949         GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CATID_IEnumGUIDImpl));
950     if (This) {
951         WCHAR prefix[6] = { 'C', 'L', 'S', 'I', 'D', '\\' };
952
953         This->lpVtbl = &COMCAT_CATID_IEnumGUID_Vtbl;
954         memcpy(This->keyname, prefix, sizeof(prefix));
955         StringFromGUID2(rclsid, This->keyname + 6, 39);
956         lstrcpyW(This->keyname + 44, postfix);
957         RegOpenKeyExW(HKEY_CLASSES_ROOT, This->keyname, 0, KEY_READ, &This->key);
958     }
959     return (LPENUMGUID)This;
960 }