Removed uses of the non standard ICOM_VTBL macro.
[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 ICOM_VTABLE(ICatInformation) COMCAT_ICatInformation_Vtbl =
280 {
281     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
282     COMCAT_ICatInformation_QueryInterface,
283     COMCAT_ICatInformation_AddRef,
284     COMCAT_ICatInformation_Release,
285     COMCAT_ICatInformation_EnumCategories,
286     COMCAT_ICatInformation_GetCategoryDesc,
287     COMCAT_ICatInformation_EnumClassesOfCategories,
288     COMCAT_ICatInformation_IsClassOfCategories,
289     COMCAT_ICatInformation_EnumImplCategoriesOfClass,
290     COMCAT_ICatInformation_EnumReqCategoriesOfClass
291 };
292
293 /**********************************************************************
294  * IEnumCATEGORYINFO implementation
295  *
296  * This implementation is not thread-safe.  The manager itself is, but
297  * I can't imagine a valid use of an enumerator in several threads.
298  */
299 typedef struct
300 {
301     ICOM_VFIELD(IEnumCATEGORYINFO);
302     DWORD ref;
303     LCID  lcid;
304     HKEY  key;
305     DWORD next_index;
306 } IEnumCATEGORYINFOImpl;
307
308 static ULONG WINAPI COMCAT_IEnumCATEGORYINFO_AddRef(LPENUMCATEGORYINFO iface)
309 {
310     ICOM_THIS(IEnumCATEGORYINFOImpl, iface);
311     TRACE("\n");
312
313     if (This == NULL) return E_POINTER;
314
315     return ++(This->ref);
316 }
317
318 static HRESULT WINAPI COMCAT_IEnumCATEGORYINFO_QueryInterface(
319     LPENUMCATEGORYINFO iface,
320     REFIID riid,
321     LPVOID *ppvObj)
322 {
323     ICOM_THIS(IEnumCATEGORYINFOImpl, iface);
324     TRACE("\n\tIID:\t%s\n",debugstr_guid(riid));
325
326     if (This == NULL || ppvObj == NULL) return E_POINTER;
327
328     if (IsEqualGUID(riid, &IID_IUnknown) ||
329         IsEqualGUID(riid, &IID_IEnumCATEGORYINFO))
330     {
331         *ppvObj = (LPVOID)iface;
332         COMCAT_IEnumCATEGORYINFO_AddRef(iface);
333         return S_OK;
334     }
335
336     return E_NOINTERFACE;
337 }
338
339 static ULONG WINAPI COMCAT_IEnumCATEGORYINFO_Release(LPENUMCATEGORYINFO iface)
340 {
341     ICOM_THIS(IEnumCATEGORYINFOImpl, iface);
342     TRACE("\n");
343
344     if (This == NULL) return E_POINTER;
345
346     if (--(This->ref) == 0) {
347         if (This->key) RegCloseKey(This->key);
348         HeapFree(GetProcessHeap(), 0, This);
349         return 0;
350     }
351     return This->ref;
352 }
353
354 static HRESULT WINAPI COMCAT_IEnumCATEGORYINFO_Next(
355     LPENUMCATEGORYINFO iface,
356     ULONG celt,
357     CATEGORYINFO *rgelt,
358     ULONG *pceltFetched)
359 {
360     ICOM_THIS(IEnumCATEGORYINFOImpl, iface);
361     ULONG fetched = 0;
362
363     TRACE("\n");
364
365     if (This == NULL || rgelt == NULL) return E_POINTER;
366
367     if (This->key) while (fetched < celt) {
368         HRESULT res;
369         WCHAR catid[39];
370         DWORD cName = 39;
371         HKEY subkey;
372
373         res = RegEnumKeyExW(This->key, This->next_index, catid, &cName,
374                             NULL, NULL, NULL, NULL);
375         if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA) break;
376         ++(This->next_index);
377
378         res = CLSIDFromString(catid, &rgelt->catid);
379         if (FAILED(res)) continue;
380
381         res = RegOpenKeyExW(This->key, catid, 0, KEY_READ, &subkey);
382         if (res != ERROR_SUCCESS) continue;
383
384         res = COMCAT_GetCategoryDesc(subkey, This->lcid,
385                                      rgelt->szDescription, 128);
386         RegCloseKey(subkey);
387         if (FAILED(res)) continue;
388
389         rgelt->lcid = This->lcid;
390         ++fetched;
391         ++rgelt;
392     }
393
394     if (pceltFetched) *pceltFetched = fetched;
395     return fetched == celt ? S_OK : S_FALSE;
396 }
397
398 static HRESULT WINAPI COMCAT_IEnumCATEGORYINFO_Skip(
399     LPENUMCATEGORYINFO iface,
400     ULONG celt)
401 {
402     ICOM_THIS(IEnumCATEGORYINFOImpl, iface);
403
404     TRACE("\n");
405
406     if (This == NULL) return E_POINTER;
407     This->next_index += celt;
408     /* This should return S_FALSE when there aren't celt elems to skip. */
409     return S_OK;
410 }
411
412 static HRESULT WINAPI COMCAT_IEnumCATEGORYINFO_Reset(LPENUMCATEGORYINFO iface)
413 {
414     ICOM_THIS(IEnumCATEGORYINFOImpl, iface);
415
416     TRACE("\n");
417
418     if (This == NULL) return E_POINTER;
419     This->next_index = 0;
420     return S_OK;
421 }
422
423 static HRESULT WINAPI COMCAT_IEnumCATEGORYINFO_Clone(
424     LPENUMCATEGORYINFO iface,
425     IEnumCATEGORYINFO **ppenum)
426 {
427     ICOM_THIS(IEnumCATEGORYINFOImpl, iface);
428     WCHAR keyname[21] = { 'C', 'o', 'm', 'p', 'o', 'n', 'e', 'n',
429                           't', ' ', 'C', 'a', 't', 'e', 'g', 'o',
430                           'r', 'i', 'e', 's', 0 };
431     IEnumCATEGORYINFOImpl *new_this;
432
433     TRACE("\n");
434
435     if (This == NULL || ppenum == NULL) return E_POINTER;
436
437     new_this = (IEnumCATEGORYINFOImpl *) HeapAlloc(
438         GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IEnumCATEGORYINFOImpl));
439     if (new_this == NULL) return E_OUTOFMEMORY;
440
441     new_this->lpVtbl = This->lpVtbl;
442     new_this->ref = 1;
443     new_this->lcid = This->lcid;
444     /* FIXME: could we more efficiently use DuplicateHandle? */
445     RegOpenKeyExW(HKEY_CLASSES_ROOT, keyname, 0, KEY_READ, &new_this->key);
446     new_this->next_index = This->next_index;
447
448     *ppenum = (LPENUMCATEGORYINFO)new_this;
449     return S_OK;
450 }
451
452 ICOM_VTABLE(IEnumCATEGORYINFO) COMCAT_IEnumCATEGORYINFO_Vtbl =
453 {
454     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
455     COMCAT_IEnumCATEGORYINFO_QueryInterface,
456     COMCAT_IEnumCATEGORYINFO_AddRef,
457     COMCAT_IEnumCATEGORYINFO_Release,
458     COMCAT_IEnumCATEGORYINFO_Next,
459     COMCAT_IEnumCATEGORYINFO_Skip,
460     COMCAT_IEnumCATEGORYINFO_Reset,
461     COMCAT_IEnumCATEGORYINFO_Clone
462 };
463
464 static LPENUMCATEGORYINFO COMCAT_IEnumCATEGORYINFO_Construct(LCID lcid)
465 {
466     IEnumCATEGORYINFOImpl *This;
467
468     This = (IEnumCATEGORYINFOImpl *) HeapAlloc(
469         GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IEnumCATEGORYINFOImpl));
470     if (This) {
471         WCHAR keyname[21] = { 'C', 'o', 'm', 'p', 'o', 'n', 'e', 'n',
472                               't', ' ', 'C', 'a', 't', 'e', 'g', 'o',
473                               'r', 'i', 'e', 's', 0 };
474
475         This->lpVtbl = &COMCAT_IEnumCATEGORYINFO_Vtbl;
476         This->lcid = lcid;
477         RegOpenKeyExW(HKEY_CLASSES_ROOT, keyname, 0, KEY_READ, &This->key);
478     }
479     return (LPENUMCATEGORYINFO)This;
480 }
481
482 /**********************************************************************
483  * COMCAT_GetCategoryDesc
484  */
485 static HRESULT COMCAT_GetCategoryDesc(HKEY key, LCID lcid, PWCHAR pszDesc,
486                                       ULONG buf_wchars)
487 {
488     WCHAR fmt[4] = { '%', 'l', 'X', 0 };
489     WCHAR valname[5];
490     HRESULT res;
491     DWORD type, size = (buf_wchars - 1) * sizeof(WCHAR);
492
493     if (pszDesc == NULL) return E_INVALIDARG;
494
495     /* FIXME: lcid comparisons are more complex than this! */
496     wsprintfW(valname, fmt, lcid);
497     res = RegQueryValueExW(key, valname, 0, &type, (LPBYTE)pszDesc, &size);
498     if (res != ERROR_SUCCESS || type != REG_SZ) {
499         FIXME("Simplified lcid comparison\n");
500         return CAT_E_NODESCRIPTION;
501     }
502     pszDesc[size / sizeof(WCHAR)] = (WCHAR)0;
503
504     return S_OK;
505 }
506
507 /**********************************************************************
508  * COMCAT_PrepareClassCategories
509  */
510 static struct class_categories *COMCAT_PrepareClassCategories(
511     ULONG impl_count, CATID *impl_catids, ULONG req_count, CATID *req_catids)
512 {
513     struct class_categories *categories;
514     WCHAR *strings;
515
516     categories = (struct class_categories *)HeapAlloc(
517         GetProcessHeap(), HEAP_ZERO_MEMORY,
518         sizeof(struct class_categories) +
519         ((impl_count + req_count) * 39 + 2) * sizeof(WCHAR));
520     if (categories == NULL) return categories;
521
522     strings = (WCHAR *)(categories + 1);
523     categories->impl_strings = strings;
524     while (impl_count--) {
525         StringFromGUID2(impl_catids++, strings, 39);
526         strings += 39;
527     }
528     *strings++ = 0;
529
530     categories->req_strings = strings;
531     while (req_count--) {
532         StringFromGUID2(req_catids++, strings, 39);
533         strings += 39;
534     }
535     *strings++ = 0;
536
537     return categories;
538 }
539
540 /**********************************************************************
541  * COMCAT_IsClassOfCategories
542  */
543 static HRESULT COMCAT_IsClassOfCategories(
544     HKEY key,
545     struct class_categories const* categories)
546 {
547     WCHAR impl_keyname[23] = { 'I', 'm', 'p', 'l', 'e', 'm', 'e', 'n',
548                                't', 'e', 'd', ' ', 'C', 'a', 't', 'e',
549                                'g', 'o', 'r', 'i', 'e', 's', 0 };
550     WCHAR req_keyname[20] = { 'R', 'e', 'q', 'u', 'i', 'r', 'e', 'd',
551                               ' ', 'C', 'a', 't', 'e', 'g', 'o', 'r',
552                               'i', 'e', 's', 0 };
553     HKEY subkey;
554     HRESULT res;
555     DWORD index;
556     LPCWSTR string;
557
558     /* Check that every given category is implemented by class. */
559     res = RegOpenKeyExW(key, impl_keyname, 0, KEY_READ, &subkey);
560     if (res != ERROR_SUCCESS) return S_FALSE;
561     for (string = categories->impl_strings; *string; string += 39) {
562         HKEY catkey;
563         res = RegOpenKeyExW(subkey, string, 0, 0, &catkey);
564         if (res != ERROR_SUCCESS) {
565             RegCloseKey(subkey);
566             return S_FALSE;
567         }
568         RegCloseKey(catkey);
569     }
570     RegCloseKey(subkey);
571
572     /* Check that all categories required by class are given. */
573     res = RegOpenKeyExW(key, req_keyname, 0, KEY_READ, &subkey);
574     if (res == ERROR_SUCCESS) {
575         for (index = 0; ; ++index) {
576             WCHAR keyname[39];
577             DWORD size = 39;
578
579             res = RegEnumKeyExW(subkey, index, keyname, &size,
580                                 NULL, NULL, NULL, NULL);
581             if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA) break;
582             if (size != 38) continue; /* bogus catid in registry */
583             for (string = categories->req_strings; *string; string += 39)
584                 if (!strcmpiW(string, keyname)) break;
585             if (!*string) {
586                 RegCloseKey(subkey);
587                 return S_FALSE;
588             }
589         }
590         RegCloseKey(subkey);
591     }
592
593     return S_OK;
594 }
595
596 /**********************************************************************
597  * ClassesOfCategories IEnumCLSID (IEnumGUID) implementation
598  *
599  * This implementation is not thread-safe.  The manager itself is, but
600  * I can't imagine a valid use of an enumerator in several threads.
601  */
602 typedef struct
603 {
604     ICOM_VFIELD(IEnumGUID);
605     DWORD ref;
606     struct class_categories const *categories;
607     HKEY  key;
608     DWORD next_index;
609 } CLSID_IEnumGUIDImpl;
610
611 static ULONG WINAPI COMCAT_CLSID_IEnumGUID_AddRef(LPENUMGUID iface)
612 {
613     ICOM_THIS(CLSID_IEnumGUIDImpl, iface);
614     TRACE("\n");
615
616     if (This == NULL) return E_POINTER;
617
618     return ++(This->ref);
619 }
620
621 static HRESULT WINAPI COMCAT_CLSID_IEnumGUID_QueryInterface(
622     LPENUMGUID iface,
623     REFIID riid,
624     LPVOID *ppvObj)
625 {
626     ICOM_THIS(CLSID_IEnumGUIDImpl, iface);
627     TRACE("\n\tIID:\t%s\n",debugstr_guid(riid));
628
629     if (This == NULL || ppvObj == NULL) return E_POINTER;
630
631     if (IsEqualGUID(riid, &IID_IUnknown) ||
632         IsEqualGUID(riid, &IID_IEnumGUID))
633     {
634         *ppvObj = (LPVOID)iface;
635         COMCAT_CLSID_IEnumGUID_AddRef(iface);
636         return S_OK;
637     }
638
639     return E_NOINTERFACE;
640 }
641
642 static ULONG WINAPI COMCAT_CLSID_IEnumGUID_Release(LPENUMGUID iface)
643 {
644     ICOM_THIS(CLSID_IEnumGUIDImpl, iface);
645     TRACE("\n");
646
647     if (This == NULL) return E_POINTER;
648
649     if (--(This->ref) == 0) {
650         if (This->key) RegCloseKey(This->key);
651         HeapFree(GetProcessHeap(), 0, (LPVOID)This->categories);
652         HeapFree(GetProcessHeap(), 0, This);
653         return 0;
654     }
655     return This->ref;
656 }
657
658 static HRESULT WINAPI COMCAT_CLSID_IEnumGUID_Next(
659     LPENUMGUID iface,
660     ULONG celt,
661     GUID *rgelt,
662     ULONG *pceltFetched)
663 {
664     ICOM_THIS(CLSID_IEnumGUIDImpl, iface);
665     ULONG fetched = 0;
666
667     TRACE("\n");
668
669     if (This == NULL || rgelt == NULL) return E_POINTER;
670
671     if (This->key) while (fetched < celt) {
672         HRESULT res;
673         WCHAR clsid[39];
674         DWORD cName = 39;
675         HKEY subkey;
676
677         res = RegEnumKeyExW(This->key, This->next_index, clsid, &cName,
678                             NULL, NULL, NULL, NULL);
679         if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA) break;
680         ++(This->next_index);
681
682         res = CLSIDFromString(clsid, rgelt);
683         if (FAILED(res)) continue;
684
685         res = RegOpenKeyExW(This->key, clsid, 0, KEY_READ, &subkey);
686         if (res != ERROR_SUCCESS) continue;
687
688         res = COMCAT_IsClassOfCategories(subkey, This->categories);
689         RegCloseKey(subkey);
690         if (res != S_OK) continue;
691
692         ++fetched;
693         ++rgelt;
694     }
695
696     if (pceltFetched) *pceltFetched = fetched;
697     return fetched == celt ? S_OK : S_FALSE;
698 }
699
700 static HRESULT WINAPI COMCAT_CLSID_IEnumGUID_Skip(
701     LPENUMGUID iface,
702     ULONG celt)
703 {
704     ICOM_THIS(CLSID_IEnumGUIDImpl, iface);
705
706     TRACE("\n");
707
708     if (This == NULL) return E_POINTER;
709     This->next_index += celt;
710     FIXME("Never returns S_FALSE\n");
711     return S_OK;
712 }
713
714 static HRESULT WINAPI COMCAT_CLSID_IEnumGUID_Reset(LPENUMGUID iface)
715 {
716     ICOM_THIS(CLSID_IEnumGUIDImpl, iface);
717
718     TRACE("\n");
719
720     if (This == NULL) return E_POINTER;
721     This->next_index = 0;
722     return S_OK;
723 }
724
725 static HRESULT WINAPI COMCAT_CLSID_IEnumGUID_Clone(
726     LPENUMGUID iface,
727     IEnumGUID **ppenum)
728 {
729     ICOM_THIS(CLSID_IEnumGUIDImpl, iface);
730     WCHAR keyname[6] = { 'C', 'L', 'S', 'I', 'D', 0 };
731     CLSID_IEnumGUIDImpl *new_this;
732     DWORD size;
733
734     TRACE("\n");
735
736     if (This == NULL || ppenum == NULL) return E_POINTER;
737
738     new_this = (CLSID_IEnumGUIDImpl *) HeapAlloc(
739         GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CLSID_IEnumGUIDImpl));
740     if (new_this == NULL) return E_OUTOFMEMORY;
741
742     new_this->lpVtbl = This->lpVtbl;
743     new_this->ref = 1;
744     size = HeapSize(GetProcessHeap(), 0, (LPVOID)This->categories);
745     new_this->categories = (struct class_categories *)
746         HeapAlloc(GetProcessHeap(), 0, size);
747     if (new_this->categories == NULL) {
748         HeapFree(GetProcessHeap(), 0, new_this);
749         return E_OUTOFMEMORY;
750     }
751     memcpy((LPVOID)new_this->categories, This->categories, size);
752     /* FIXME: could we more efficiently use DuplicateHandle? */
753     RegOpenKeyExW(HKEY_CLASSES_ROOT, keyname, 0, KEY_READ, &new_this->key);
754     new_this->next_index = This->next_index;
755
756     *ppenum = (LPENUMGUID)new_this;
757     return S_OK;
758 }
759
760 ICOM_VTABLE(IEnumGUID) COMCAT_CLSID_IEnumGUID_Vtbl =
761 {
762     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
763     COMCAT_CLSID_IEnumGUID_QueryInterface,
764     COMCAT_CLSID_IEnumGUID_AddRef,
765     COMCAT_CLSID_IEnumGUID_Release,
766     COMCAT_CLSID_IEnumGUID_Next,
767     COMCAT_CLSID_IEnumGUID_Skip,
768     COMCAT_CLSID_IEnumGUID_Reset,
769     COMCAT_CLSID_IEnumGUID_Clone
770 };
771
772 static LPENUMGUID COMCAT_CLSID_IEnumGUID_Construct(
773     struct class_categories const* categories)
774 {
775     CLSID_IEnumGUIDImpl *This;
776
777     This = (CLSID_IEnumGUIDImpl *) HeapAlloc(
778         GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CLSID_IEnumGUIDImpl));
779     if (This) {
780         WCHAR keyname[6] = { 'C', 'L', 'S', 'I', 'D', 0 };
781
782         This->lpVtbl = &COMCAT_CLSID_IEnumGUID_Vtbl;
783         This->categories = categories;
784         RegOpenKeyExW(HKEY_CLASSES_ROOT, keyname, 0, KEY_READ, &This->key);
785     }
786     return (LPENUMGUID)This;
787 }
788
789 /**********************************************************************
790  * CategoriesOfClass IEnumCATID (IEnumGUID) implementation
791  *
792  * This implementation is not thread-safe.  The manager itself is, but
793  * I can't imagine a valid use of an enumerator in several threads.
794  */
795 typedef struct
796 {
797     ICOM_VFIELD(IEnumGUID);
798     DWORD ref;
799     WCHAR keyname[68];
800     HKEY  key;
801     DWORD next_index;
802 } CATID_IEnumGUIDImpl;
803
804 static ULONG WINAPI COMCAT_CATID_IEnumGUID_AddRef(LPENUMGUID iface)
805 {
806     ICOM_THIS(CATID_IEnumGUIDImpl, iface);
807     TRACE("\n");
808
809     if (This == NULL) return E_POINTER;
810
811     return ++(This->ref);
812 }
813
814 static HRESULT WINAPI COMCAT_CATID_IEnumGUID_QueryInterface(
815     LPENUMGUID iface,
816     REFIID riid,
817     LPVOID *ppvObj)
818 {
819     ICOM_THIS(CATID_IEnumGUIDImpl, iface);
820     TRACE("\n\tIID:\t%s\n",debugstr_guid(riid));
821
822     if (This == NULL || ppvObj == NULL) return E_POINTER;
823
824     if (IsEqualGUID(riid, &IID_IUnknown) ||
825         IsEqualGUID(riid, &IID_IEnumGUID))
826     {
827         *ppvObj = (LPVOID)iface;
828         COMCAT_CATID_IEnumGUID_AddRef(iface);
829         return S_OK;
830     }
831
832     return E_NOINTERFACE;
833 }
834
835 static ULONG WINAPI COMCAT_CATID_IEnumGUID_Release(LPENUMGUID iface)
836 {
837     ICOM_THIS(CATID_IEnumGUIDImpl, iface);
838     TRACE("\n");
839
840     if (This == NULL) return E_POINTER;
841
842     if (--(This->ref) == 0) {
843         if (This->key) RegCloseKey(This->key);
844         HeapFree(GetProcessHeap(), 0, This);
845         return 0;
846     }
847     return This->ref;
848 }
849
850 static HRESULT WINAPI COMCAT_CATID_IEnumGUID_Next(
851     LPENUMGUID iface,
852     ULONG celt,
853     GUID *rgelt,
854     ULONG *pceltFetched)
855 {
856     ICOM_THIS(CATID_IEnumGUIDImpl, iface);
857     ULONG fetched = 0;
858
859     TRACE("\n");
860
861     if (This == NULL || rgelt == NULL) return E_POINTER;
862
863     if (This->key) while (fetched < celt) {
864         HRESULT res;
865         WCHAR catid[39];
866         DWORD cName = 39;
867
868         res = RegEnumKeyExW(This->key, This->next_index, catid, &cName,
869                             NULL, NULL, NULL, NULL);
870         if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA) break;
871         ++(This->next_index);
872
873         res = CLSIDFromString(catid, rgelt);
874         if (FAILED(res)) continue;
875
876         ++fetched;
877         ++rgelt;
878     }
879
880     if (pceltFetched) *pceltFetched = fetched;
881     return fetched == celt ? S_OK : S_FALSE;
882 }
883
884 static HRESULT WINAPI COMCAT_CATID_IEnumGUID_Skip(
885     LPENUMGUID iface,
886     ULONG celt)
887 {
888     ICOM_THIS(CATID_IEnumGUIDImpl, iface);
889
890     TRACE("\n");
891
892     if (This == NULL) return E_POINTER;
893     This->next_index += celt;
894     FIXME("Never returns S_FALSE\n");
895     return S_OK;
896 }
897
898 static HRESULT WINAPI COMCAT_CATID_IEnumGUID_Reset(LPENUMGUID iface)
899 {
900     ICOM_THIS(CATID_IEnumGUIDImpl, iface);
901
902     TRACE("\n");
903
904     if (This == NULL) return E_POINTER;
905     This->next_index = 0;
906     return S_OK;
907 }
908
909 static HRESULT WINAPI COMCAT_CATID_IEnumGUID_Clone(
910     LPENUMGUID iface,
911     IEnumGUID **ppenum)
912 {
913     ICOM_THIS(CATID_IEnumGUIDImpl, iface);
914     CATID_IEnumGUIDImpl *new_this;
915
916     TRACE("\n");
917
918     if (This == NULL || ppenum == NULL) return E_POINTER;
919
920     new_this = (CATID_IEnumGUIDImpl *) HeapAlloc(
921         GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CATID_IEnumGUIDImpl));
922     if (new_this == NULL) return E_OUTOFMEMORY;
923
924     new_this->lpVtbl = This->lpVtbl;
925     new_this->ref = 1;
926     lstrcpyW(new_this->keyname, This->keyname);
927     /* FIXME: could we more efficiently use DuplicateHandle? */
928     RegOpenKeyExW(HKEY_CLASSES_ROOT, new_this->keyname, 0, KEY_READ, &new_this->key);
929     new_this->next_index = This->next_index;
930
931     *ppenum = (LPENUMGUID)new_this;
932     return S_OK;
933 }
934
935 ICOM_VTABLE(IEnumGUID) COMCAT_CATID_IEnumGUID_Vtbl =
936 {
937     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
938     COMCAT_CATID_IEnumGUID_QueryInterface,
939     COMCAT_CATID_IEnumGUID_AddRef,
940     COMCAT_CATID_IEnumGUID_Release,
941     COMCAT_CATID_IEnumGUID_Next,
942     COMCAT_CATID_IEnumGUID_Skip,
943     COMCAT_CATID_IEnumGUID_Reset,
944     COMCAT_CATID_IEnumGUID_Clone
945 };
946
947 static LPENUMGUID COMCAT_CATID_IEnumGUID_Construct(
948     REFCLSID rclsid, LPCWSTR postfix)
949 {
950     CATID_IEnumGUIDImpl *This;
951
952     This = (CATID_IEnumGUIDImpl *) HeapAlloc(
953         GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CATID_IEnumGUIDImpl));
954     if (This) {
955         WCHAR prefix[6] = { 'C', 'L', 'S', 'I', 'D', '\\' };
956
957         This->lpVtbl = &COMCAT_CATID_IEnumGUID_Vtbl;
958         memcpy(This->keyname, prefix, sizeof prefix);
959         StringFromGUID2(rclsid, This->keyname + 6, 39);
960         lstrcpyW(This->keyname + 44, postfix);
961         RegOpenKeyExW(HKEY_CLASSES_ROOT, This->keyname, 0, KEY_READ, &This->key);
962     }
963     return (LPENUMGUID)This;
964 }