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