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