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