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