comcat: Simplify the class factory implementation and make it more similar to the...
[wine] / dlls / comcat / information.c
1 /*
2  *      ComCatMgr ICatInformation implementation for comcat.dll
3  *
4  * Copyright (C) 2002 John K. Hohm
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include <string.h>
22 #include "comcat_private.h"
23
24 #include "wine/debug.h"
25
26 WINE_DEFAULT_DEBUG_CHANNEL(ole);
27
28 static LPENUMCATEGORYINFO COMCAT_IEnumCATEGORYINFO_Construct(LCID lcid);
29 static HRESULT COMCAT_GetCategoryDesc(HKEY key, LCID lcid, PWCHAR pszDesc,
30                                       ULONG buf_wchars);
31
32 struct class_categories {
33     LPCWSTR impl_strings;
34     LPCWSTR req_strings;
35 };
36
37 static struct class_categories *COMCAT_PrepareClassCategories(
38     ULONG impl_count, const CATID *impl_catids, ULONG req_count, const CATID *req_catids);
39 static HRESULT COMCAT_IsClassOfCategories(
40     HKEY key, struct class_categories const* class_categories);
41 static LPENUMGUID COMCAT_CLSID_IEnumGUID_Construct(
42     struct class_categories *class_categories);
43 static LPENUMGUID COMCAT_CATID_IEnumGUID_Construct(
44     REFCLSID rclsid, LPCWSTR impl_req);
45
46 /**********************************************************************
47  * COMCAT_ICatInformation_QueryInterface
48  */
49 static HRESULT WINAPI COMCAT_ICatInformation_QueryInterface(
50     LPCATINFORMATION iface,
51     REFIID riid,
52     LPVOID *ppvObj)
53 {
54     ICOM_THIS_MULTI(ComCatMgrImpl, infVtbl, iface);
55     TRACE("\n\tIID:\t%s\n",debugstr_guid(riid));
56
57     if (ppvObj == NULL) return E_POINTER;
58
59     return IUnknown_QueryInterface((LPUNKNOWN)&This->unkVtbl, riid, ppvObj);
60 }
61
62 /**********************************************************************
63  * COMCAT_ICatInformation_AddRef
64  */
65 static ULONG WINAPI COMCAT_ICatInformation_AddRef(LPCATINFORMATION iface)
66 {
67     ICOM_THIS_MULTI(ComCatMgrImpl, infVtbl, iface);
68     TRACE("\n");
69
70     return IUnknown_AddRef((LPUNKNOWN)&This->unkVtbl);
71 }
72
73 /**********************************************************************
74  * COMCAT_ICatInformation_Release
75  */
76 static ULONG WINAPI COMCAT_ICatInformation_Release(LPCATINFORMATION iface)
77 {
78     ICOM_THIS_MULTI(ComCatMgrImpl, infVtbl, iface);
79     TRACE("\n");
80
81     return IUnknown_Release((LPUNKNOWN)&This->unkVtbl);
82 }
83
84 /**********************************************************************
85  * COMCAT_ICatInformation_EnumCategories
86  */
87 static HRESULT WINAPI COMCAT_ICatInformation_EnumCategories(
88     LPCATINFORMATION iface,
89     LCID lcid,
90     LPENUMCATEGORYINFO *ppenumCatInfo)
91 {
92 /*     ICOM_THIS_MULTI(ComCatMgrImpl, infVtbl, iface); */
93     TRACE("\n");
94
95     if (ppenumCatInfo == NULL) return E_POINTER;
96
97     *ppenumCatInfo = COMCAT_IEnumCATEGORYINFO_Construct(lcid);
98     if (*ppenumCatInfo == NULL) return E_OUTOFMEMORY;
99     IEnumCATEGORYINFO_AddRef(*ppenumCatInfo);
100     return S_OK;
101 }
102
103 /**********************************************************************
104  * COMCAT_ICatInformation_GetCategoryDesc
105  */
106 static HRESULT WINAPI COMCAT_ICatInformation_GetCategoryDesc(
107     LPCATINFORMATION iface,
108     REFCATID rcatid,
109     LCID lcid,
110     PWCHAR *ppszDesc)
111 {
112 /*     ICOM_THIS_MULTI(ComCatMgrImpl, infVtbl, iface); */
113     WCHAR keyname[60] = { 'C', 'o', 'm', 'p', 'o', 'n', 'e', 'n',
114                           't', ' ', 'C', 'a', 't', 'e', 'g', 'o',
115                           'r', 'i', 'e', 's', '\\', 0 };
116     HKEY key;
117     HRESULT res;
118
119     TRACE("\n\tCATID:\t%s\n\tLCID:\t%X\n",debugstr_guid(rcatid), lcid);
120
121     if (rcatid == NULL || ppszDesc == NULL) return E_INVALIDARG;
122
123     /* Open the key for this category. */
124     if (!StringFromGUID2(rcatid, keyname + 21, 39)) return E_FAIL;
125     res = RegOpenKeyExW(HKEY_CLASSES_ROOT, keyname, 0, KEY_READ, &key);
126     if (res != ERROR_SUCCESS) return CAT_E_CATIDNOEXIST;
127
128     /* Allocate a sensible amount of memory for the description. */
129     *ppszDesc = (PWCHAR) CoTaskMemAlloc(128 * sizeof(WCHAR));
130     if (*ppszDesc == NULL) {
131         RegCloseKey(key);
132         return E_OUTOFMEMORY;
133     }
134
135     /* Get the description, and make sure it's null terminated. */
136     res = COMCAT_GetCategoryDesc(key, lcid, *ppszDesc, 128);
137     RegCloseKey(key);
138     if (FAILED(res)) {
139         CoTaskMemFree(*ppszDesc);
140         return res;
141     }
142
143     return S_OK;
144 }
145
146 /**********************************************************************
147  * COMCAT_ICatInformation_EnumClassesOfCategories
148  */
149 static HRESULT WINAPI COMCAT_ICatInformation_EnumClassesOfCategories(
150     LPCATINFORMATION iface,
151     ULONG cImplemented,
152     CATID *rgcatidImpl,
153     ULONG cRequired,
154     CATID *rgcatidReq,
155     LPENUMCLSID *ppenumCLSID)
156 {
157 /*     ICOM_THIS_MULTI(ComCatMgrImpl, infVtbl, iface); */
158     struct class_categories *categories;
159
160     TRACE("\n");
161
162         if (cImplemented == (ULONG)-1) 
163                 cImplemented = 0;
164         if (cRequired == (ULONG)-1) 
165                 cRequired = 0;
166         
167     if (ppenumCLSID == NULL ||
168         (cImplemented && rgcatidImpl == NULL) ||
169         (cRequired && rgcatidReq == NULL)) return E_POINTER;
170
171     categories = COMCAT_PrepareClassCategories(cImplemented, rgcatidImpl,
172                                                cRequired, rgcatidReq);
173     if (categories == NULL) return E_OUTOFMEMORY;
174     *ppenumCLSID = COMCAT_CLSID_IEnumGUID_Construct(categories);
175     if (*ppenumCLSID == NULL) {
176         HeapFree(GetProcessHeap(), 0, categories);
177         return E_OUTOFMEMORY;
178     }
179     IEnumGUID_AddRef(*ppenumCLSID);
180     return S_OK;
181 }
182
183 /**********************************************************************
184  * COMCAT_ICatInformation_IsClassOfCategories
185  */
186 static HRESULT WINAPI COMCAT_ICatInformation_IsClassOfCategories(
187     LPCATINFORMATION iface,
188     REFCLSID rclsid,
189     ULONG cImplemented,
190     CATID *rgcatidImpl,
191     ULONG cRequired,
192     CATID *rgcatidReq)
193 {
194 /*     ICOM_THIS_MULTI(ComCatMgrImpl, infVtbl, iface); */
195     WCHAR keyname[45] = { 'C', 'L', 'S', 'I', 'D', '\\', 0 };
196     HRESULT res;
197     struct class_categories *categories;
198     HKEY key;
199
200     if (WINE_TRACE_ON(ole)) {
201         ULONG count;
202         TRACE("\n\tCLSID:\t%s\n\tImplemented %u\n",debugstr_guid(rclsid),cImplemented);
203         for (count = 0; count < cImplemented; ++count)
204             TRACE("\t\t%s\n",debugstr_guid(&rgcatidImpl[count]));
205         TRACE("\tRequired %u\n",cRequired);
206         for (count = 0; count < cRequired; ++count)
207             TRACE("\t\t%s\n",debugstr_guid(&rgcatidReq[count]));
208     }
209
210     if ((cImplemented && rgcatidImpl == NULL) ||
211         (cRequired && rgcatidReq == NULL)) return E_POINTER;
212
213     res = StringFromGUID2(rclsid, keyname + 6, 39);
214     if (FAILED(res)) return res;
215
216     categories = COMCAT_PrepareClassCategories(cImplemented, rgcatidImpl,
217                                                cRequired, rgcatidReq);
218     if (categories == NULL) return E_OUTOFMEMORY;
219
220     res = RegOpenKeyExW(HKEY_CLASSES_ROOT, keyname, 0, KEY_READ, &key);
221     if (res == ERROR_SUCCESS) {
222         res = COMCAT_IsClassOfCategories(key, categories);
223         RegCloseKey(key);
224     } else res = S_FALSE;
225
226     HeapFree(GetProcessHeap(), 0, categories);
227
228     return res;
229 }
230
231 /**********************************************************************
232  * COMCAT_ICatInformation_EnumImplCategoriesOfClass
233  */
234 static HRESULT WINAPI COMCAT_ICatInformation_EnumImplCategoriesOfClass(
235     LPCATINFORMATION iface,
236     REFCLSID rclsid,
237     LPENUMCATID *ppenumCATID)
238 {
239 /*     ICOM_THIS_MULTI(ComCatMgrImpl, infVtbl, iface); */
240     static const WCHAR postfix[24] = { '\\', 'I', 'm', 'p', 'l', 'e', 'm', 'e',
241                           'n', 't', 'e', 'd', ' ', 'C', 'a', 't',
242                           'e', 'g', 'o', 'r', 'i', 'e', 's', 0 };
243
244     TRACE("\n\tCLSID:\t%s\n",debugstr_guid(rclsid));
245
246     if (rclsid == NULL || ppenumCATID == NULL)
247         return E_POINTER;
248
249     *ppenumCATID = COMCAT_CATID_IEnumGUID_Construct(rclsid, postfix);
250     if (*ppenumCATID == NULL) return E_OUTOFMEMORY;
251     return S_OK;
252 }
253
254 /**********************************************************************
255  * COMCAT_ICatInformation_EnumReqCategoriesOfClass
256  */
257 static HRESULT WINAPI COMCAT_ICatInformation_EnumReqCategoriesOfClass(
258     LPCATINFORMATION iface,
259     REFCLSID rclsid,
260     LPENUMCATID *ppenumCATID)
261 {
262 /*     ICOM_THIS_MULTI(ComCatMgrImpl, infVtbl, iface); */
263     static const WCHAR postfix[21] = { '\\', 'R', 'e', 'q', 'u', 'i', 'r', 'e',
264                           'd', ' ', 'C', 'a', 't', 'e', 'g', 'o',
265                           'r', 'i', 'e', 's', 0 };
266
267     TRACE("\n\tCLSID:\t%s\n",debugstr_guid(rclsid));
268
269     if (rclsid == NULL || ppenumCATID == NULL)
270         return E_POINTER;
271
272     *ppenumCATID = COMCAT_CATID_IEnumGUID_Construct(rclsid, postfix);
273     if (*ppenumCATID == NULL) return E_OUTOFMEMORY;
274     return S_OK;
275 }
276
277 /**********************************************************************
278  * COMCAT_ICatInformation_Vtbl
279  */
280 const ICatInformationVtbl COMCAT_ICatInformation_Vtbl =
281 {
282     COMCAT_ICatInformation_QueryInterface,
283     COMCAT_ICatInformation_AddRef,
284     COMCAT_ICatInformation_Release,
285     COMCAT_ICatInformation_EnumCategories,
286     COMCAT_ICatInformation_GetCategoryDesc,
287     COMCAT_ICatInformation_EnumClassesOfCategories,
288     COMCAT_ICatInformation_IsClassOfCategories,
289     COMCAT_ICatInformation_EnumImplCategoriesOfClass,
290     COMCAT_ICatInformation_EnumReqCategoriesOfClass
291 };
292
293 /**********************************************************************
294  * IEnumCATEGORYINFO implementation
295  *
296  * This implementation is not thread-safe.  The manager itself is, but
297  * I can't imagine a valid use of an enumerator in several threads.
298  */
299 typedef struct
300 {
301     const IEnumCATEGORYINFOVtbl *lpVtbl;
302     LONG  ref;
303     LCID  lcid;
304     HKEY  key;
305     DWORD next_index;
306 } IEnumCATEGORYINFOImpl;
307
308 static ULONG WINAPI COMCAT_IEnumCATEGORYINFO_AddRef(LPENUMCATEGORYINFO iface)
309 {
310     IEnumCATEGORYINFOImpl *This = (IEnumCATEGORYINFOImpl *)iface;
311
312     TRACE("\n");
313
314     return InterlockedIncrement(&This->ref);
315 }
316
317 static HRESULT WINAPI COMCAT_IEnumCATEGORYINFO_QueryInterface(
318     LPENUMCATEGORYINFO iface,
319     REFIID riid,
320     LPVOID *ppvObj)
321 {
322     TRACE("\n\tIID:\t%s\n",debugstr_guid(riid));
323
324     if (ppvObj == NULL) return E_POINTER;
325
326     if (IsEqualGUID(riid, &IID_IUnknown) ||
327         IsEqualGUID(riid, &IID_IEnumCATEGORYINFO))
328     {
329         *ppvObj = (LPVOID)iface;
330         COMCAT_IEnumCATEGORYINFO_AddRef(iface);
331         return S_OK;
332     }
333
334     return E_NOINTERFACE;
335 }
336
337 static ULONG WINAPI COMCAT_IEnumCATEGORYINFO_Release(LPENUMCATEGORYINFO iface)
338 {
339     IEnumCATEGORYINFOImpl *This = (IEnumCATEGORYINFOImpl *)iface;
340     ULONG ref;
341
342     TRACE("\n");
343
344     ref = InterlockedDecrement(&This->ref);
345     if (ref == 0) {
346         if (This->key) RegCloseKey(This->key);
347         HeapFree(GetProcessHeap(), 0, This);
348         return 0;
349     }
350     return ref;
351 }
352
353 static HRESULT WINAPI COMCAT_IEnumCATEGORYINFO_Next(
354     LPENUMCATEGORYINFO iface,
355     ULONG celt,
356     CATEGORYINFO *rgelt,
357     ULONG *pceltFetched)
358 {
359     IEnumCATEGORYINFOImpl *This = (IEnumCATEGORYINFOImpl *)iface;
360     ULONG fetched = 0;
361
362     TRACE("\n");
363
364     if (rgelt == NULL) return E_POINTER;
365
366     if (This->key) while (fetched < celt) {
367         LSTATUS res;
368         HRESULT hr;
369         WCHAR catid[39];
370         DWORD cName = 39;
371         HKEY subkey;
372
373         res = RegEnumKeyExW(This->key, This->next_index, catid, &cName,
374                             NULL, NULL, NULL, NULL);
375         if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA) break;
376         ++(This->next_index);
377
378         hr = CLSIDFromString(catid, &rgelt->catid);
379         if (FAILED(hr)) continue;
380
381         res = RegOpenKeyExW(This->key, catid, 0, KEY_READ, &subkey);
382         if (res != ERROR_SUCCESS) continue;
383
384         hr = COMCAT_GetCategoryDesc(subkey, This->lcid,
385                                     rgelt->szDescription, 128);
386         RegCloseKey(subkey);
387         if (FAILED(hr)) continue;
388
389         rgelt->lcid = This->lcid;
390         ++fetched;
391         ++rgelt;
392     }
393
394     if (pceltFetched) *pceltFetched = fetched;
395     return fetched == celt ? S_OK : S_FALSE;
396 }
397
398 static HRESULT WINAPI COMCAT_IEnumCATEGORYINFO_Skip(
399     LPENUMCATEGORYINFO iface,
400     ULONG celt)
401 {
402     IEnumCATEGORYINFOImpl *This = (IEnumCATEGORYINFOImpl *)iface;
403
404     TRACE("\n");
405
406     This->next_index += celt;
407     /* This should return S_FALSE when there aren't celt elems to skip. */
408     return S_OK;
409 }
410
411 static HRESULT WINAPI COMCAT_IEnumCATEGORYINFO_Reset(LPENUMCATEGORYINFO iface)
412 {
413     IEnumCATEGORYINFOImpl *This = (IEnumCATEGORYINFOImpl *)iface;
414
415     TRACE("\n");
416
417     This->next_index = 0;
418     return S_OK;
419 }
420
421 static HRESULT WINAPI COMCAT_IEnumCATEGORYINFO_Clone(
422     LPENUMCATEGORYINFO iface,
423     IEnumCATEGORYINFO **ppenum)
424 {
425     IEnumCATEGORYINFOImpl *This = (IEnumCATEGORYINFOImpl *)iface;
426     static const WCHAR keyname[] = { 'C', 'o', 'm', 'p', 'o', 'n', 'e', 'n',
427                                      't', ' ', 'C', 'a', 't', 'e', 'g', 'o',
428                                      'r', 'i', 'e', 's', 0 };
429     IEnumCATEGORYINFOImpl *new_this;
430
431     TRACE("\n");
432
433     if (ppenum == NULL) return E_POINTER;
434
435     new_this = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IEnumCATEGORYINFOImpl));
436     if (new_this == NULL) return E_OUTOFMEMORY;
437
438     new_this->lpVtbl = This->lpVtbl;
439     new_this->ref = 1;
440     new_this->lcid = This->lcid;
441     /* FIXME: could we more efficiently use DuplicateHandle? */
442     RegOpenKeyExW(HKEY_CLASSES_ROOT, keyname, 0, KEY_READ, &new_this->key);
443     new_this->next_index = This->next_index;
444
445     *ppenum = (LPENUMCATEGORYINFO)new_this;
446     return S_OK;
447 }
448
449 static const IEnumCATEGORYINFOVtbl COMCAT_IEnumCATEGORYINFO_Vtbl =
450 {
451     COMCAT_IEnumCATEGORYINFO_QueryInterface,
452     COMCAT_IEnumCATEGORYINFO_AddRef,
453     COMCAT_IEnumCATEGORYINFO_Release,
454     COMCAT_IEnumCATEGORYINFO_Next,
455     COMCAT_IEnumCATEGORYINFO_Skip,
456     COMCAT_IEnumCATEGORYINFO_Reset,
457     COMCAT_IEnumCATEGORYINFO_Clone
458 };
459
460 static LPENUMCATEGORYINFO COMCAT_IEnumCATEGORYINFO_Construct(LCID lcid)
461 {
462     IEnumCATEGORYINFOImpl *This;
463
464     This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IEnumCATEGORYINFOImpl));
465     if (This) {
466         static const WCHAR keyname[] = { 'C', 'o', 'm', 'p', 'o', 'n', 'e', 'n',
467                                          't', ' ', 'C', 'a', 't', 'e', 'g', 'o',
468                                          'r', 'i', 'e', 's', 0 };
469
470         This->lpVtbl = &COMCAT_IEnumCATEGORYINFO_Vtbl;
471         This->lcid = lcid;
472         RegOpenKeyExW(HKEY_CLASSES_ROOT, keyname, 0, KEY_READ, &This->key);
473     }
474     return (LPENUMCATEGORYINFO)This;
475 }
476
477 /**********************************************************************
478  * COMCAT_GetCategoryDesc
479  */
480 static HRESULT COMCAT_GetCategoryDesc(HKEY key, LCID lcid, PWCHAR pszDesc,
481                                       ULONG buf_wchars)
482 {
483     static const WCHAR fmt[] = { '%', 'l', 'X', 0 };
484     WCHAR valname[5];
485     HRESULT res;
486     DWORD type, size = (buf_wchars - 1) * sizeof(WCHAR);
487
488     if (pszDesc == NULL) return E_INVALIDARG;
489
490     /* FIXME: lcid comparisons are more complex than this! */
491     wsprintfW(valname, fmt, lcid);
492     res = RegQueryValueExW(key, valname, 0, &type, (LPBYTE)pszDesc, &size);
493     if (res != ERROR_SUCCESS || type != REG_SZ) {
494         FIXME("Simplified lcid comparison\n");
495         return CAT_E_NODESCRIPTION;
496     }
497     pszDesc[size / sizeof(WCHAR)] = (WCHAR)0;
498
499     return S_OK;
500 }
501
502 /**********************************************************************
503  * COMCAT_PrepareClassCategories
504  */
505 static struct class_categories *COMCAT_PrepareClassCategories(
506     ULONG impl_count, const CATID *impl_catids, ULONG req_count, const CATID *req_catids)
507 {
508     struct class_categories *categories;
509     WCHAR *strings;
510
511     categories = HeapAlloc(
512         GetProcessHeap(), HEAP_ZERO_MEMORY,
513         sizeof(struct class_categories) +
514         ((impl_count + req_count) * 39 + 2) * sizeof(WCHAR));
515     if (categories == NULL) return categories;
516
517     strings = (WCHAR *)(categories + 1);
518     categories->impl_strings = strings;
519     while (impl_count--) {
520         StringFromGUID2(impl_catids++, strings, 39);
521         strings += 39;
522     }
523     *strings++ = 0;
524
525     categories->req_strings = strings;
526     while (req_count--) {
527         StringFromGUID2(req_catids++, strings, 39);
528         strings += 39;
529     }
530     *strings++ = 0;
531
532     return categories;
533 }
534
535 /**********************************************************************
536  * COMCAT_IsClassOfCategories
537  */
538 static HRESULT COMCAT_IsClassOfCategories(
539     HKEY key,
540     struct class_categories const* categories)
541 {
542     static const WCHAR impl_keyname[] = { 'I', 'm', 'p', 'l', 'e', 'm', 'e', 'n',
543                                           't', 'e', 'd', ' ', 'C', 'a', 't', 'e',
544                                           'g', 'o', 'r', 'i', 'e', 's', 0 };
545     static const WCHAR req_keyname[]  = { 'R', 'e', 'q', 'u', 'i', 'r', 'e', 'd',
546                                           ' ', 'C', 'a', 't', 'e', 'g', 'o', 'r',
547                                           'i', 'e', 's', 0 };
548     HKEY subkey;
549     HRESULT res;
550     DWORD index;
551     LPCWSTR string;
552
553     /* Check that every given category is implemented by class. */
554     res = RegOpenKeyExW(key, impl_keyname, 0, KEY_READ, &subkey);
555     if (res != ERROR_SUCCESS) return S_FALSE;
556     for (string = categories->impl_strings; *string; string += 39) {
557         HKEY catkey;
558         res = RegOpenKeyExW(subkey, string, 0, 0, &catkey);
559         if (res != ERROR_SUCCESS) {
560             RegCloseKey(subkey);
561             return S_FALSE;
562         }
563         RegCloseKey(catkey);
564     }
565     RegCloseKey(subkey);
566
567     /* Check that all categories required by class are given. */
568     res = RegOpenKeyExW(key, req_keyname, 0, KEY_READ, &subkey);
569     if (res == ERROR_SUCCESS) {
570         for (index = 0; ; ++index) {
571             WCHAR keyname[39];
572             DWORD size = 39;
573
574             res = RegEnumKeyExW(subkey, index, keyname, &size,
575                                 NULL, NULL, NULL, NULL);
576             if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA) break;
577             if (size != 38) continue; /* bogus catid in registry */
578             for (string = categories->req_strings; *string; string += 39)
579                 if (!strcmpiW(string, keyname)) break;
580             if (!*string) {
581                 RegCloseKey(subkey);
582                 return S_FALSE;
583             }
584         }
585         RegCloseKey(subkey);
586     }
587
588     return S_OK;
589 }
590
591 /**********************************************************************
592  * ClassesOfCategories IEnumCLSID (IEnumGUID) implementation
593  *
594  * This implementation is not thread-safe.  The manager itself is, but
595  * I can't imagine a valid use of an enumerator in several threads.
596  */
597 typedef struct
598 {
599     const IEnumGUIDVtbl *lpVtbl;
600     LONG  ref;
601     struct class_categories *categories;
602     HKEY  key;
603     DWORD next_index;
604 } CLSID_IEnumGUIDImpl;
605
606 static ULONG WINAPI COMCAT_CLSID_IEnumGUID_AddRef(LPENUMGUID iface)
607 {
608     CLSID_IEnumGUIDImpl *This = (CLSID_IEnumGUIDImpl *)iface;
609     TRACE("\n");
610
611     return InterlockedIncrement(&This->ref);
612 }
613
614 static HRESULT WINAPI COMCAT_CLSID_IEnumGUID_QueryInterface(
615     LPENUMGUID iface,
616     REFIID riid,
617     LPVOID *ppvObj)
618 {
619     TRACE("\n\tIID:\t%s\n",debugstr_guid(riid));
620
621     if (ppvObj == NULL) return E_POINTER;
622
623     if (IsEqualGUID(riid, &IID_IUnknown) ||
624         IsEqualGUID(riid, &IID_IEnumGUID))
625     {
626         *ppvObj = (LPVOID)iface;
627         COMCAT_CLSID_IEnumGUID_AddRef(iface);
628         return S_OK;
629     }
630
631     return E_NOINTERFACE;
632 }
633
634 static ULONG WINAPI COMCAT_CLSID_IEnumGUID_Release(LPENUMGUID iface)
635 {
636     CLSID_IEnumGUIDImpl *This = (CLSID_IEnumGUIDImpl *)iface;
637     ULONG ref;
638
639     TRACE("\n");
640
641     ref = InterlockedDecrement(&This->ref);
642     if (ref == 0) {
643         if (This->key) RegCloseKey(This->key);
644         HeapFree(GetProcessHeap(), 0, (LPVOID)This->categories);
645         HeapFree(GetProcessHeap(), 0, This);
646         return 0;
647     }
648     return ref;
649 }
650
651 static HRESULT WINAPI COMCAT_CLSID_IEnumGUID_Next(
652     LPENUMGUID iface,
653     ULONG celt,
654     GUID *rgelt,
655     ULONG *pceltFetched)
656 {
657     CLSID_IEnumGUIDImpl *This = (CLSID_IEnumGUIDImpl *)iface;
658     ULONG fetched = 0;
659
660     TRACE("\n");
661
662     if (rgelt == NULL) return E_POINTER;
663
664     if (This->key) while (fetched < celt) {
665         LSTATUS res;
666         HRESULT hr;
667         WCHAR clsid[39];
668         DWORD cName = 39;
669         HKEY subkey;
670
671         res = RegEnumKeyExW(This->key, This->next_index, clsid, &cName,
672                             NULL, NULL, NULL, NULL);
673         if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA) break;
674         ++(This->next_index);
675
676         hr = CLSIDFromString(clsid, rgelt);
677         if (FAILED(hr)) continue;
678
679         res = RegOpenKeyExW(This->key, clsid, 0, KEY_READ, &subkey);
680         if (res != ERROR_SUCCESS) continue;
681
682         hr = COMCAT_IsClassOfCategories(subkey, This->categories);
683         RegCloseKey(subkey);
684         if (hr != S_OK) continue;
685
686         ++fetched;
687         ++rgelt;
688     }
689
690     if (pceltFetched) *pceltFetched = fetched;
691     return fetched == celt ? S_OK : S_FALSE;
692 }
693
694 static HRESULT WINAPI COMCAT_CLSID_IEnumGUID_Skip(
695     LPENUMGUID iface,
696     ULONG celt)
697 {
698     CLSID_IEnumGUIDImpl *This = (CLSID_IEnumGUIDImpl *)iface;
699
700     TRACE("\n");
701
702     This->next_index += celt;
703     FIXME("Never returns S_FALSE\n");
704     return S_OK;
705 }
706
707 static HRESULT WINAPI COMCAT_CLSID_IEnumGUID_Reset(LPENUMGUID iface)
708 {
709     CLSID_IEnumGUIDImpl *This = (CLSID_IEnumGUIDImpl *)iface;
710
711     TRACE("\n");
712
713     This->next_index = 0;
714     return S_OK;
715 }
716
717 static HRESULT WINAPI COMCAT_CLSID_IEnumGUID_Clone(
718     LPENUMGUID iface,
719     IEnumGUID **ppenum)
720 {
721     CLSID_IEnumGUIDImpl *This = (CLSID_IEnumGUIDImpl *)iface;
722     static const WCHAR keyname[] = { 'C', 'L', 'S', 'I', 'D', 0 };
723     CLSID_IEnumGUIDImpl *new_this;
724     DWORD size;
725
726     TRACE("\n");
727
728     if (ppenum == NULL) return E_POINTER;
729
730     new_this = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CLSID_IEnumGUIDImpl));
731     if (new_this == NULL) return E_OUTOFMEMORY;
732
733     new_this->lpVtbl = This->lpVtbl;
734     new_this->ref = 1;
735     size = HeapSize(GetProcessHeap(), 0, (LPVOID)This->categories);
736     new_this->categories =
737         HeapAlloc(GetProcessHeap(), 0, size);
738     if (new_this->categories == NULL) {
739         HeapFree(GetProcessHeap(), 0, new_this);
740         return E_OUTOFMEMORY;
741     }
742     memcpy((LPVOID)new_this->categories, This->categories, size);
743     /* FIXME: could we more efficiently use DuplicateHandle? */
744     RegOpenKeyExW(HKEY_CLASSES_ROOT, keyname, 0, KEY_READ, &new_this->key);
745     new_this->next_index = This->next_index;
746
747     *ppenum = (LPENUMGUID)new_this;
748     return S_OK;
749 }
750
751 static const IEnumGUIDVtbl COMCAT_CLSID_IEnumGUID_Vtbl =
752 {
753     COMCAT_CLSID_IEnumGUID_QueryInterface,
754     COMCAT_CLSID_IEnumGUID_AddRef,
755     COMCAT_CLSID_IEnumGUID_Release,
756     COMCAT_CLSID_IEnumGUID_Next,
757     COMCAT_CLSID_IEnumGUID_Skip,
758     COMCAT_CLSID_IEnumGUID_Reset,
759     COMCAT_CLSID_IEnumGUID_Clone
760 };
761
762 static LPENUMGUID COMCAT_CLSID_IEnumGUID_Construct(struct class_categories *categories)
763 {
764     CLSID_IEnumGUIDImpl *This;
765
766     This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CLSID_IEnumGUIDImpl));
767     if (This) {
768         static const WCHAR keyname[] = { 'C', 'L', 'S', 'I', 'D', 0 };
769
770         This->lpVtbl = &COMCAT_CLSID_IEnumGUID_Vtbl;
771         This->categories = categories;
772         RegOpenKeyExW(HKEY_CLASSES_ROOT, keyname, 0, KEY_READ, &This->key);
773     }
774     return (LPENUMGUID)This;
775 }
776
777 /**********************************************************************
778  * CategoriesOfClass IEnumCATID (IEnumGUID) implementation
779  *
780  * This implementation is not thread-safe.  The manager itself is, but
781  * I can't imagine a valid use of an enumerator in several threads.
782  */
783 typedef struct
784 {
785     const IEnumGUIDVtbl *lpVtbl;
786     LONG  ref;
787     WCHAR keyname[68];
788     HKEY  key;
789     DWORD next_index;
790 } CATID_IEnumGUIDImpl;
791
792 static ULONG WINAPI COMCAT_CATID_IEnumGUID_AddRef(LPENUMGUID iface)
793 {
794     CATID_IEnumGUIDImpl *This = (CATID_IEnumGUIDImpl *)iface;
795     TRACE("\n");
796
797     return InterlockedIncrement(&This->ref);
798 }
799
800 static HRESULT WINAPI COMCAT_CATID_IEnumGUID_QueryInterface(
801     LPENUMGUID iface,
802     REFIID riid,
803     LPVOID *ppvObj)
804 {
805     TRACE("\n\tIID:\t%s\n",debugstr_guid(riid));
806
807     if (ppvObj == NULL) return E_POINTER;
808
809     if (IsEqualGUID(riid, &IID_IUnknown) ||
810         IsEqualGUID(riid, &IID_IEnumGUID))
811     {
812         *ppvObj = (LPVOID)iface;
813         COMCAT_CATID_IEnumGUID_AddRef(iface);
814         return S_OK;
815     }
816
817     return E_NOINTERFACE;
818 }
819
820 static ULONG WINAPI COMCAT_CATID_IEnumGUID_Release(LPENUMGUID iface)
821 {
822     CATID_IEnumGUIDImpl *This = (CATID_IEnumGUIDImpl *)iface;
823     ULONG ref;
824
825     TRACE("\n");
826
827     ref = InterlockedDecrement(&This->ref);
828     if (ref == 0) {
829         if (This->key) RegCloseKey(This->key);
830         HeapFree(GetProcessHeap(), 0, This);
831         return 0;
832     }
833     return ref;
834 }
835
836 static HRESULT WINAPI COMCAT_CATID_IEnumGUID_Next(
837     LPENUMGUID iface,
838     ULONG celt,
839     GUID *rgelt,
840     ULONG *pceltFetched)
841 {
842     CATID_IEnumGUIDImpl *This = (CATID_IEnumGUIDImpl *)iface;
843     ULONG fetched = 0;
844
845     TRACE("\n");
846
847     if (rgelt == NULL) return E_POINTER;
848
849     if (This->key) while (fetched < celt) {
850         LSTATUS res;
851         HRESULT hr;
852         WCHAR catid[39];
853         DWORD cName = 39;
854
855         res = RegEnumKeyExW(This->key, This->next_index, catid, &cName,
856                             NULL, NULL, NULL, NULL);
857         if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA) break;
858         ++(This->next_index);
859
860         hr = CLSIDFromString(catid, rgelt);
861         if (FAILED(hr)) continue;
862
863         ++fetched;
864         ++rgelt;
865     }
866
867     if (pceltFetched) *pceltFetched = fetched;
868     return fetched == celt ? S_OK : S_FALSE;
869 }
870
871 static HRESULT WINAPI COMCAT_CATID_IEnumGUID_Skip(
872     LPENUMGUID iface,
873     ULONG celt)
874 {
875     CATID_IEnumGUIDImpl *This = (CATID_IEnumGUIDImpl *)iface;
876
877     TRACE("\n");
878
879     This->next_index += celt;
880     FIXME("Never returns S_FALSE\n");
881     return S_OK;
882 }
883
884 static HRESULT WINAPI COMCAT_CATID_IEnumGUID_Reset(LPENUMGUID iface)
885 {
886     CATID_IEnumGUIDImpl *This = (CATID_IEnumGUIDImpl *)iface;
887
888     TRACE("\n");
889
890     This->next_index = 0;
891     return S_OK;
892 }
893
894 static HRESULT WINAPI COMCAT_CATID_IEnumGUID_Clone(
895     LPENUMGUID iface,
896     IEnumGUID **ppenum)
897 {
898     CATID_IEnumGUIDImpl *This = (CATID_IEnumGUIDImpl *)iface;
899     CATID_IEnumGUIDImpl *new_this;
900
901     TRACE("\n");
902
903     if (ppenum == NULL) return E_POINTER;
904
905     new_this = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CATID_IEnumGUIDImpl));
906     if (new_this == NULL) return E_OUTOFMEMORY;
907
908     new_this->lpVtbl = This->lpVtbl;
909     new_this->ref = 1;
910     lstrcpyW(new_this->keyname, This->keyname);
911     /* FIXME: could we more efficiently use DuplicateHandle? */
912     RegOpenKeyExW(HKEY_CLASSES_ROOT, new_this->keyname, 0, KEY_READ, &new_this->key);
913     new_this->next_index = This->next_index;
914
915     *ppenum = (LPENUMGUID)new_this;
916     return S_OK;
917 }
918
919 static const IEnumGUIDVtbl COMCAT_CATID_IEnumGUID_Vtbl =
920 {
921     COMCAT_CATID_IEnumGUID_QueryInterface,
922     COMCAT_CATID_IEnumGUID_AddRef,
923     COMCAT_CATID_IEnumGUID_Release,
924     COMCAT_CATID_IEnumGUID_Next,
925     COMCAT_CATID_IEnumGUID_Skip,
926     COMCAT_CATID_IEnumGUID_Reset,
927     COMCAT_CATID_IEnumGUID_Clone
928 };
929
930 static LPENUMGUID COMCAT_CATID_IEnumGUID_Construct(
931     REFCLSID rclsid, LPCWSTR postfix)
932 {
933     CATID_IEnumGUIDImpl *This;
934
935     This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CATID_IEnumGUIDImpl));
936     if (This) {
937         WCHAR prefix[6] = { 'C', 'L', 'S', 'I', 'D', '\\' };
938
939         This->lpVtbl = &COMCAT_CATID_IEnumGUID_Vtbl;
940         memcpy(This->keyname, prefix, sizeof(prefix));
941         StringFromGUID2(rclsid, This->keyname + 6, 39);
942         lstrcpyW(This->keyname + 44, postfix);
943         RegOpenKeyExW(HKEY_CLASSES_ROOT, This->keyname, 0, KEY_READ, &This->key);
944     }
945     return (LPENUMGUID)This;
946 }