Authors: Michael Lin <mlin@corvu.com.au>, Michael Jung <mjung@iss.tu-darmstadt.de>
[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     static const 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     static const 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 const 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     const 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
311     TRACE("\n");
312
313     if (This == NULL) return E_POINTER;
314
315     return InterlockedIncrement(&This->ref);
316 }
317
318 static HRESULT WINAPI COMCAT_IEnumCATEGORYINFO_QueryInterface(
319     LPENUMCATEGORYINFO iface,
320     REFIID riid,
321     LPVOID *ppvObj)
322 {
323     IEnumCATEGORYINFOImpl *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     IEnumCATEGORYINFOImpl *This = (IEnumCATEGORYINFOImpl *)iface;
342     ULONG ref;
343
344     TRACE("\n");
345
346     if (This == NULL) return E_POINTER;
347
348     ref = InterlockedDecrement(&This->ref);
349     if (ref == 0) {
350         if (This->key) RegCloseKey(This->key);
351         HeapFree(GetProcessHeap(), 0, This);
352         return 0;
353     }
354     return ref;
355 }
356
357 static HRESULT WINAPI COMCAT_IEnumCATEGORYINFO_Next(
358     LPENUMCATEGORYINFO iface,
359     ULONG celt,
360     CATEGORYINFO *rgelt,
361     ULONG *pceltFetched)
362 {
363     IEnumCATEGORYINFOImpl *This = (IEnumCATEGORYINFOImpl *)iface;
364     ULONG fetched = 0;
365
366     TRACE("\n");
367
368     if (This == NULL || rgelt == NULL) return E_POINTER;
369
370     if (This->key) while (fetched < celt) {
371         HRESULT res;
372         WCHAR catid[39];
373         DWORD cName = 39;
374         HKEY subkey;
375
376         res = RegEnumKeyExW(This->key, This->next_index, catid, &cName,
377                             NULL, NULL, NULL, NULL);
378         if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA) break;
379         ++(This->next_index);
380
381         res = CLSIDFromString(catid, &rgelt->catid);
382         if (FAILED(res)) continue;
383
384         res = RegOpenKeyExW(This->key, catid, 0, KEY_READ, &subkey);
385         if (res != ERROR_SUCCESS) continue;
386
387         res = COMCAT_GetCategoryDesc(subkey, This->lcid,
388                                      rgelt->szDescription, 128);
389         RegCloseKey(subkey);
390         if (FAILED(res)) continue;
391
392         rgelt->lcid = This->lcid;
393         ++fetched;
394         ++rgelt;
395     }
396
397     if (pceltFetched) *pceltFetched = fetched;
398     return fetched == celt ? S_OK : S_FALSE;
399 }
400
401 static HRESULT WINAPI COMCAT_IEnumCATEGORYINFO_Skip(
402     LPENUMCATEGORYINFO iface,
403     ULONG celt)
404 {
405     IEnumCATEGORYINFOImpl *This = (IEnumCATEGORYINFOImpl *)iface;
406
407     TRACE("\n");
408
409     if (This == NULL) return E_POINTER;
410     This->next_index += celt;
411     /* This should return S_FALSE when there aren't celt elems to skip. */
412     return S_OK;
413 }
414
415 static HRESULT WINAPI COMCAT_IEnumCATEGORYINFO_Reset(LPENUMCATEGORYINFO iface)
416 {
417     IEnumCATEGORYINFOImpl *This = (IEnumCATEGORYINFOImpl *)iface;
418
419     TRACE("\n");
420
421     if (This == NULL) return E_POINTER;
422     This->next_index = 0;
423     return S_OK;
424 }
425
426 static HRESULT WINAPI COMCAT_IEnumCATEGORYINFO_Clone(
427     LPENUMCATEGORYINFO iface,
428     IEnumCATEGORYINFO **ppenum)
429 {
430     IEnumCATEGORYINFOImpl *This = (IEnumCATEGORYINFOImpl *)iface;
431     WCHAR keyname[21] = { 'C', 'o', 'm', 'p', 'o', 'n', 'e', 'n',
432                           't', ' ', 'C', 'a', 't', 'e', 'g', 'o',
433                           'r', 'i', 'e', 's', 0 };
434     IEnumCATEGORYINFOImpl *new_this;
435
436     TRACE("\n");
437
438     if (This == NULL || ppenum == NULL) return E_POINTER;
439
440     new_this = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IEnumCATEGORYINFOImpl));
441     if (new_this == NULL) return E_OUTOFMEMORY;
442
443     new_this->lpVtbl = This->lpVtbl;
444     new_this->ref = 1;
445     new_this->lcid = This->lcid;
446     /* FIXME: could we more efficiently use DuplicateHandle? */
447     RegOpenKeyExW(HKEY_CLASSES_ROOT, keyname, 0, KEY_READ, &new_this->key);
448     new_this->next_index = This->next_index;
449
450     *ppenum = (LPENUMCATEGORYINFO)new_this;
451     return S_OK;
452 }
453
454 static const IEnumCATEGORYINFOVtbl COMCAT_IEnumCATEGORYINFO_Vtbl =
455 {
456     COMCAT_IEnumCATEGORYINFO_QueryInterface,
457     COMCAT_IEnumCATEGORYINFO_AddRef,
458     COMCAT_IEnumCATEGORYINFO_Release,
459     COMCAT_IEnumCATEGORYINFO_Next,
460     COMCAT_IEnumCATEGORYINFO_Skip,
461     COMCAT_IEnumCATEGORYINFO_Reset,
462     COMCAT_IEnumCATEGORYINFO_Clone
463 };
464
465 static LPENUMCATEGORYINFO COMCAT_IEnumCATEGORYINFO_Construct(LCID lcid)
466 {
467     IEnumCATEGORYINFOImpl *This;
468
469     This = HeapAlloc(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 = 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     const IEnumGUIDVtbl *lpVtbl;
605     DWORD ref;
606     const struct class_categories *categories;
607     HKEY  key;
608     DWORD next_index;
609 } CLSID_IEnumGUIDImpl;
610
611 static ULONG WINAPI COMCAT_CLSID_IEnumGUID_AddRef(LPENUMGUID iface)
612 {
613     CLSID_IEnumGUIDImpl *This = (CLSID_IEnumGUIDImpl *)iface;
614     TRACE("\n");
615
616     if (This == NULL) return E_POINTER;
617
618     return InterlockedIncrement(&This->ref);
619 }
620
621 static HRESULT WINAPI COMCAT_CLSID_IEnumGUID_QueryInterface(
622     LPENUMGUID iface,
623     REFIID riid,
624     LPVOID *ppvObj)
625 {
626     CLSID_IEnumGUIDImpl *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     CLSID_IEnumGUIDImpl *This = (CLSID_IEnumGUIDImpl *)iface;
645     ULONG ref;
646
647     TRACE("\n");
648
649     if (This == NULL) return E_POINTER;
650
651     ref = InterlockedDecrement(&This->ref);
652     if (ref == 0) {
653         if (This->key) RegCloseKey(This->key);
654         HeapFree(GetProcessHeap(), 0, (LPVOID)This->categories);
655         HeapFree(GetProcessHeap(), 0, This);
656         return 0;
657     }
658     return ref;
659 }
660
661 static HRESULT WINAPI COMCAT_CLSID_IEnumGUID_Next(
662     LPENUMGUID iface,
663     ULONG celt,
664     GUID *rgelt,
665     ULONG *pceltFetched)
666 {
667     CLSID_IEnumGUIDImpl *This = (CLSID_IEnumGUIDImpl *)iface;
668     ULONG fetched = 0;
669
670     TRACE("\n");
671
672     if (This == NULL || rgelt == NULL) return E_POINTER;
673
674     if (This->key) while (fetched < celt) {
675         HRESULT res;
676         WCHAR clsid[39];
677         DWORD cName = 39;
678         HKEY subkey;
679
680         res = RegEnumKeyExW(This->key, This->next_index, clsid, &cName,
681                             NULL, NULL, NULL, NULL);
682         if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA) break;
683         ++(This->next_index);
684
685         res = CLSIDFromString(clsid, rgelt);
686         if (FAILED(res)) continue;
687
688         res = RegOpenKeyExW(This->key, clsid, 0, KEY_READ, &subkey);
689         if (res != ERROR_SUCCESS) continue;
690
691         res = COMCAT_IsClassOfCategories(subkey, This->categories);
692         RegCloseKey(subkey);
693         if (res != S_OK) continue;
694
695         ++fetched;
696         ++rgelt;
697     }
698
699     if (pceltFetched) *pceltFetched = fetched;
700     return fetched == celt ? S_OK : S_FALSE;
701 }
702
703 static HRESULT WINAPI COMCAT_CLSID_IEnumGUID_Skip(
704     LPENUMGUID iface,
705     ULONG celt)
706 {
707     CLSID_IEnumGUIDImpl *This = (CLSID_IEnumGUIDImpl *)iface;
708
709     TRACE("\n");
710
711     if (This == NULL) return E_POINTER;
712     This->next_index += celt;
713     FIXME("Never returns S_FALSE\n");
714     return S_OK;
715 }
716
717 static HRESULT WINAPI COMCAT_CLSID_IEnumGUID_Reset(LPENUMGUID iface)
718 {
719     CLSID_IEnumGUIDImpl *This = (CLSID_IEnumGUIDImpl *)iface;
720
721     TRACE("\n");
722
723     if (This == NULL) return E_POINTER;
724     This->next_index = 0;
725     return S_OK;
726 }
727
728 static HRESULT WINAPI COMCAT_CLSID_IEnumGUID_Clone(
729     LPENUMGUID iface,
730     IEnumGUID **ppenum)
731 {
732     CLSID_IEnumGUIDImpl *This = (CLSID_IEnumGUIDImpl *)iface;
733     WCHAR keyname[6] = { 'C', 'L', 'S', 'I', 'D', 0 };
734     CLSID_IEnumGUIDImpl *new_this;
735     DWORD size;
736
737     TRACE("\n");
738
739     if (This == NULL || ppenum == NULL) return E_POINTER;
740
741     new_this = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CLSID_IEnumGUIDImpl));
742     if (new_this == NULL) return E_OUTOFMEMORY;
743
744     new_this->lpVtbl = This->lpVtbl;
745     new_this->ref = 1;
746     size = HeapSize(GetProcessHeap(), 0, (LPVOID)This->categories);
747     new_this->categories =
748         HeapAlloc(GetProcessHeap(), 0, size);
749     if (new_this->categories == NULL) {
750         HeapFree(GetProcessHeap(), 0, new_this);
751         return E_OUTOFMEMORY;
752     }
753     memcpy((LPVOID)new_this->categories, This->categories, size);
754     /* FIXME: could we more efficiently use DuplicateHandle? */
755     RegOpenKeyExW(HKEY_CLASSES_ROOT, keyname, 0, KEY_READ, &new_this->key);
756     new_this->next_index = This->next_index;
757
758     *ppenum = (LPENUMGUID)new_this;
759     return S_OK;
760 }
761
762 static const IEnumGUIDVtbl COMCAT_CLSID_IEnumGUID_Vtbl =
763 {
764     COMCAT_CLSID_IEnumGUID_QueryInterface,
765     COMCAT_CLSID_IEnumGUID_AddRef,
766     COMCAT_CLSID_IEnumGUID_Release,
767     COMCAT_CLSID_IEnumGUID_Next,
768     COMCAT_CLSID_IEnumGUID_Skip,
769     COMCAT_CLSID_IEnumGUID_Reset,
770     COMCAT_CLSID_IEnumGUID_Clone
771 };
772
773 static LPENUMGUID COMCAT_CLSID_IEnumGUID_Construct(
774     struct class_categories const* categories)
775 {
776     CLSID_IEnumGUIDImpl *This;
777
778     This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CLSID_IEnumGUIDImpl));
779     if (This) {
780         static const WCHAR keyname[] = { '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     const IEnumGUIDVtbl *lpVtbl;
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     CATID_IEnumGUIDImpl *This = (CATID_IEnumGUIDImpl *)iface;
807     TRACE("\n");
808
809     if (This == NULL) return E_POINTER;
810
811     return InterlockedIncrement(&This->ref);
812 }
813
814 static HRESULT WINAPI COMCAT_CATID_IEnumGUID_QueryInterface(
815     LPENUMGUID iface,
816     REFIID riid,
817     LPVOID *ppvObj)
818 {
819     CATID_IEnumGUIDImpl *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     CATID_IEnumGUIDImpl *This = (CATID_IEnumGUIDImpl *)iface;
838     ULONG ref;
839
840     TRACE("\n");
841
842     if (This == NULL) return E_POINTER;
843
844     ref = InterlockedDecrement(&This->ref);
845     if (ref == 0) {
846         if (This->key) RegCloseKey(This->key);
847         HeapFree(GetProcessHeap(), 0, This);
848         return 0;
849     }
850     return ref;
851 }
852
853 static HRESULT WINAPI COMCAT_CATID_IEnumGUID_Next(
854     LPENUMGUID iface,
855     ULONG celt,
856     GUID *rgelt,
857     ULONG *pceltFetched)
858 {
859     CATID_IEnumGUIDImpl *This = (CATID_IEnumGUIDImpl *)iface;
860     ULONG fetched = 0;
861
862     TRACE("\n");
863
864     if (This == NULL || rgelt == NULL) return E_POINTER;
865
866     if (This->key) while (fetched < celt) {
867         HRESULT res;
868         WCHAR catid[39];
869         DWORD cName = 39;
870
871         res = RegEnumKeyExW(This->key, This->next_index, catid, &cName,
872                             NULL, NULL, NULL, NULL);
873         if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA) break;
874         ++(This->next_index);
875
876         res = CLSIDFromString(catid, rgelt);
877         if (FAILED(res)) continue;
878
879         ++fetched;
880         ++rgelt;
881     }
882
883     if (pceltFetched) *pceltFetched = fetched;
884     return fetched == celt ? S_OK : S_FALSE;
885 }
886
887 static HRESULT WINAPI COMCAT_CATID_IEnumGUID_Skip(
888     LPENUMGUID iface,
889     ULONG celt)
890 {
891     CATID_IEnumGUIDImpl *This = (CATID_IEnumGUIDImpl *)iface;
892
893     TRACE("\n");
894
895     if (This == NULL) return E_POINTER;
896     This->next_index += celt;
897     FIXME("Never returns S_FALSE\n");
898     return S_OK;
899 }
900
901 static HRESULT WINAPI COMCAT_CATID_IEnumGUID_Reset(LPENUMGUID iface)
902 {
903     CATID_IEnumGUIDImpl *This = (CATID_IEnumGUIDImpl *)iface;
904
905     TRACE("\n");
906
907     if (This == NULL) return E_POINTER;
908     This->next_index = 0;
909     return S_OK;
910 }
911
912 static HRESULT WINAPI COMCAT_CATID_IEnumGUID_Clone(
913     LPENUMGUID iface,
914     IEnumGUID **ppenum)
915 {
916     CATID_IEnumGUIDImpl *This = (CATID_IEnumGUIDImpl *)iface;
917     CATID_IEnumGUIDImpl *new_this;
918
919     TRACE("\n");
920
921     if (This == NULL || ppenum == NULL) return E_POINTER;
922
923     new_this = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CATID_IEnumGUIDImpl));
924     if (new_this == NULL) return E_OUTOFMEMORY;
925
926     new_this->lpVtbl = This->lpVtbl;
927     new_this->ref = 1;
928     lstrcpyW(new_this->keyname, This->keyname);
929     /* FIXME: could we more efficiently use DuplicateHandle? */
930     RegOpenKeyExW(HKEY_CLASSES_ROOT, new_this->keyname, 0, KEY_READ, &new_this->key);
931     new_this->next_index = This->next_index;
932
933     *ppenum = (LPENUMGUID)new_this;
934     return S_OK;
935 }
936
937 static const IEnumGUIDVtbl COMCAT_CATID_IEnumGUID_Vtbl =
938 {
939     COMCAT_CATID_IEnumGUID_QueryInterface,
940     COMCAT_CATID_IEnumGUID_AddRef,
941     COMCAT_CATID_IEnumGUID_Release,
942     COMCAT_CATID_IEnumGUID_Next,
943     COMCAT_CATID_IEnumGUID_Skip,
944     COMCAT_CATID_IEnumGUID_Reset,
945     COMCAT_CATID_IEnumGUID_Clone
946 };
947
948 static LPENUMGUID COMCAT_CATID_IEnumGUID_Construct(
949     REFCLSID rclsid, LPCWSTR postfix)
950 {
951     CATID_IEnumGUIDImpl *This;
952
953     This = HeapAlloc(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 }