kernel32/tests: Fix some integer to pointer conversion warnings.
[wine] / dlls / ole32 / comcat.c
1 /*
2  * Comcat implementation
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include <string.h>
22 #include <stdarg.h>
23
24 #define COBJMACROS
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winuser.h"
29 #include "winreg.h"
30 #include "winerror.h"
31
32 #include "ole2.h"
33 #include "comcat.h"
34 #include "wine/unicode.h"
35 #include "wine/debug.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(ole);
38
39 typedef struct
40 {
41     const ICatRegisterVtbl *lpVtbl;
42     const ICatInformationVtbl *infVtbl;
43 } ComCatMgrImpl;
44
45 struct class_categories {
46     LPCWSTR impl_strings;
47     LPCWSTR req_strings;
48 };
49
50 static LPENUMCATEGORYINFO COMCAT_IEnumCATEGORYINFO_Construct(LCID lcid);
51 static LPENUMGUID COMCAT_CLSID_IEnumGUID_Construct(struct class_categories *class_categories);
52 static LPENUMGUID COMCAT_CATID_IEnumGUID_Construct(REFCLSID rclsid, LPCWSTR impl_req);
53
54 /**********************************************************************
55  * File-scope string constants
56  */
57 static const WCHAR comcat_keyname[] = {
58     'C','o','m','p','o','n','e','n','t',' ','C','a','t','e','g','o','r','i','e','s',0 };
59 static const WCHAR impl_keyname[] = {
60     'I','m','p','l','e','m','e','n','t','e','d',' ','C','a','t','e','g','o','r','i','e','s',0 };
61 static const WCHAR req_keyname[] = {
62     'R','e','q','u','i','r','e','d',' ','C','a','t','e','g','o','r','i','e','s',0 };
63 static const WCHAR clsid_keyname[] = { 'C','L','S','I','D',0 };
64
65
66 static inline ComCatMgrImpl *impl_from_ICatInformation( ICatInformation *iface )
67 {
68     return (ComCatMgrImpl *)((char*)iface - FIELD_OFFSET(ComCatMgrImpl, infVtbl));
69 }
70
71
72 /**********************************************************************
73  * COMCAT_RegisterClassCategories
74  */
75 static HRESULT COMCAT_RegisterClassCategories(
76     REFCLSID rclsid,
77     LPCWSTR type,
78     ULONG cCategories,
79     const CATID *rgcatid)
80 {
81     WCHAR keyname[39];
82     HRESULT res;
83     HKEY clsid_key, class_key, type_key;
84
85     if (cCategories && rgcatid == NULL) return E_POINTER;
86
87     /* Format the class key name. */
88     res = StringFromGUID2(rclsid, keyname, 39);
89     if (FAILED(res)) return res;
90
91     /* Create (or open) the CLSID key. */
92     res = RegCreateKeyExW(HKEY_CLASSES_ROOT, clsid_keyname, 0, NULL, 0,
93                           KEY_READ | KEY_WRITE, NULL, &clsid_key, NULL);
94     if (res != ERROR_SUCCESS) return E_FAIL;
95
96     /* Create (or open) the class key. */
97     res = RegCreateKeyExW(clsid_key, keyname, 0, NULL, 0,
98                           KEY_READ | KEY_WRITE, NULL, &class_key, NULL);
99     if (res == ERROR_SUCCESS) {
100         /* Create (or open) the category type key. */
101         res = RegCreateKeyExW(class_key, type, 0, NULL, 0,
102                               KEY_READ | KEY_WRITE, NULL, &type_key, NULL);
103         if (res == ERROR_SUCCESS) {
104             for (; cCategories; --cCategories, ++rgcatid) {
105                 HKEY key;
106
107                 /* Format the category key name. */
108                 res = StringFromGUID2(rgcatid, keyname, 39);
109                 if (FAILED(res)) continue;
110
111                 /* Do the register. */
112                 res = RegCreateKeyExW(type_key, keyname, 0, NULL, 0,
113                                       KEY_READ | KEY_WRITE, NULL, &key, NULL);
114                 if (res == ERROR_SUCCESS) RegCloseKey(key);
115             }
116             res = S_OK;
117         } else res = E_FAIL;
118         RegCloseKey(class_key);
119     } else res = E_FAIL;
120     RegCloseKey(clsid_key);
121
122     return res;
123 }
124
125 /**********************************************************************
126  * COMCAT_UnRegisterClassCategories
127  */
128 static HRESULT COMCAT_UnRegisterClassCategories(
129     REFCLSID rclsid,
130     LPCWSTR type,
131     ULONG cCategories,
132     const CATID *rgcatid)
133 {
134     WCHAR keyname[68] = { 'C', 'L', 'S', 'I', 'D', '\\' };
135     HRESULT res;
136     HKEY type_key;
137
138     if (cCategories && rgcatid == NULL) return E_POINTER;
139
140     /* Format the class category type key name. */
141     res = StringFromGUID2(rclsid, keyname + 6, 39);
142     if (FAILED(res)) return res;
143     keyname[44] = '\\';
144     lstrcpyW(keyname + 45, type);
145
146     /* Open the class category type key. */
147     res = RegOpenKeyExW(HKEY_CLASSES_ROOT, keyname, 0,
148                         KEY_READ | KEY_WRITE, &type_key);
149     if (res != ERROR_SUCCESS) return E_FAIL;
150
151     for (; cCategories; --cCategories, ++rgcatid) {
152         /* Format the category key name. */
153         res = StringFromGUID2(rgcatid, keyname, 39);
154         if (FAILED(res)) continue;
155
156         /* Do the unregister. */
157         RegDeleteKeyW(type_key, keyname);
158     }
159     RegCloseKey(type_key);
160
161     return S_OK;
162 }
163
164 /**********************************************************************
165  * COMCAT_GetCategoryDesc
166  */
167 static HRESULT COMCAT_GetCategoryDesc(HKEY key, LCID lcid, PWCHAR pszDesc,
168                                       ULONG buf_wchars)
169 {
170     static const WCHAR fmt[] = { '%', 'l', 'X', 0 };
171     WCHAR valname[5];
172     HRESULT res;
173     DWORD type, size = (buf_wchars - 1) * sizeof(WCHAR);
174
175     if (pszDesc == NULL) return E_INVALIDARG;
176
177     /* FIXME: lcid comparisons are more complex than this! */
178     wsprintfW(valname, fmt, lcid);
179     res = RegQueryValueExW(key, valname, 0, &type, (LPBYTE)pszDesc, &size);
180     if (res != ERROR_SUCCESS || type != REG_SZ) {
181         FIXME("Simplified lcid comparison\n");
182         return CAT_E_NODESCRIPTION;
183     }
184     pszDesc[size / sizeof(WCHAR)] = 0;
185
186     return S_OK;
187 }
188
189 /**********************************************************************
190  * COMCAT_PrepareClassCategories
191  */
192 static struct class_categories *COMCAT_PrepareClassCategories(
193     ULONG impl_count, const CATID *impl_catids, ULONG req_count, const CATID *req_catids)
194 {
195     struct class_categories *categories;
196     WCHAR *strings;
197
198     categories = HeapAlloc(
199         GetProcessHeap(), HEAP_ZERO_MEMORY,
200         sizeof(struct class_categories) +
201         ((impl_count + req_count) * 39 + 2) * sizeof(WCHAR));
202     if (categories == NULL) return categories;
203
204     strings = (WCHAR *)(categories + 1);
205     categories->impl_strings = strings;
206     while (impl_count--) {
207         StringFromGUID2(impl_catids++, strings, 39);
208         strings += 39;
209     }
210     *strings++ = 0;
211
212     categories->req_strings = strings;
213     while (req_count--) {
214         StringFromGUID2(req_catids++, strings, 39);
215         strings += 39;
216     }
217     *strings++ = 0;
218
219     return categories;
220 }
221
222 /**********************************************************************
223  * COMCAT_IsClassOfCategories
224  */
225 static HRESULT COMCAT_IsClassOfCategories(
226     HKEY key,
227     struct class_categories const* categories)
228 {
229     static const WCHAR impl_keyname[] = { 'I', 'm', 'p', 'l', 'e', 'm', 'e', 'n',
230                                           't', 'e', 'd', ' ', 'C', 'a', 't', 'e',
231                                           'g', 'o', 'r', 'i', 'e', 's', 0 };
232     static const WCHAR req_keyname[]  = { 'R', 'e', 'q', 'u', 'i', 'r', 'e', 'd',
233                                           ' ', 'C', 'a', 't', 'e', 'g', 'o', 'r',
234                                           'i', 'e', 's', 0 };
235     HKEY subkey;
236     HRESULT res;
237     DWORD index;
238     LPCWSTR string;
239
240     /* Check that every given category is implemented by class. */
241     res = RegOpenKeyExW(key, impl_keyname, 0, KEY_READ, &subkey);
242     if (res != ERROR_SUCCESS) return S_FALSE;
243     for (string = categories->impl_strings; *string; string += 39) {
244         HKEY catkey;
245         res = RegOpenKeyExW(subkey, string, 0, 0, &catkey);
246         if (res != ERROR_SUCCESS) {
247             RegCloseKey(subkey);
248             return S_FALSE;
249         }
250         RegCloseKey(catkey);
251     }
252     RegCloseKey(subkey);
253
254     /* Check that all categories required by class are given. */
255     res = RegOpenKeyExW(key, req_keyname, 0, KEY_READ, &subkey);
256     if (res == ERROR_SUCCESS) {
257         for (index = 0; ; ++index) {
258             WCHAR keyname[39];
259             DWORD size = 39;
260
261             res = RegEnumKeyExW(subkey, index, keyname, &size,
262                                 NULL, NULL, NULL, NULL);
263             if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA) break;
264             if (size != 38) continue; /* bogus catid in registry */
265             for (string = categories->req_strings; *string; string += 39)
266                 if (!strcmpiW(string, keyname)) break;
267             if (!*string) {
268                 RegCloseKey(subkey);
269                 return S_FALSE;
270             }
271         }
272         RegCloseKey(subkey);
273     }
274
275     return S_OK;
276 }
277
278 /**********************************************************************
279  * COMCAT_ICatRegister_QueryInterface
280  */
281 static HRESULT WINAPI COMCAT_ICatRegister_QueryInterface(
282     LPCATREGISTER iface,
283     REFIID riid,
284     LPVOID *ppvObj)
285 {
286     ComCatMgrImpl *This = (ComCatMgrImpl *)iface;
287     TRACE("\n\tIID:\t%s\n",debugstr_guid(riid));
288
289     if (ppvObj == NULL) return E_POINTER;
290
291     if (IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_ICatRegister)) {
292         *ppvObj = iface;
293         IUnknown_AddRef(iface);
294         return S_OK;
295     }
296
297     if (IsEqualGUID(riid, &IID_ICatInformation)) {
298         *ppvObj = &This->infVtbl;
299         IUnknown_AddRef(iface);
300         return S_OK;
301     }
302
303     return E_NOINTERFACE;
304 }
305
306 /**********************************************************************
307  * COMCAT_ICatRegister_AddRef
308  */
309 static ULONG WINAPI COMCAT_ICatRegister_AddRef(LPCATREGISTER iface)
310 {
311     return 2; /* non-heap based object */
312 }
313
314 /**********************************************************************
315  * COMCAT_ICatRegister_Release
316  */
317 static ULONG WINAPI COMCAT_ICatRegister_Release(LPCATREGISTER iface)
318 {
319     return 1; /* non-heap based object */
320 }
321
322 /**********************************************************************
323  * COMCAT_ICatRegister_RegisterCategories
324  */
325 static HRESULT WINAPI COMCAT_ICatRegister_RegisterCategories(
326     LPCATREGISTER iface,
327     ULONG cCategories,
328     CATEGORYINFO *rgci)
329 {
330     HKEY comcat_key;
331     HRESULT res;
332
333     TRACE("\n");
334
335     if (cCategories && rgci == NULL)
336         return E_POINTER;
337
338     /* Create (or open) the component categories key. */
339     res = RegCreateKeyExW(HKEY_CLASSES_ROOT, comcat_keyname, 0, NULL, 0,
340                           KEY_READ | KEY_WRITE, NULL, &comcat_key, NULL);
341     if (res != ERROR_SUCCESS) return E_FAIL;
342
343     for (; cCategories; --cCategories, ++rgci) {
344         static const WCHAR fmt[] = { '%', 'l', 'X', 0 };
345         WCHAR keyname[39];
346         WCHAR valname[9];
347         HKEY cat_key;
348
349         /* Create (or open) the key for this category. */
350         if (!StringFromGUID2(&rgci->catid, keyname, 39)) continue;
351         res = RegCreateKeyExW(comcat_key, keyname, 0, NULL, 0,
352                               KEY_READ | KEY_WRITE, NULL, &cat_key, NULL);
353         if (res != ERROR_SUCCESS) continue;
354
355         /* Set the value for this locale's description. */
356         wsprintfW(valname, fmt, rgci->lcid);
357         RegSetValueExW(cat_key, valname, 0, REG_SZ,
358                        (CONST BYTE*)(rgci->szDescription),
359                        (lstrlenW(rgci->szDescription) + 1) * sizeof(WCHAR));
360
361         RegCloseKey(cat_key);
362     }
363
364     RegCloseKey(comcat_key);
365     return S_OK;
366 }
367
368 /**********************************************************************
369  * COMCAT_ICatRegister_UnRegisterCategories
370  */
371 static HRESULT WINAPI COMCAT_ICatRegister_UnRegisterCategories(
372     LPCATREGISTER iface,
373     ULONG cCategories,
374     CATID *rgcatid)
375 {
376     HKEY comcat_key;
377     HRESULT res;
378
379     TRACE("\n");
380
381     if (cCategories && rgcatid == NULL)
382         return E_POINTER;
383
384     /* Open the component categories key. */
385     res = RegOpenKeyExW(HKEY_CLASSES_ROOT, comcat_keyname, 0,
386                         KEY_READ | KEY_WRITE, &comcat_key);
387     if (res != ERROR_SUCCESS) return E_FAIL;
388
389     for (; cCategories; --cCategories, ++rgcatid) {
390         WCHAR keyname[39];
391
392         /* Delete the key for this category. */
393         if (!StringFromGUID2(rgcatid, keyname, 39)) continue;
394         RegDeleteKeyW(comcat_key, keyname);
395     }
396
397     RegCloseKey(comcat_key);
398     return S_OK;
399 }
400
401 /**********************************************************************
402  * COMCAT_ICatRegister_RegisterClassImplCategories
403  */
404 static HRESULT WINAPI COMCAT_ICatRegister_RegisterClassImplCategories(
405     LPCATREGISTER iface,
406     REFCLSID rclsid,
407     ULONG cCategories,
408     CATID *rgcatid)
409 {
410     TRACE("\n");
411
412     return COMCAT_RegisterClassCategories(
413         rclsid, impl_keyname, cCategories, rgcatid);
414 }
415
416 /**********************************************************************
417  * COMCAT_ICatRegister_UnRegisterClassImplCategories
418  */
419 static HRESULT WINAPI COMCAT_ICatRegister_UnRegisterClassImplCategories(
420     LPCATREGISTER iface,
421     REFCLSID rclsid,
422     ULONG cCategories,
423     CATID *rgcatid)
424 {
425     TRACE("\n");
426
427     return COMCAT_UnRegisterClassCategories(
428         rclsid, impl_keyname, cCategories, rgcatid);
429 }
430
431 /**********************************************************************
432  * COMCAT_ICatRegister_RegisterClassReqCategories
433  */
434 static HRESULT WINAPI COMCAT_ICatRegister_RegisterClassReqCategories(
435     LPCATREGISTER iface,
436     REFCLSID rclsid,
437     ULONG cCategories,
438     CATID *rgcatid)
439 {
440     TRACE("\n");
441
442     return COMCAT_RegisterClassCategories(
443         rclsid, req_keyname, cCategories, rgcatid);
444 }
445
446 /**********************************************************************
447  * COMCAT_ICatRegister_UnRegisterClassReqCategories
448  */
449 static HRESULT WINAPI COMCAT_ICatRegister_UnRegisterClassReqCategories(
450     LPCATREGISTER iface,
451     REFCLSID rclsid,
452     ULONG cCategories,
453     CATID *rgcatid)
454 {
455     TRACE("\n");
456
457     return COMCAT_UnRegisterClassCategories(
458         rclsid, req_keyname, cCategories, rgcatid);
459 }
460
461 /**********************************************************************
462  * COMCAT_ICatInformation_QueryInterface
463  */
464 static HRESULT WINAPI COMCAT_ICatInformation_QueryInterface(
465     LPCATINFORMATION iface,
466     REFIID riid,
467     LPVOID *ppvObj)
468 {
469     ComCatMgrImpl *This = impl_from_ICatInformation( iface );
470     return IUnknown_QueryInterface((LPUNKNOWN)This, riid, ppvObj);
471 }
472
473 /**********************************************************************
474  * COMCAT_ICatInformation_AddRef
475  */
476 static ULONG WINAPI COMCAT_ICatInformation_AddRef(LPCATINFORMATION iface)
477 {
478     ComCatMgrImpl *This = impl_from_ICatInformation( iface );
479     return IUnknown_AddRef((LPUNKNOWN)This);
480 }
481
482 /**********************************************************************
483  * COMCAT_ICatInformation_Release
484  */
485 static ULONG WINAPI COMCAT_ICatInformation_Release(LPCATINFORMATION iface)
486 {
487     ComCatMgrImpl *This = impl_from_ICatInformation( iface );
488     return IUnknown_Release((LPUNKNOWN)This);
489 }
490
491 /**********************************************************************
492  * COMCAT_ICatInformation_EnumCategories
493  */
494 static HRESULT WINAPI COMCAT_ICatInformation_EnumCategories(
495     LPCATINFORMATION iface,
496     LCID lcid,
497     LPENUMCATEGORYINFO *ppenumCatInfo)
498 {
499     TRACE("\n");
500
501     if (ppenumCatInfo == NULL) return E_POINTER;
502
503     *ppenumCatInfo = COMCAT_IEnumCATEGORYINFO_Construct(lcid);
504     if (*ppenumCatInfo == NULL) return E_OUTOFMEMORY;
505     IEnumCATEGORYINFO_AddRef(*ppenumCatInfo);
506     return S_OK;
507 }
508
509 /**********************************************************************
510  * COMCAT_ICatInformation_GetCategoryDesc
511  */
512 static HRESULT WINAPI COMCAT_ICatInformation_GetCategoryDesc(
513     LPCATINFORMATION iface,
514     REFCATID rcatid,
515     LCID lcid,
516     PWCHAR *ppszDesc)
517 {
518     WCHAR keyname[60] = { 'C', 'o', 'm', 'p', 'o', 'n', 'e', 'n',
519                           't', ' ', 'C', 'a', 't', 'e', 'g', 'o',
520                           'r', 'i', 'e', 's', '\\', 0 };
521     HKEY key;
522     HRESULT res;
523
524     TRACE("\n\tCATID:\t%s\n\tLCID:\t%X\n",debugstr_guid(rcatid), lcid);
525
526     if (rcatid == NULL || ppszDesc == NULL) return E_INVALIDARG;
527
528     /* Open the key for this category. */
529     if (!StringFromGUID2(rcatid, keyname + 21, 39)) return E_FAIL;
530     res = RegOpenKeyExW(HKEY_CLASSES_ROOT, keyname, 0, KEY_READ, &key);
531     if (res != ERROR_SUCCESS) return CAT_E_CATIDNOEXIST;
532
533     /* Allocate a sensible amount of memory for the description. */
534     *ppszDesc = CoTaskMemAlloc(128 * sizeof(WCHAR));
535     if (*ppszDesc == NULL) {
536         RegCloseKey(key);
537         return E_OUTOFMEMORY;
538     }
539
540     /* Get the description, and make sure it's null terminated. */
541     res = COMCAT_GetCategoryDesc(key, lcid, *ppszDesc, 128);
542     RegCloseKey(key);
543     if (FAILED(res)) {
544         CoTaskMemFree(*ppszDesc);
545         return res;
546     }
547
548     return S_OK;
549 }
550
551 /**********************************************************************
552  * COMCAT_ICatInformation_EnumClassesOfCategories
553  */
554 static HRESULT WINAPI COMCAT_ICatInformation_EnumClassesOfCategories(
555     LPCATINFORMATION iface,
556     ULONG cImplemented,
557     CATID *rgcatidImpl,
558     ULONG cRequired,
559     CATID *rgcatidReq,
560     LPENUMCLSID *ppenumCLSID)
561 {
562     struct class_categories *categories;
563
564     TRACE("\n");
565
566         if (cImplemented == (ULONG)-1)
567                 cImplemented = 0;
568         if (cRequired == (ULONG)-1)
569                 cRequired = 0;
570
571     if (ppenumCLSID == NULL ||
572         (cImplemented && rgcatidImpl == NULL) ||
573         (cRequired && rgcatidReq == NULL)) return E_POINTER;
574
575     categories = COMCAT_PrepareClassCategories(cImplemented, rgcatidImpl,
576                                                cRequired, rgcatidReq);
577     if (categories == NULL) return E_OUTOFMEMORY;
578     *ppenumCLSID = COMCAT_CLSID_IEnumGUID_Construct(categories);
579     if (*ppenumCLSID == NULL) {
580         HeapFree(GetProcessHeap(), 0, categories);
581         return E_OUTOFMEMORY;
582     }
583     IEnumGUID_AddRef(*ppenumCLSID);
584     return S_OK;
585 }
586
587 /**********************************************************************
588  * COMCAT_ICatInformation_IsClassOfCategories
589  */
590 static HRESULT WINAPI COMCAT_ICatInformation_IsClassOfCategories(
591     LPCATINFORMATION iface,
592     REFCLSID rclsid,
593     ULONG cImplemented,
594     CATID *rgcatidImpl,
595     ULONG cRequired,
596     CATID *rgcatidReq)
597 {
598     WCHAR keyname[45] = { 'C', 'L', 'S', 'I', 'D', '\\', 0 };
599     HRESULT res;
600     struct class_categories *categories;
601     HKEY key;
602
603     if (WINE_TRACE_ON(ole)) {
604         ULONG count;
605         TRACE("\n\tCLSID:\t%s\n\tImplemented %u\n",debugstr_guid(rclsid),cImplemented);
606         for (count = 0; count < cImplemented; ++count)
607             TRACE("\t\t%s\n",debugstr_guid(&rgcatidImpl[count]));
608         TRACE("\tRequired %u\n",cRequired);
609         for (count = 0; count < cRequired; ++count)
610             TRACE("\t\t%s\n",debugstr_guid(&rgcatidReq[count]));
611     }
612
613     if ((cImplemented && rgcatidImpl == NULL) ||
614         (cRequired && rgcatidReq == NULL)) return E_POINTER;
615
616     res = StringFromGUID2(rclsid, keyname + 6, 39);
617     if (FAILED(res)) return res;
618
619     categories = COMCAT_PrepareClassCategories(cImplemented, rgcatidImpl,
620                                                cRequired, rgcatidReq);
621     if (categories == NULL) return E_OUTOFMEMORY;
622
623     res = RegOpenKeyExW(HKEY_CLASSES_ROOT, keyname, 0, KEY_READ, &key);
624     if (res == ERROR_SUCCESS) {
625         res = COMCAT_IsClassOfCategories(key, categories);
626         RegCloseKey(key);
627     } else res = S_FALSE;
628
629     HeapFree(GetProcessHeap(), 0, categories);
630
631     return res;
632 }
633
634 /**********************************************************************
635  * COMCAT_ICatInformation_EnumImplCategoriesOfClass
636  */
637 static HRESULT WINAPI COMCAT_ICatInformation_EnumImplCategoriesOfClass(
638     LPCATINFORMATION iface,
639     REFCLSID rclsid,
640     LPENUMCATID *ppenumCATID)
641 {
642     static const WCHAR postfix[24] = { '\\', 'I', 'm', 'p', 'l', 'e', 'm', 'e',
643                           'n', 't', 'e', 'd', ' ', 'C', 'a', 't',
644                           'e', 'g', 'o', 'r', 'i', 'e', 's', 0 };
645
646     TRACE("\n\tCLSID:\t%s\n",debugstr_guid(rclsid));
647
648     if (rclsid == NULL || ppenumCATID == NULL)
649         return E_POINTER;
650
651     *ppenumCATID = COMCAT_CATID_IEnumGUID_Construct(rclsid, postfix);
652     if (*ppenumCATID == NULL) return E_OUTOFMEMORY;
653     return S_OK;
654 }
655
656 /**********************************************************************
657  * COMCAT_ICatInformation_EnumReqCategoriesOfClass
658  */
659 static HRESULT WINAPI COMCAT_ICatInformation_EnumReqCategoriesOfClass(
660     LPCATINFORMATION iface,
661     REFCLSID rclsid,
662     LPENUMCATID *ppenumCATID)
663 {
664     static const WCHAR postfix[21] = { '\\', 'R', 'e', 'q', 'u', 'i', 'r', 'e',
665                           'd', ' ', 'C', 'a', 't', 'e', 'g', 'o',
666                           'r', 'i', 'e', 's', 0 };
667
668     TRACE("\n\tCLSID:\t%s\n",debugstr_guid(rclsid));
669
670     if (rclsid == NULL || ppenumCATID == NULL)
671         return E_POINTER;
672
673     *ppenumCATID = COMCAT_CATID_IEnumGUID_Construct(rclsid, postfix);
674     if (*ppenumCATID == NULL) return E_OUTOFMEMORY;
675     return S_OK;
676 }
677
678 /**********************************************************************
679  * COMCAT_ICatRegister_Vtbl
680  */
681 static const ICatRegisterVtbl COMCAT_ICatRegister_Vtbl =
682 {
683     COMCAT_ICatRegister_QueryInterface,
684     COMCAT_ICatRegister_AddRef,
685     COMCAT_ICatRegister_Release,
686     COMCAT_ICatRegister_RegisterCategories,
687     COMCAT_ICatRegister_UnRegisterCategories,
688     COMCAT_ICatRegister_RegisterClassImplCategories,
689     COMCAT_ICatRegister_UnRegisterClassImplCategories,
690     COMCAT_ICatRegister_RegisterClassReqCategories,
691     COMCAT_ICatRegister_UnRegisterClassReqCategories
692 };
693
694
695 /**********************************************************************
696  * COMCAT_ICatInformation_Vtbl
697  */
698 static const ICatInformationVtbl COMCAT_ICatInformation_Vtbl =
699 {
700     COMCAT_ICatInformation_QueryInterface,
701     COMCAT_ICatInformation_AddRef,
702     COMCAT_ICatInformation_Release,
703     COMCAT_ICatInformation_EnumCategories,
704     COMCAT_ICatInformation_GetCategoryDesc,
705     COMCAT_ICatInformation_EnumClassesOfCategories,
706     COMCAT_ICatInformation_IsClassOfCategories,
707     COMCAT_ICatInformation_EnumImplCategoriesOfClass,
708     COMCAT_ICatInformation_EnumReqCategoriesOfClass
709 };
710
711 /**********************************************************************
712  * static ComCatMgr instance
713  */
714 static ComCatMgrImpl COMCAT_ComCatMgr =
715 {
716     &COMCAT_ICatRegister_Vtbl,
717     &COMCAT_ICatInformation_Vtbl
718 };
719
720 /**********************************************************************
721  * COMCAT_IClassFactory_QueryInterface (also IUnknown)
722  */
723 static HRESULT WINAPI COMCAT_IClassFactory_QueryInterface(
724     LPCLASSFACTORY iface,
725     REFIID riid,
726     LPVOID *ppvObj)
727 {
728     TRACE("\n\tIID:\t%s\n",debugstr_guid(riid));
729
730     if (ppvObj == NULL) return E_POINTER;
731
732     if (IsEqualGUID(riid, &IID_IUnknown) ||
733         IsEqualGUID(riid, &IID_IClassFactory))
734     {
735         *ppvObj = (LPVOID)iface;
736         IUnknown_AddRef(iface);
737         return S_OK;
738     }
739
740     return E_NOINTERFACE;
741 }
742
743 /**********************************************************************
744  * COMCAT_IClassFactory_AddRef (also IUnknown)
745  */
746 static ULONG WINAPI COMCAT_IClassFactory_AddRef(LPCLASSFACTORY iface)
747 {
748     return 2; /* non-heap based object */
749 }
750
751 /**********************************************************************
752  * COMCAT_IClassFactory_Release (also IUnknown)
753  */
754 static ULONG WINAPI COMCAT_IClassFactory_Release(LPCLASSFACTORY iface)
755 {
756     return 1; /* non-heap based object */
757 }
758
759 /**********************************************************************
760  * COMCAT_IClassFactory_CreateInstance
761  */
762 static HRESULT WINAPI COMCAT_IClassFactory_CreateInstance(
763     LPCLASSFACTORY iface,
764     LPUNKNOWN pUnkOuter,
765     REFIID riid,
766     LPVOID *ppvObj)
767 {
768     HRESULT res;
769     TRACE("\n\tIID:\t%s\n",debugstr_guid(riid));
770
771     if (ppvObj == NULL) return E_POINTER;
772
773     /* Don't support aggregation (Windows doesn't) */
774     if (pUnkOuter != NULL) return CLASS_E_NOAGGREGATION;
775
776     res = IUnknown_QueryInterface((LPUNKNOWN)&COMCAT_ComCatMgr, riid, ppvObj);
777     if (SUCCEEDED(res)) {
778         return res;
779     }
780
781     return CLASS_E_CLASSNOTAVAILABLE;
782 }
783
784 /**********************************************************************
785  * COMCAT_IClassFactory_LockServer
786  */
787 static HRESULT WINAPI COMCAT_IClassFactory_LockServer(
788     LPCLASSFACTORY iface,
789     BOOL fLock)
790 {
791     FIXME("(%d), stub!\n",fLock);
792     return S_OK;
793 }
794
795 /**********************************************************************
796  * static ClassFactory instance
797  */
798 static const IClassFactoryVtbl ComCatCFVtbl =
799 {
800     COMCAT_IClassFactory_QueryInterface,
801     COMCAT_IClassFactory_AddRef,
802     COMCAT_IClassFactory_Release,
803     COMCAT_IClassFactory_CreateInstance,
804     COMCAT_IClassFactory_LockServer
805 };
806
807 static const IClassFactoryVtbl *ComCatCF = &ComCatCFVtbl;
808
809 HRESULT ComCatCF_Create(REFIID riid, LPVOID *ppv)
810 {
811     return IClassFactory_QueryInterface((IClassFactory *)&ComCatCF, riid, ppv);
812 }
813
814 /**********************************************************************
815  * IEnumCATEGORYINFO implementation
816  *
817  * This implementation is not thread-safe.  The manager itself is, but
818  * I can't imagine a valid use of an enumerator in several threads.
819  */
820 typedef struct
821 {
822     const IEnumCATEGORYINFOVtbl *lpVtbl;
823     LONG  ref;
824     LCID  lcid;
825     HKEY  key;
826     DWORD next_index;
827 } IEnumCATEGORYINFOImpl;
828
829 static ULONG WINAPI COMCAT_IEnumCATEGORYINFO_AddRef(LPENUMCATEGORYINFO iface)
830 {
831     IEnumCATEGORYINFOImpl *This = (IEnumCATEGORYINFOImpl *)iface;
832
833     TRACE("\n");
834
835     return InterlockedIncrement(&This->ref);
836 }
837
838 static HRESULT WINAPI COMCAT_IEnumCATEGORYINFO_QueryInterface(
839     LPENUMCATEGORYINFO iface,
840     REFIID riid,
841     LPVOID *ppvObj)
842 {
843     TRACE("\n\tIID:\t%s\n",debugstr_guid(riid));
844
845     if (ppvObj == NULL) return E_POINTER;
846
847     if (IsEqualGUID(riid, &IID_IUnknown) ||
848         IsEqualGUID(riid, &IID_IEnumCATEGORYINFO))
849     {
850         *ppvObj = (LPVOID)iface;
851         COMCAT_IEnumCATEGORYINFO_AddRef(iface);
852         return S_OK;
853     }
854
855     return E_NOINTERFACE;
856 }
857
858 static ULONG WINAPI COMCAT_IEnumCATEGORYINFO_Release(LPENUMCATEGORYINFO iface)
859 {
860     IEnumCATEGORYINFOImpl *This = (IEnumCATEGORYINFOImpl *)iface;
861     ULONG ref;
862
863     TRACE("\n");
864
865     ref = InterlockedDecrement(&This->ref);
866     if (ref == 0) {
867         if (This->key) RegCloseKey(This->key);
868         HeapFree(GetProcessHeap(), 0, This);
869         return 0;
870     }
871     return ref;
872 }
873
874 static HRESULT WINAPI COMCAT_IEnumCATEGORYINFO_Next(
875     LPENUMCATEGORYINFO iface,
876     ULONG celt,
877     CATEGORYINFO *rgelt,
878     ULONG *pceltFetched)
879 {
880     IEnumCATEGORYINFOImpl *This = (IEnumCATEGORYINFOImpl *)iface;
881     ULONG fetched = 0;
882
883     TRACE("\n");
884
885     if (rgelt == NULL) return E_POINTER;
886
887     if (This->key) while (fetched < celt) {
888         LSTATUS res;
889         HRESULT hr;
890         WCHAR catid[39];
891         DWORD cName = 39;
892         HKEY subkey;
893
894         res = RegEnumKeyExW(This->key, This->next_index, catid, &cName,
895                             NULL, NULL, NULL, NULL);
896         if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA) break;
897         ++(This->next_index);
898
899         hr = CLSIDFromString(catid, &rgelt->catid);
900         if (FAILED(hr)) continue;
901
902         res = RegOpenKeyExW(This->key, catid, 0, KEY_READ, &subkey);
903         if (res != ERROR_SUCCESS) continue;
904
905         hr = COMCAT_GetCategoryDesc(subkey, This->lcid,
906                                     rgelt->szDescription, 128);
907         RegCloseKey(subkey);
908         if (FAILED(hr)) continue;
909
910         rgelt->lcid = This->lcid;
911         ++fetched;
912         ++rgelt;
913     }
914
915     if (pceltFetched) *pceltFetched = fetched;
916     return fetched == celt ? S_OK : S_FALSE;
917 }
918
919 static HRESULT WINAPI COMCAT_IEnumCATEGORYINFO_Skip(
920     LPENUMCATEGORYINFO iface,
921     ULONG celt)
922 {
923     IEnumCATEGORYINFOImpl *This = (IEnumCATEGORYINFOImpl *)iface;
924
925     TRACE("\n");
926
927     This->next_index += celt;
928     /* This should return S_FALSE when there aren't celt elems to skip. */
929     return S_OK;
930 }
931
932 static HRESULT WINAPI COMCAT_IEnumCATEGORYINFO_Reset(LPENUMCATEGORYINFO iface)
933 {
934     IEnumCATEGORYINFOImpl *This = (IEnumCATEGORYINFOImpl *)iface;
935
936     TRACE("\n");
937
938     This->next_index = 0;
939     return S_OK;
940 }
941
942 static HRESULT WINAPI COMCAT_IEnumCATEGORYINFO_Clone(
943     LPENUMCATEGORYINFO iface,
944     IEnumCATEGORYINFO **ppenum)
945 {
946     IEnumCATEGORYINFOImpl *This = (IEnumCATEGORYINFOImpl *)iface;
947     static const WCHAR keyname[] = { 'C', 'o', 'm', 'p', 'o', 'n', 'e', 'n',
948                                      't', ' ', 'C', 'a', 't', 'e', 'g', 'o',
949                                      'r', 'i', 'e', 's', 0 };
950     IEnumCATEGORYINFOImpl *new_this;
951
952     TRACE("\n");
953
954     if (ppenum == NULL) return E_POINTER;
955
956     new_this = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IEnumCATEGORYINFOImpl));
957     if (new_this == NULL) return E_OUTOFMEMORY;
958
959     new_this->lpVtbl = This->lpVtbl;
960     new_this->ref = 1;
961     new_this->lcid = This->lcid;
962     /* FIXME: could we more efficiently use DuplicateHandle? */
963     RegOpenKeyExW(HKEY_CLASSES_ROOT, keyname, 0, KEY_READ, &new_this->key);
964     new_this->next_index = This->next_index;
965
966     *ppenum = (LPENUMCATEGORYINFO)new_this;
967     return S_OK;
968 }
969
970 static const IEnumCATEGORYINFOVtbl COMCAT_IEnumCATEGORYINFO_Vtbl =
971 {
972     COMCAT_IEnumCATEGORYINFO_QueryInterface,
973     COMCAT_IEnumCATEGORYINFO_AddRef,
974     COMCAT_IEnumCATEGORYINFO_Release,
975     COMCAT_IEnumCATEGORYINFO_Next,
976     COMCAT_IEnumCATEGORYINFO_Skip,
977     COMCAT_IEnumCATEGORYINFO_Reset,
978     COMCAT_IEnumCATEGORYINFO_Clone
979 };
980
981 static LPENUMCATEGORYINFO COMCAT_IEnumCATEGORYINFO_Construct(LCID lcid)
982 {
983     IEnumCATEGORYINFOImpl *This;
984
985     This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IEnumCATEGORYINFOImpl));
986     if (This) {
987         static const WCHAR keyname[] = { 'C', 'o', 'm', 'p', 'o', 'n', 'e', 'n',
988                                          't', ' ', 'C', 'a', 't', 'e', 'g', 'o',
989                                          'r', 'i', 'e', 's', 0 };
990
991         This->lpVtbl = &COMCAT_IEnumCATEGORYINFO_Vtbl;
992         This->lcid = lcid;
993         RegOpenKeyExW(HKEY_CLASSES_ROOT, keyname, 0, KEY_READ, &This->key);
994     }
995     return (LPENUMCATEGORYINFO)This;
996 }
997
998 /**********************************************************************
999  * ClassesOfCategories IEnumCLSID (IEnumGUID) implementation
1000  *
1001  * This implementation is not thread-safe.  The manager itself is, but
1002  * I can't imagine a valid use of an enumerator in several threads.
1003  */
1004 typedef struct
1005 {
1006     const IEnumGUIDVtbl *lpVtbl;
1007     LONG  ref;
1008     struct class_categories *categories;
1009     HKEY  key;
1010     DWORD next_index;
1011 } CLSID_IEnumGUIDImpl;
1012
1013 static ULONG WINAPI COMCAT_CLSID_IEnumGUID_AddRef(LPENUMGUID iface)
1014 {
1015     CLSID_IEnumGUIDImpl *This = (CLSID_IEnumGUIDImpl *)iface;
1016     TRACE("\n");
1017
1018     return InterlockedIncrement(&This->ref);
1019 }
1020
1021 static HRESULT WINAPI COMCAT_CLSID_IEnumGUID_QueryInterface(
1022     LPENUMGUID iface,
1023     REFIID riid,
1024     LPVOID *ppvObj)
1025 {
1026     TRACE("\n\tIID:\t%s\n",debugstr_guid(riid));
1027
1028     if (ppvObj == NULL) return E_POINTER;
1029
1030     if (IsEqualGUID(riid, &IID_IUnknown) ||
1031         IsEqualGUID(riid, &IID_IEnumGUID))
1032     {
1033         *ppvObj = (LPVOID)iface;
1034         COMCAT_CLSID_IEnumGUID_AddRef(iface);
1035         return S_OK;
1036     }
1037
1038     return E_NOINTERFACE;
1039 }
1040
1041 static ULONG WINAPI COMCAT_CLSID_IEnumGUID_Release(LPENUMGUID iface)
1042 {
1043     CLSID_IEnumGUIDImpl *This = (CLSID_IEnumGUIDImpl *)iface;
1044     ULONG ref;
1045
1046     TRACE("\n");
1047
1048     ref = InterlockedDecrement(&This->ref);
1049     if (ref == 0) {
1050         if (This->key) RegCloseKey(This->key);
1051         HeapFree(GetProcessHeap(), 0, (LPVOID)This->categories);
1052         HeapFree(GetProcessHeap(), 0, This);
1053         return 0;
1054     }
1055     return ref;
1056 }
1057
1058 static HRESULT WINAPI COMCAT_CLSID_IEnumGUID_Next(
1059     LPENUMGUID iface,
1060     ULONG celt,
1061     GUID *rgelt,
1062     ULONG *pceltFetched)
1063 {
1064     CLSID_IEnumGUIDImpl *This = (CLSID_IEnumGUIDImpl *)iface;
1065     ULONG fetched = 0;
1066
1067     TRACE("\n");
1068
1069     if (rgelt == NULL) return E_POINTER;
1070
1071     if (This->key) while (fetched < celt) {
1072         LSTATUS res;
1073         HRESULT hr;
1074         WCHAR clsid[39];
1075         DWORD cName = 39;
1076         HKEY subkey;
1077
1078         res = RegEnumKeyExW(This->key, This->next_index, clsid, &cName,
1079                             NULL, NULL, NULL, NULL);
1080         if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA) break;
1081         ++(This->next_index);
1082
1083         hr = CLSIDFromString(clsid, rgelt);
1084         if (FAILED(hr)) continue;
1085
1086         res = RegOpenKeyExW(This->key, clsid, 0, KEY_READ, &subkey);
1087         if (res != ERROR_SUCCESS) continue;
1088
1089         hr = COMCAT_IsClassOfCategories(subkey, This->categories);
1090         RegCloseKey(subkey);
1091         if (hr != S_OK) continue;
1092
1093         ++fetched;
1094         ++rgelt;
1095     }
1096
1097     if (pceltFetched) *pceltFetched = fetched;
1098     return fetched == celt ? S_OK : S_FALSE;
1099 }
1100
1101 static HRESULT WINAPI COMCAT_CLSID_IEnumGUID_Skip(
1102     LPENUMGUID iface,
1103     ULONG celt)
1104 {
1105     CLSID_IEnumGUIDImpl *This = (CLSID_IEnumGUIDImpl *)iface;
1106
1107     TRACE("\n");
1108
1109     This->next_index += celt;
1110     FIXME("Never returns S_FALSE\n");
1111     return S_OK;
1112 }
1113
1114 static HRESULT WINAPI COMCAT_CLSID_IEnumGUID_Reset(LPENUMGUID iface)
1115 {
1116     CLSID_IEnumGUIDImpl *This = (CLSID_IEnumGUIDImpl *)iface;
1117
1118     TRACE("\n");
1119
1120     This->next_index = 0;
1121     return S_OK;
1122 }
1123
1124 static HRESULT WINAPI COMCAT_CLSID_IEnumGUID_Clone(
1125     LPENUMGUID iface,
1126     IEnumGUID **ppenum)
1127 {
1128     CLSID_IEnumGUIDImpl *This = (CLSID_IEnumGUIDImpl *)iface;
1129     static const WCHAR keyname[] = { 'C', 'L', 'S', 'I', 'D', 0 };
1130     CLSID_IEnumGUIDImpl *new_this;
1131     DWORD size;
1132
1133     TRACE("\n");
1134
1135     if (ppenum == NULL) return E_POINTER;
1136
1137     new_this = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CLSID_IEnumGUIDImpl));
1138     if (new_this == NULL) return E_OUTOFMEMORY;
1139
1140     new_this->lpVtbl = This->lpVtbl;
1141     new_this->ref = 1;
1142     size = HeapSize(GetProcessHeap(), 0, (LPVOID)This->categories);
1143     new_this->categories =
1144         HeapAlloc(GetProcessHeap(), 0, size);
1145     if (new_this->categories == NULL) {
1146         HeapFree(GetProcessHeap(), 0, new_this);
1147         return E_OUTOFMEMORY;
1148     }
1149     memcpy((LPVOID)new_this->categories, This->categories, size);
1150     /* FIXME: could we more efficiently use DuplicateHandle? */
1151     RegOpenKeyExW(HKEY_CLASSES_ROOT, keyname, 0, KEY_READ, &new_this->key);
1152     new_this->next_index = This->next_index;
1153
1154     *ppenum = (LPENUMGUID)new_this;
1155     return S_OK;
1156 }
1157
1158 static const IEnumGUIDVtbl COMCAT_CLSID_IEnumGUID_Vtbl =
1159 {
1160     COMCAT_CLSID_IEnumGUID_QueryInterface,
1161     COMCAT_CLSID_IEnumGUID_AddRef,
1162     COMCAT_CLSID_IEnumGUID_Release,
1163     COMCAT_CLSID_IEnumGUID_Next,
1164     COMCAT_CLSID_IEnumGUID_Skip,
1165     COMCAT_CLSID_IEnumGUID_Reset,
1166     COMCAT_CLSID_IEnumGUID_Clone
1167 };
1168
1169 static LPENUMGUID COMCAT_CLSID_IEnumGUID_Construct(struct class_categories *categories)
1170 {
1171     CLSID_IEnumGUIDImpl *This;
1172
1173     This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CLSID_IEnumGUIDImpl));
1174     if (This) {
1175         static const WCHAR keyname[] = { 'C', 'L', 'S', 'I', 'D', 0 };
1176
1177         This->lpVtbl = &COMCAT_CLSID_IEnumGUID_Vtbl;
1178         This->categories = categories;
1179         RegOpenKeyExW(HKEY_CLASSES_ROOT, keyname, 0, KEY_READ, &This->key);
1180     }
1181     return (LPENUMGUID)This;
1182 }
1183
1184 /**********************************************************************
1185  * CategoriesOfClass IEnumCATID (IEnumGUID) implementation
1186  *
1187  * This implementation is not thread-safe.  The manager itself is, but
1188  * I can't imagine a valid use of an enumerator in several threads.
1189  */
1190 typedef struct
1191 {
1192     const IEnumGUIDVtbl *lpVtbl;
1193     LONG  ref;
1194     WCHAR keyname[68];
1195     HKEY  key;
1196     DWORD next_index;
1197 } CATID_IEnumGUIDImpl;
1198
1199 static ULONG WINAPI COMCAT_CATID_IEnumGUID_AddRef(LPENUMGUID iface)
1200 {
1201     CATID_IEnumGUIDImpl *This = (CATID_IEnumGUIDImpl *)iface;
1202     TRACE("\n");
1203
1204     return InterlockedIncrement(&This->ref);
1205 }
1206
1207 static HRESULT WINAPI COMCAT_CATID_IEnumGUID_QueryInterface(
1208     LPENUMGUID iface,
1209     REFIID riid,
1210     LPVOID *ppvObj)
1211 {
1212     TRACE("\n\tIID:\t%s\n",debugstr_guid(riid));
1213
1214     if (ppvObj == NULL) return E_POINTER;
1215
1216     if (IsEqualGUID(riid, &IID_IUnknown) ||
1217         IsEqualGUID(riid, &IID_IEnumGUID))
1218     {
1219         *ppvObj = (LPVOID)iface;
1220         COMCAT_CATID_IEnumGUID_AddRef(iface);
1221         return S_OK;
1222     }
1223
1224     return E_NOINTERFACE;
1225 }
1226
1227 static ULONG WINAPI COMCAT_CATID_IEnumGUID_Release(LPENUMGUID iface)
1228 {
1229     CATID_IEnumGUIDImpl *This = (CATID_IEnumGUIDImpl *)iface;
1230     ULONG ref;
1231
1232     TRACE("\n");
1233
1234     ref = InterlockedDecrement(&This->ref);
1235     if (ref == 0) {
1236         if (This->key) RegCloseKey(This->key);
1237         HeapFree(GetProcessHeap(), 0, This);
1238         return 0;
1239     }
1240     return ref;
1241 }
1242
1243 static HRESULT WINAPI COMCAT_CATID_IEnumGUID_Next(
1244     LPENUMGUID iface,
1245     ULONG celt,
1246     GUID *rgelt,
1247     ULONG *pceltFetched)
1248 {
1249     CATID_IEnumGUIDImpl *This = (CATID_IEnumGUIDImpl *)iface;
1250     ULONG fetched = 0;
1251
1252     TRACE("\n");
1253
1254     if (rgelt == NULL) return E_POINTER;
1255
1256     if (This->key) while (fetched < celt) {
1257         LSTATUS res;
1258         HRESULT hr;
1259         WCHAR catid[39];
1260         DWORD cName = 39;
1261
1262         res = RegEnumKeyExW(This->key, This->next_index, catid, &cName,
1263                             NULL, NULL, NULL, NULL);
1264         if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA) break;
1265         ++(This->next_index);
1266
1267         hr = CLSIDFromString(catid, rgelt);
1268         if (FAILED(hr)) continue;
1269
1270         ++fetched;
1271         ++rgelt;
1272     }
1273
1274     if (pceltFetched) *pceltFetched = fetched;
1275     return fetched == celt ? S_OK : S_FALSE;
1276 }
1277
1278 static HRESULT WINAPI COMCAT_CATID_IEnumGUID_Skip(
1279     LPENUMGUID iface,
1280     ULONG celt)
1281 {
1282     CATID_IEnumGUIDImpl *This = (CATID_IEnumGUIDImpl *)iface;
1283
1284     TRACE("\n");
1285
1286     This->next_index += celt;
1287     FIXME("Never returns S_FALSE\n");
1288     return S_OK;
1289 }
1290
1291 static HRESULT WINAPI COMCAT_CATID_IEnumGUID_Reset(LPENUMGUID iface)
1292 {
1293     CATID_IEnumGUIDImpl *This = (CATID_IEnumGUIDImpl *)iface;
1294
1295     TRACE("\n");
1296
1297     This->next_index = 0;
1298     return S_OK;
1299 }
1300
1301 static HRESULT WINAPI COMCAT_CATID_IEnumGUID_Clone(
1302     LPENUMGUID iface,
1303     IEnumGUID **ppenum)
1304 {
1305     CATID_IEnumGUIDImpl *This = (CATID_IEnumGUIDImpl *)iface;
1306     CATID_IEnumGUIDImpl *new_this;
1307
1308     TRACE("\n");
1309
1310     if (ppenum == NULL) return E_POINTER;
1311
1312     new_this = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CATID_IEnumGUIDImpl));
1313     if (new_this == NULL) return E_OUTOFMEMORY;
1314
1315     new_this->lpVtbl = This->lpVtbl;
1316     new_this->ref = 1;
1317     lstrcpyW(new_this->keyname, This->keyname);
1318     /* FIXME: could we more efficiently use DuplicateHandle? */
1319     RegOpenKeyExW(HKEY_CLASSES_ROOT, new_this->keyname, 0, KEY_READ, &new_this->key);
1320     new_this->next_index = This->next_index;
1321
1322     *ppenum = (LPENUMGUID)new_this;
1323     return S_OK;
1324 }
1325
1326 static const IEnumGUIDVtbl COMCAT_CATID_IEnumGUID_Vtbl =
1327 {
1328     COMCAT_CATID_IEnumGUID_QueryInterface,
1329     COMCAT_CATID_IEnumGUID_AddRef,
1330     COMCAT_CATID_IEnumGUID_Release,
1331     COMCAT_CATID_IEnumGUID_Next,
1332     COMCAT_CATID_IEnumGUID_Skip,
1333     COMCAT_CATID_IEnumGUID_Reset,
1334     COMCAT_CATID_IEnumGUID_Clone
1335 };
1336
1337 static LPENUMGUID COMCAT_CATID_IEnumGUID_Construct(
1338     REFCLSID rclsid, LPCWSTR postfix)
1339 {
1340     CATID_IEnumGUIDImpl *This;
1341
1342     This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CATID_IEnumGUIDImpl));
1343     if (This) {
1344         WCHAR prefix[6] = { 'C', 'L', 'S', 'I', 'D', '\\' };
1345
1346         This->lpVtbl = &COMCAT_CATID_IEnumGUID_Vtbl;
1347         memcpy(This->keyname, prefix, sizeof(prefix));
1348         StringFromGUID2(rclsid, This->keyname + 6, 39);
1349         lstrcpyW(This->keyname + 44, postfix);
1350         RegOpenKeyExW(HKEY_CLASSES_ROOT, This->keyname, 0, KEY_READ, &This->key);
1351     }
1352     return (LPENUMGUID)This;
1353 }