1 /*
2  *	IEnumMoniker implementation for DEVENUM.dll
3  *
4  * Copyright (C) 2002 Robert Shearman
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  * NOTES ON THIS FILE:
21  * - Implements IEnumMoniker interface which enumerates through moniker
22  *   objects created from HKEY_CLASSES/CLSID/{DEVICE_CLSID}/Instance
23  */
24 
25 #include "devenum_private.h"
26 #include "oleauto.h"
27 #include "ocidl.h"
28 
29 #include "wine/debug.h"
30 
31 WINE_DEFAULT_DEBUG_CHANNEL(devenum);
32 
33 typedef struct
34 {
35     IEnumMoniker IEnumMoniker_iface;
36     LONG ref;
37     DWORD index;
38     DWORD subkey_cnt;
39     HKEY hkey;
40     HKEY special_hkey;
41 } EnumMonikerImpl;
42 
43 typedef struct
44 {
45     IPropertyBag IPropertyBag_iface;
46     LONG ref;
47     HKEY hkey;
48 } RegPropBagImpl;
49 
50 
51 static inline RegPropBagImpl *impl_from_IPropertyBag(IPropertyBag *iface)
52 {
53     return CONTAINING_RECORD(iface, RegPropBagImpl, IPropertyBag_iface);
54 }
55 
56 static HRESULT WINAPI DEVENUM_IPropertyBag_QueryInterface(
57     LPPROPERTYBAG iface,
58     REFIID riid,
59     LPVOID *ppvObj)
60 {
61     RegPropBagImpl *This = impl_from_IPropertyBag(iface);
62 
63     TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppvObj);
64 
65     if (This == NULL || ppvObj == NULL) return E_POINTER;
66 
67     if (IsEqualGUID(riid, &IID_IUnknown) ||
68         IsEqualGUID(riid, &IID_IPropertyBag))
69     {
70         *ppvObj = iface;
71         IPropertyBag_AddRef(iface);
72         return S_OK;
73     }
74 
75     FIXME("- no interface IID: %s\n", debugstr_guid(riid));
76     return E_NOINTERFACE;
77 }
78 
79 /**********************************************************************
80  * DEVENUM_IPropertyBag_AddRef (also IUnknown)
81  */
82 static ULONG WINAPI DEVENUM_IPropertyBag_AddRef(LPPROPERTYBAG iface)
83 {
84     RegPropBagImpl *This = impl_from_IPropertyBag(iface);
85 
86     TRACE("(%p)->() AddRef from %d\n", iface, This->ref);
87 
88     return InterlockedIncrement(&This->ref);
89 }
90 
91 /**********************************************************************
92  * DEVENUM_IPropertyBag_Release (also IUnknown)
93  */
94 static ULONG WINAPI DEVENUM_IPropertyBag_Release(LPPROPERTYBAG iface)
95 {
96     RegPropBagImpl *This = impl_from_IPropertyBag(iface);
97     ULONG ref;
98 
99     TRACE("(%p)->() ReleaseThis->ref from %d\n", iface, This->ref);
100 
101     ref = InterlockedDecrement(&This->ref);
102     if (ref == 0) {
103         RegCloseKey(This->hkey);
104         CoTaskMemFree(This);
105         DEVENUM_UnlockModule();
106     }
107     return ref;
108 }
109 
110 static HRESULT WINAPI DEVENUM_IPropertyBag_Read(
111     LPPROPERTYBAG iface,
112     LPCOLESTR pszPropName,
113     VARIANT* pVar,
114     IErrorLog* pErrorLog)
115 {
116     LPVOID pData = NULL;
117     DWORD received;
118     DWORD type = 0;
119     RegPropBagImpl *This = impl_from_IPropertyBag(iface);
120     HRESULT res = S_OK;
121     LONG reswin32;
122 
123     TRACE("(%p)->(%s, %p, %p)\n", This, debugstr_w(pszPropName), pVar, pErrorLog);
124 
125     if (!pszPropName || !pVar)
126         return E_POINTER;
127 
128     reswin32 = RegQueryValueExW(This->hkey, pszPropName, NULL, NULL, NULL, &received);
129     res = HRESULT_FROM_WIN32(reswin32);
130 
131     if (SUCCEEDED(res))
132     {
133         pData = HeapAlloc(GetProcessHeap(), 0, received);
134 
135         /* work around a GCC bug that occurs here unless we use the reswin32 variable as well */
136         reswin32 = RegQueryValueExW(This->hkey, pszPropName, NULL, &type, pData, &received);
137         res = HRESULT_FROM_WIN32(reswin32);
138     }
139 
140     if (SUCCEEDED(res))
141     {
142         res = E_INVALIDARG; /* assume we cannot coerce into right type */
143 
144         TRACE("Read %d bytes (%s)\n", received, type == REG_SZ ? debugstr_w(pData) : "binary data");
145 
146         switch (type)
147         {
148         case REG_SZ:
149             switch (V_VT(pVar))
150             {
151             case VT_LPWSTR:
152                 V_BSTR(pVar) = CoTaskMemAlloc(received);
153                 memcpy(V_BSTR(pVar), pData, received);
154                 res = S_OK;
155                 break;
156             case VT_EMPTY:
157                 V_VT(pVar) = VT_BSTR;
158             /* fall through */
159             case VT_BSTR:
160                 V_BSTR(pVar) = SysAllocStringLen(pData, received/sizeof(WCHAR) - 1);
161                 res = S_OK;
162                 break;
163             }
164             break;
165         case REG_DWORD:
166             TRACE("REG_DWORD: %x\n", *(DWORD *)pData);
167             switch (V_VT(pVar))
168             {
169             case VT_EMPTY:
170                 V_VT(pVar) = VT_I4;
171                 /* fall through */
172             case VT_I4:
173             case VT_UI4:
174                 V_I4(pVar) = *(DWORD *)pData;
175                 res = S_OK;
176                 break;
177             }
178             break;
179         case REG_BINARY:
180             {
181                 SAFEARRAYBOUND bound;
182                 void * pArrayElements;
183                 bound.lLbound = 0;
184                 bound.cElements = received;
185                 TRACE("REG_BINARY: len = %d\n", received);
186                 switch (V_VT(pVar))
187                 {
188                 case VT_EMPTY:
189                     V_VT(pVar) = VT_ARRAY | VT_UI1;
190                     /* fall through */
191                 case VT_ARRAY | VT_UI1:
192                     if (!(V_ARRAY(pVar) = SafeArrayCreate(VT_UI1, 1, &bound)))
193                         res = E_OUTOFMEMORY;
194                     else
195                         res = S_OK;
196                     break;
197                 }
198 
199                 if (res == E_INVALIDARG)
200                     break;
201 
202                 res = SafeArrayAccessData(V_ARRAY(pVar), &pArrayElements);
203                 if (FAILED(res))
204                     break;
205 
206                 CopyMemory(pArrayElements, pData, received);
207                 res = SafeArrayUnaccessData(V_ARRAY(pVar));
208                 break;
209             }
210         }
211         if (res == E_INVALIDARG)
212             FIXME("Variant type %x not supported for regtype %x\n", V_VT(pVar), type);
213     }
214 
215     HeapFree(GetProcessHeap(), 0, pData);
216 
217     TRACE("<- %x\n", res);
218     return res;
219 }
220 
221 static HRESULT WINAPI DEVENUM_IPropertyBag_Write(
222     LPPROPERTYBAG iface,
223     LPCOLESTR pszPropName,
224     VARIANT* pVar)
225 {
226     RegPropBagImpl *This = impl_from_IPropertyBag(iface);
227     LPVOID lpData = NULL;
228     DWORD cbData = 0;
229     DWORD dwType = 0;
230     HRESULT res = S_OK;
231 
232     TRACE("(%p)->(%s, %p)\n", This, debugstr_w(pszPropName), pVar);
233 
234     switch (V_VT(pVar))
235     {
236     case VT_BSTR:
237     case VT_LPWSTR:
238         TRACE("writing %s\n", debugstr_w(V_BSTR(pVar)));
239         lpData = V_BSTR(pVar);
240         dwType = REG_SZ;
241         cbData = (lstrlenW(V_BSTR(pVar)) + 1) * sizeof(WCHAR);
242         break;
243     case VT_I4:
244     case VT_UI4:
245         TRACE("writing %u\n", V_UI4(pVar));
246         lpData = &V_UI4(pVar);
247         dwType = REG_DWORD;
248         cbData = sizeof(DWORD);
249         break;
250     case VT_ARRAY | VT_UI1:
251     {
252         LONG lUbound = 0;
253         LONG lLbound = 0;
254         dwType = REG_BINARY;
255         res = SafeArrayGetLBound(V_ARRAY(pVar), 1, &lLbound);
256         res = SafeArrayGetUBound(V_ARRAY(pVar), 1, &lUbound);
257         cbData = (lUbound - lLbound + 1) /* * sizeof(BYTE)*/;
258         TRACE("cbData: %d\n", cbData);
259         res = SafeArrayAccessData(V_ARRAY(pVar), &lpData);
260         break;
261     }
262     default:
263         FIXME("Variant type %d not handled\n", V_VT(pVar));
264         return E_FAIL;
265     }
266 
267     if (RegSetValueExW(This->hkey,
268                        pszPropName, 0,
269                        dwType, lpData, cbData) != ERROR_SUCCESS)
270         res = E_FAIL;
271 
272     if (V_VT(pVar) & VT_ARRAY)
273         res = SafeArrayUnaccessData(V_ARRAY(pVar));
274 
275     return res;
276 }
277 
278 static const IPropertyBagVtbl IPropertyBag_Vtbl =
279 {
280     DEVENUM_IPropertyBag_QueryInterface,
281     DEVENUM_IPropertyBag_AddRef,
282     DEVENUM_IPropertyBag_Release,
283     DEVENUM_IPropertyBag_Read,
284     DEVENUM_IPropertyBag_Write
285 };
286 
287 static HRESULT DEVENUM_IPropertyBag_Construct(HANDLE hkey, IPropertyBag **ppBag)
288 {
289     RegPropBagImpl * rpb = CoTaskMemAlloc(sizeof(RegPropBagImpl));
290     if (!rpb)
291         return E_OUTOFMEMORY;
292     rpb->IPropertyBag_iface.lpVtbl = &IPropertyBag_Vtbl;
293     rpb->ref = 1;
294     rpb->hkey = hkey;
295     *ppBag = &rpb->IPropertyBag_iface;
296     DEVENUM_LockModule();
297     return S_OK;
298 }
299 
300 
301 static inline MediaCatMoniker *impl_from_IMoniker(IMoniker *iface)
302 {
303     return CONTAINING_RECORD(iface, MediaCatMoniker, IMoniker_iface);
304 }
305 
306 static HRESULT WINAPI DEVENUM_IMediaCatMoniker_QueryInterface(IMoniker *iface, REFIID riid,
307         void **ppv)
308 {
309     TRACE("\n\tIID:\t%s\n",debugstr_guid(riid));
310 
311     if (!ppv)
312         return E_POINTER;
313 
314     if (IsEqualGUID(riid, &IID_IUnknown) ||
315         IsEqualGUID(riid, &IID_IPersist) ||
316         IsEqualGUID(riid, &IID_IPersistStream) ||
317         IsEqualGUID(riid, &IID_IMoniker))
318     {
319         *ppv = iface;
320         IMoniker_AddRef(iface);
321         return S_OK;
322     }
323 
324     FIXME("- no interface IID: %s\n", debugstr_guid(riid));
325     *ppv = NULL;
326     return E_NOINTERFACE;
327 }
328 
329 static ULONG WINAPI DEVENUM_IMediaCatMoniker_AddRef(IMoniker *iface)
330 {
331     MediaCatMoniker *This = impl_from_IMoniker(iface);
332     ULONG ref = InterlockedIncrement(&This->ref);
333 
334     TRACE("(%p) ref=%d\n", This, ref);
335 
336     return ref;
337 }
338 
339 static ULONG WINAPI DEVENUM_IMediaCatMoniker_Release(IMoniker *iface)
340 {
341     MediaCatMoniker *This = impl_from_IMoniker(iface);
342     ULONG ref = InterlockedDecrement(&This->ref);
343 
344     TRACE("(%p) ref=%d\n", This, ref);
345 
346     if (ref == 0) {
347         RegCloseKey(This->hkey);
348         CoTaskMemFree(This);
349         DEVENUM_UnlockModule();
350     }
351     return ref;
352 }
353 
354 static HRESULT WINAPI DEVENUM_IMediaCatMoniker_GetClassID(IMoniker *iface, CLSID *pClassID)
355 {
356     MediaCatMoniker *This = impl_from_IMoniker(iface);
357 
358     TRACE("(%p)->(%p)\n", This, pClassID);
359 
360     if (pClassID == NULL)
361         return E_INVALIDARG;
362 
363     *pClassID = CLSID_CDeviceMoniker;
364 
365     return S_OK;
366 }
367 
368 static HRESULT WINAPI DEVENUM_IMediaCatMoniker_IsDirty(IMoniker *iface)
369 {
370     FIXME("(%p)->(): stub\n", iface);
371 
372     return S_FALSE;
373 }
374 
375 static HRESULT WINAPI DEVENUM_IMediaCatMoniker_Load(IMoniker *iface, IStream *pStm)
376 {
377     FIXME("(%p)->(%p): stub\n", iface, pStm);
378 
379     return E_NOTIMPL;
380 }
381 
382 static HRESULT WINAPI DEVENUM_IMediaCatMoniker_Save(IMoniker *iface, IStream *pStm, BOOL fClearDirty)
383 {
384     FIXME("(%p)->(%p, %s): stub\n", iface, pStm, fClearDirty ? "true" : "false");
385 
386     return STG_E_CANTSAVE;
387 }
388 
389 static HRESULT WINAPI DEVENUM_IMediaCatMoniker_GetSizeMax(IMoniker *iface, ULARGE_INTEGER *pcbSize)
390 {
391     FIXME("(%p)->(%p): stub\n", iface, pcbSize);
392 
393     ZeroMemory(pcbSize, sizeof(*pcbSize));
394 
395     return S_OK;
396 }
397 
398 static HRESULT WINAPI DEVENUM_IMediaCatMoniker_BindToObject(IMoniker *iface, IBindCtx *pbc,
399         IMoniker *pmkToLeft, REFIID riidResult, void **ppvResult)
400 {
401     MediaCatMoniker *This = impl_from_IMoniker(iface);
402     IUnknown * pObj = NULL;
403     IPropertyBag * pProp = NULL;
404     CLSID clsID;
405     VARIANT var;
406     HRESULT res = E_FAIL;
407 
408     TRACE("(%p)->(%p, %p, %s, %p)\n", This, pbc, pmkToLeft, debugstr_guid(riidResult), ppvResult);
409 
410     VariantInit(&var);
411     *ppvResult = NULL;
412 
413     if(pmkToLeft==NULL)
414     {
415             /* first activation of this class */
416 	    LPVOID pvptr;
417             res=IMoniker_BindToStorage(iface, NULL, NULL, &IID_IPropertyBag, &pvptr);
418             pProp = pvptr;
419             if (SUCCEEDED(res))
420             {
421                 V_VT(&var) = VT_LPWSTR;
422                 res = IPropertyBag_Read(pProp, clsid_keyname, &var, NULL);
423             }
424             if (SUCCEEDED(res))
425             {
426                 res = CLSIDFromString(V_BSTR(&var), &clsID);
427                 CoTaskMemFree(V_BSTR(&var));
428             }
429             if (SUCCEEDED(res))
430             {
431                 res=CoCreateInstance(&clsID,NULL,CLSCTX_ALL,&IID_IUnknown,&pvptr);
432                 pObj = pvptr;
433             }
434     }
435 
436     if (pObj!=NULL)
437     {
438         /* get the requested interface from the loaded class */
439         res = S_OK;
440         if (pProp) {
441            HRESULT res2;
442            LPVOID ppv = NULL;
443            res2 = IUnknown_QueryInterface(pObj, &IID_IPersistPropertyBag, &ppv);
444            if (SUCCEEDED(res2)) {
445               res = IPersistPropertyBag_Load((IPersistPropertyBag *) ppv, pProp, NULL);
446               IPersistPropertyBag_Release((IPersistPropertyBag *) ppv);
447            }
448         }
449         if (SUCCEEDED(res))
450            res= IUnknown_QueryInterface(pObj,riidResult,ppvResult);
451         IUnknown_Release(pObj);
452     }
453 
454     if (pProp)
455     {
456         IPropertyBag_Release(pProp);
457     }
458 
459     TRACE("<- 0x%x\n", res);
460 
461     return res;
462 }
463 
464 static HRESULT WINAPI DEVENUM_IMediaCatMoniker_BindToStorage(IMoniker *iface, IBindCtx *pbc,
465         IMoniker *pmkToLeft, REFIID riid, void **ppvObj)
466 {
467     MediaCatMoniker *This = impl_from_IMoniker(iface);
468 
469     TRACE("(%p)->(%p, %p, %s, %p)\n", This, pbc, pmkToLeft, debugstr_guid(riid), ppvObj);
470 
471     *ppvObj = NULL;
472 
473     if (pmkToLeft)
474         return MK_E_NOSTORAGE;
475 
476     if (pbc != NULL)
477     {
478         static DWORD reported;
479         if (!reported)
480         {
481             FIXME("ignoring IBindCtx %p\n", pbc);
482             reported++;
483         }
484     }
485 
486     if (IsEqualGUID(riid, &IID_IPropertyBag))
487     {
488         HANDLE hkey;
489         DuplicateHandle(GetCurrentProcess(), This->hkey, GetCurrentProcess(), &hkey, 0, 0, DUPLICATE_SAME_ACCESS);
490         return DEVENUM_IPropertyBag_Construct(hkey, (IPropertyBag**)ppvObj);
491     }
492 
493     return MK_E_NOSTORAGE;
494 }
495 
496 static HRESULT WINAPI DEVENUM_IMediaCatMoniker_Reduce(IMoniker *iface, IBindCtx *pbc,
497         DWORD dwReduceHowFar, IMoniker **ppmkToLeft, IMoniker **ppmkReduced)
498 {
499     TRACE("(%p)->(%p, %d, %p, %p)\n", iface, pbc, dwReduceHowFar, ppmkToLeft, ppmkReduced);
500 
501     if (ppmkToLeft)
502         *ppmkToLeft = NULL;
503     *ppmkReduced = iface;
504 
505     return MK_S_REDUCED_TO_SELF;
506 }
507 
508 static HRESULT WINAPI DEVENUM_IMediaCatMoniker_ComposeWith(IMoniker *iface, IMoniker *pmkRight,
509         BOOL fOnlyIfNotGeneric, IMoniker **ppmkComposite)
510 {
511     FIXME("(%p)->(%p, %s, %p): stub\n", iface, pmkRight, fOnlyIfNotGeneric ? "true" : "false", ppmkComposite);
512 
513     /* FIXME: use CreateGenericComposite? */
514     *ppmkComposite = NULL;
515 
516     return E_NOTIMPL;
517 }
518 
519 static HRESULT WINAPI DEVENUM_IMediaCatMoniker_Enum(IMoniker *iface, BOOL fForward,
520         IEnumMoniker **ppenumMoniker)
521 {
522     FIXME("(%p)->(%s, %p): stub\n", iface, fForward ? "true" : "false", ppenumMoniker);
523 
524     *ppenumMoniker = NULL;
525 
526     return S_OK;
527 }
528 
529 static HRESULT WINAPI DEVENUM_IMediaCatMoniker_IsEqual(IMoniker *iface, IMoniker *pmkOtherMoniker)
530 {
531     CLSID clsid;
532     LPOLESTR this_name, other_name;
533     IBindCtx *bind;
534     HRESULT res;
535 
536     TRACE("(%p)->(%p)\n", iface, pmkOtherMoniker);
537 
538     if (!pmkOtherMoniker)
539         return E_INVALIDARG;
540 
541     IMoniker_GetClassID(pmkOtherMoniker, &clsid);
542     if (!IsEqualCLSID(&clsid, &CLSID_CDeviceMoniker))
543         return S_FALSE;
544 
545     res = CreateBindCtx(0, &bind);
546     if (FAILED(res))
547        return res;
548 
549     res = S_FALSE;
550     if (SUCCEEDED(IMoniker_GetDisplayName(iface, bind, NULL, &this_name)) &&
551         SUCCEEDED(IMoniker_GetDisplayName(pmkOtherMoniker, bind, NULL, &other_name)))
552     {
553         int result = lstrcmpiW(this_name, other_name);
554         CoTaskMemFree(this_name);
555         CoTaskMemFree(other_name);
556         if (!result)
557             res = S_OK;
558     }
559     IBindCtx_Release(bind);
560     return res;
561 }
562 
563 static HRESULT WINAPI DEVENUM_IMediaCatMoniker_Hash(IMoniker *iface, DWORD *pdwHash)
564 {
565     TRACE("(%p)->(%p)\n", iface, pdwHash);
566 
567     *pdwHash = 0;
568 
569     return S_OK;
570 }
571 
572 static HRESULT WINAPI DEVENUM_IMediaCatMoniker_IsRunning(IMoniker *iface, IBindCtx *pbc,
573         IMoniker *pmkToLeft, IMoniker *pmkNewlyRunning)
574 {
575     FIXME("(%p)->(%p, %p, %p): stub\n", iface, pbc, pmkToLeft, pmkNewlyRunning);
576 
577     return S_FALSE;
578 }
579 
580 static HRESULT WINAPI DEVENUM_IMediaCatMoniker_GetTimeOfLastChange(IMoniker *iface, IBindCtx *pbc,
581         IMoniker *pmkToLeft, FILETIME *pFileTime)
582 {
583     TRACE("(%p)->(%p, %p, %p)\n", iface, pbc, pmkToLeft, pFileTime);
584 
585     pFileTime->dwLowDateTime = 0xFFFFFFFF;
586     pFileTime->dwHighDateTime = 0x7FFFFFFF;
587 
588     return MK_E_UNAVAILABLE;
589 }
590 
591 static HRESULT WINAPI DEVENUM_IMediaCatMoniker_Inverse(IMoniker *iface, IMoniker **ppmk)
592 {
593     TRACE("(%p)->(%p)\n", iface, ppmk);
594 
595     *ppmk = NULL;
596 
597     return MK_E_NOINVERSE;
598 }
599 
600 static HRESULT WINAPI DEVENUM_IMediaCatMoniker_CommonPrefixWith(IMoniker *iface,
601         IMoniker *pmkOtherMoniker, IMoniker **ppmkPrefix)
602 {
603     TRACE("(%p)->(%p, %p)\n", iface, pmkOtherMoniker, ppmkPrefix);
604 
605     *ppmkPrefix = NULL;
606 
607     return MK_E_NOPREFIX;
608 }
609 
610 static HRESULT WINAPI DEVENUM_IMediaCatMoniker_RelativePathTo(IMoniker *iface, IMoniker *pmkOther,
611         IMoniker **ppmkRelPath)
612 {
613     TRACE("(%p)->(%p, %p)\n", iface, pmkOther, ppmkRelPath);
614 
615     *ppmkRelPath = pmkOther;
616 
617     return MK_S_HIM;
618 }
619 
620 static HRESULT WINAPI DEVENUM_IMediaCatMoniker_GetDisplayName(IMoniker *iface, IBindCtx *pbc,
621         IMoniker *pmkToLeft, LPOLESTR *ppszDisplayName)
622 {
623     MediaCatMoniker *This = impl_from_IMoniker(iface);
624     WCHAR wszBuffer[MAX_PATH];
625     static const WCHAR wszFriendlyName[] = {'F','r','i','e','n','d','l','y','N','a','m','e',0};
626     DWORD received = sizeof(wszBuffer);
627 
628     TRACE("(%p)->(%p, %p, %p)\n", iface, pbc, pmkToLeft, ppszDisplayName);
629 
630     *ppszDisplayName = NULL;
631 
632     /* FIXME: should this be the weird stuff we have to parse in IParseDisplayName? */
633     if (RegQueryValueExW(This->hkey, wszFriendlyName, NULL, NULL, (LPBYTE)wszBuffer, &received) == ERROR_SUCCESS)
634     {
635         *ppszDisplayName = CoTaskMemAlloc(received);
636         strcpyW(*ppszDisplayName, wszBuffer);
637         return S_OK;
638     }
639 
640     return E_FAIL;
641 }
642 
643 static HRESULT WINAPI DEVENUM_IMediaCatMoniker_ParseDisplayName(IMoniker *iface, IBindCtx *pbc,
644         IMoniker *pmkToLeft, LPOLESTR pszDisplayName, ULONG *pchEaten, IMoniker **ppmkOut)
645 {
646     FIXME("(%p)->(%p, %p, %s, %p, %p)\n", iface, pbc, pmkToLeft, debugstr_w(pszDisplayName), pchEaten, ppmkOut);
647 
648     *pchEaten = 0;
649     *ppmkOut = NULL;
650 
651     return MK_E_SYNTAX;
652 }
653 
654 static HRESULT WINAPI DEVENUM_IMediaCatMoniker_IsSystemMoniker(IMoniker *iface, DWORD *pdwMksys)
655 {
656     TRACE("(%p)->(%p)\n", iface, pdwMksys);
657 
658     return S_FALSE;
659 }
660 
661 static const IMonikerVtbl IMoniker_Vtbl =
662 {
663     DEVENUM_IMediaCatMoniker_QueryInterface,
664     DEVENUM_IMediaCatMoniker_AddRef,
665     DEVENUM_IMediaCatMoniker_Release,
666     DEVENUM_IMediaCatMoniker_GetClassID,
667     DEVENUM_IMediaCatMoniker_IsDirty,
668     DEVENUM_IMediaCatMoniker_Load,
669     DEVENUM_IMediaCatMoniker_Save,
670     DEVENUM_IMediaCatMoniker_GetSizeMax,
671     DEVENUM_IMediaCatMoniker_BindToObject,
672     DEVENUM_IMediaCatMoniker_BindToStorage,
673     DEVENUM_IMediaCatMoniker_Reduce,
674     DEVENUM_IMediaCatMoniker_ComposeWith,
675     DEVENUM_IMediaCatMoniker_Enum,
676     DEVENUM_IMediaCatMoniker_IsEqual,
677     DEVENUM_IMediaCatMoniker_Hash,
678     DEVENUM_IMediaCatMoniker_IsRunning,
679     DEVENUM_IMediaCatMoniker_GetTimeOfLastChange,
680     DEVENUM_IMediaCatMoniker_Inverse,
681     DEVENUM_IMediaCatMoniker_CommonPrefixWith,
682     DEVENUM_IMediaCatMoniker_RelativePathTo,
683     DEVENUM_IMediaCatMoniker_GetDisplayName,
684     DEVENUM_IMediaCatMoniker_ParseDisplayName,
685     DEVENUM_IMediaCatMoniker_IsSystemMoniker
686 };
687 
688 MediaCatMoniker * DEVENUM_IMediaCatMoniker_Construct(void)
689 {
690     MediaCatMoniker * pMoniker = NULL;
691     pMoniker = CoTaskMemAlloc(sizeof(MediaCatMoniker));
692     if (!pMoniker)
693         return NULL;
694 
695     pMoniker->IMoniker_iface.lpVtbl = &IMoniker_Vtbl;
696     pMoniker->ref = 0;
697     pMoniker->hkey = NULL;
698 
699     DEVENUM_IMediaCatMoniker_AddRef(&pMoniker->IMoniker_iface);
700 
701     DEVENUM_LockModule();
702 
703     return pMoniker;
704 }
705 
706 static inline EnumMonikerImpl *impl_from_IEnumMoniker(IEnumMoniker *iface)
707 {
708     return CONTAINING_RECORD(iface, EnumMonikerImpl, IEnumMoniker_iface);
709 }
710 
711 static HRESULT WINAPI DEVENUM_IEnumMoniker_QueryInterface(IEnumMoniker *iface, REFIID riid,
712         void **ppv)
713 {
714     TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
715 
716     if (!ppv)
717         return E_POINTER;
718 
719     if (IsEqualGUID(riid, &IID_IUnknown) ||
720         IsEqualGUID(riid, &IID_IEnumMoniker))
721     {
722         *ppv = iface;
723         IEnumMoniker_AddRef(iface);
724         return S_OK;
725     }
726 
727     FIXME("- no interface IID: %s\n", debugstr_guid(riid));
728     *ppv = NULL;
729     return E_NOINTERFACE;
730 }
731 
732 static ULONG WINAPI DEVENUM_IEnumMoniker_AddRef(IEnumMoniker *iface)
733 {
734     EnumMonikerImpl *This = impl_from_IEnumMoniker(iface);
735     ULONG ref = InterlockedIncrement(&This->ref);
736 
737     TRACE("(%p) ref=%d\n", This, ref);
738 
739     return ref;
740 }
741 
742 static ULONG WINAPI DEVENUM_IEnumMoniker_Release(IEnumMoniker *iface)
743 {
744     EnumMonikerImpl *This = impl_from_IEnumMoniker(iface);
745     ULONG ref = InterlockedDecrement(&This->ref);
746 
747     TRACE("(%p) ref=%d\n", This, ref);
748 
749     if (!ref)
750     {
751         if(This->special_hkey)
752             RegCloseKey(This->special_hkey);
753         RegCloseKey(This->hkey);
754         CoTaskMemFree(This);
755         DEVENUM_UnlockModule();
756         return 0;
757     }
758     return ref;
759 }
760 
761 static HRESULT WINAPI DEVENUM_IEnumMoniker_Next(IEnumMoniker *iface, ULONG celt, IMoniker **rgelt,
762         ULONG *pceltFetched)
763 {
764     EnumMonikerImpl *This = impl_from_IEnumMoniker(iface);
765     WCHAR buffer[MAX_PATH + 1];
766     LONG res;
767     ULONG fetched = 0;
768     MediaCatMoniker * pMoniker;
769 
770     TRACE("(%p)->(%d, %p, %p)\n", iface, celt, rgelt, pceltFetched);
771 
772     while (fetched < celt)
773     {
774         if(This->index+fetched < This->subkey_cnt)
775             res = RegEnumKeyW(This->hkey, This->index+fetched, buffer, sizeof(buffer) / sizeof(WCHAR));
776         else if(This->special_hkey)
777             res = RegEnumKeyW(This->special_hkey, This->index+fetched-This->subkey_cnt, buffer, sizeof(buffer) / sizeof(WCHAR));
778         else
779             break;
780         if (res != ERROR_SUCCESS)
781         {
782             break;
783         }
784         pMoniker = DEVENUM_IMediaCatMoniker_Construct();
785         if (!pMoniker)
786             return E_OUTOFMEMORY;
787 
788         if (RegOpenKeyW(This->index+fetched < This->subkey_cnt ? This->hkey : This->special_hkey,
789                         buffer, &pMoniker->hkey) != ERROR_SUCCESS)
790         {
791             IMoniker_Release(&pMoniker->IMoniker_iface);
792             break;
793         }
794         rgelt[fetched] = &pMoniker->IMoniker_iface;
795         fetched++;
796     }
797 
798     This->index += fetched;
799 
800     TRACE("-- fetched %d\n", fetched);
801 
802     if (pceltFetched)
803         *pceltFetched = fetched;
804 
805     if (fetched != celt)
806         return S_FALSE;
807     else
808         return S_OK;
809 }
810 
811 static HRESULT WINAPI DEVENUM_IEnumMoniker_Skip(IEnumMoniker *iface, ULONG celt)
812 {
813     EnumMonikerImpl *This = impl_from_IEnumMoniker(iface);
814     DWORD special_subkeys = 0;
815 
816     TRACE("(%p)->(%d)\n", iface, celt);
817 
818     /* Before incrementing, check if there are any more values to run through.
819        Some programs use the Skip() function to get the number of devices */
820     if(This->special_hkey)
821         RegQueryInfoKeyW(This->special_hkey, NULL, NULL, NULL, &special_subkeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
822 
823     if((This->index + celt) >= This->subkey_cnt + special_subkeys)
824     {
825         return S_FALSE;
826     }
827 
828     This->index += celt;
829 
830     return S_OK;
831 }
832 
833 static HRESULT WINAPI DEVENUM_IEnumMoniker_Reset(IEnumMoniker *iface)
834 {
835     EnumMonikerImpl *This = impl_from_IEnumMoniker(iface);
836 
837     TRACE("(%p)->()\n", iface);
838 
839     This->index = 0;
840 
841     return S_OK;
842 }
843 
844 static HRESULT WINAPI DEVENUM_IEnumMoniker_Clone(IEnumMoniker *iface, IEnumMoniker **ppenum)
845 {
846     FIXME("(%p)->(%p): stub\n", iface, ppenum);
847 
848     return E_NOTIMPL;
849 }
850 
851 /**********************************************************************
852  * IEnumMoniker_Vtbl
853  */
854 static const IEnumMonikerVtbl IEnumMoniker_Vtbl =
855 {
856     DEVENUM_IEnumMoniker_QueryInterface,
857     DEVENUM_IEnumMoniker_AddRef,
858     DEVENUM_IEnumMoniker_Release,
859     DEVENUM_IEnumMoniker_Next,
860     DEVENUM_IEnumMoniker_Skip,
861     DEVENUM_IEnumMoniker_Reset,
862     DEVENUM_IEnumMoniker_Clone
863 };
864 
865 HRESULT DEVENUM_IEnumMoniker_Construct(HKEY hkey, HKEY special_hkey, IEnumMoniker ** ppEnumMoniker)
866 {
867     EnumMonikerImpl * pEnumMoniker = CoTaskMemAlloc(sizeof(EnumMonikerImpl));
868     if (!pEnumMoniker)
869         return E_OUTOFMEMORY;
870 
871     pEnumMoniker->IEnumMoniker_iface.lpVtbl = &IEnumMoniker_Vtbl;
872     pEnumMoniker->ref = 1;
873     pEnumMoniker->index = 0;
874     pEnumMoniker->hkey = hkey;
875     pEnumMoniker->special_hkey = special_hkey;
876 
877     *ppEnumMoniker = &pEnumMoniker->IEnumMoniker_iface;
878 
879     if(RegQueryInfoKeyW(pEnumMoniker->hkey, NULL, NULL, NULL, &pEnumMoniker->subkey_cnt, NULL, NULL, NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
880         pEnumMoniker->subkey_cnt = 0;
881 
882 
883     DEVENUM_LockModule();
884 
885     return S_OK;
886 }
887