shell32: Implement IExplorerBrowser::SetOptions and IExplorerBrowser::GetOptions.
[wine] / dlls / shell32 / shellitem.c
1 /*
2  * IShellItem and IShellItemArray implementations
3  *
4  * Copyright 2008 Vincent Povirk for CodeWeavers
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 "config.h"
22 #include "wine/port.h"
23
24 #include <stdio.h>
25 #include <stdarg.h>
26
27 #define COBJMACROS
28 #define NONAMELESSUNION
29 #define NONAMELESSSTRUCT
30
31 #include "windef.h"
32 #include "winbase.h"
33 #include "wine/debug.h"
34
35 #include "pidl.h"
36 #include "shell32_main.h"
37 #include "debughlp.h"
38
39 WINE_DEFAULT_DEBUG_CHANNEL(shell);
40
41 typedef struct _ShellItem {
42     const IShellItemVtbl    *lpIShellItemVtbl;
43     LONG                    ref;
44     LPITEMIDLIST            pidl;
45     const IPersistIDListVtbl *lpIPersistIDListVtbl;
46 } ShellItem;
47
48
49 static inline ShellItem *impl_from_IPersistIDList( IPersistIDList *iface )
50 {
51     return (ShellItem*)((char*)iface - FIELD_OFFSET(ShellItem, lpIPersistIDListVtbl));
52 }
53
54
55 static HRESULT WINAPI ShellItem_QueryInterface(IShellItem *iface, REFIID riid,
56     void **ppv)
57 {
58     ShellItem *This = (ShellItem*)iface;
59
60     TRACE("(%p,%p,%p)\n", iface, riid, ppv);
61
62     if (!ppv) return E_INVALIDARG;
63
64     if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IShellItem, riid))
65     {
66         *ppv = This;
67     }
68     else if (IsEqualIID(&IID_IPersist, riid) || IsEqualIID(&IID_IPersistIDList, riid))
69     {
70         *ppv = &(This->lpIPersistIDListVtbl);
71     }
72     else {
73         FIXME("not implemented for %s\n", shdebugstr_guid(riid));
74         *ppv = NULL;
75         return E_NOINTERFACE;
76     }
77
78     IUnknown_AddRef((IUnknown*)*ppv);
79     return S_OK;
80 }
81
82 static ULONG WINAPI ShellItem_AddRef(IShellItem *iface)
83 {
84     ShellItem *This = (ShellItem*)iface;
85     ULONG ref = InterlockedIncrement(&This->ref);
86
87     TRACE("(%p), new refcount=%i\n", iface, ref);
88
89     return ref;
90 }
91
92 static ULONG WINAPI ShellItem_Release(IShellItem *iface)
93 {
94     ShellItem *This = (ShellItem*)iface;
95     ULONG ref = InterlockedDecrement(&This->ref);
96
97     TRACE("(%p), new refcount=%i\n", iface, ref);
98
99     if (ref == 0)
100     {
101         ILFree(This->pidl);
102         HeapFree(GetProcessHeap(), 0, This);
103     }
104
105     return ref;
106 }
107
108 static HRESULT ShellItem_get_parent_pidl(ShellItem *This, LPITEMIDLIST *parent_pidl)
109 {
110     *parent_pidl = ILClone(This->pidl);
111     if (*parent_pidl)
112     {
113         if (ILRemoveLastID(*parent_pidl))
114             return S_OK;
115         else
116         {
117             ILFree(*parent_pidl);
118             *parent_pidl = NULL;
119             return E_INVALIDARG;
120         }
121     }
122     else
123     {
124         *parent_pidl = NULL;
125         return E_OUTOFMEMORY;
126     }
127 }
128
129 static HRESULT ShellItem_get_parent_shellfolder(ShellItem *This, IShellFolder **ppsf)
130 {
131     LPITEMIDLIST parent_pidl;
132     IShellFolder *desktop;
133     HRESULT ret;
134
135     ret = ShellItem_get_parent_pidl(This, &parent_pidl);
136     if (SUCCEEDED(ret))
137     {
138         ret = SHGetDesktopFolder(&desktop);
139         if (SUCCEEDED(ret))
140         {
141             ret = IShellFolder_BindToObject(desktop, parent_pidl, NULL, &IID_IShellFolder, (void**)ppsf);
142             IShellFolder_Release(desktop);
143         }
144         ILFree(parent_pidl);
145     }
146
147     return ret;
148 }
149
150 static HRESULT ShellItem_get_shellfolder(ShellItem *This, IBindCtx *pbc, IShellFolder **ppsf)
151 {
152     IShellFolder *desktop;
153     HRESULT ret;
154
155     ret = SHGetDesktopFolder(&desktop);
156     if (SUCCEEDED(ret))
157     {
158         if (_ILIsDesktop(This->pidl))
159         {
160             *ppsf = desktop;
161             IShellFolder_AddRef(*ppsf);
162         }
163         else
164         {
165             ret = IShellFolder_BindToObject(desktop, This->pidl, pbc, &IID_IShellFolder, (void**)ppsf);
166         }
167
168         IShellFolder_Release(desktop);
169     }
170
171     return ret;
172 }
173
174 static HRESULT WINAPI ShellItem_BindToHandler(IShellItem *iface, IBindCtx *pbc,
175     REFGUID rbhid, REFIID riid, void **ppvOut)
176 {
177     ShellItem *This = (ShellItem*)iface;
178     HRESULT ret;
179     TRACE("(%p,%p,%s,%p,%p)\n", iface, pbc, shdebugstr_guid(rbhid), riid, ppvOut);
180
181     *ppvOut = NULL;
182     if (IsEqualGUID(rbhid, &BHID_SFObject))
183     {
184         IShellFolder *psf;
185         ret = ShellItem_get_shellfolder(This, pbc, &psf);
186         if (SUCCEEDED(ret))
187         {
188             ret = IShellFolder_QueryInterface(psf, riid, ppvOut);
189             IShellFolder_Release(psf);
190         }
191         return ret;
192     }
193
194     FIXME("Unsupported BHID %s.\n", debugstr_guid(rbhid));
195
196     return MK_E_NOOBJECT;
197 }
198
199 static HRESULT WINAPI ShellItem_GetParent(IShellItem *iface, IShellItem **ppsi)
200 {
201     ShellItem *This = (ShellItem*)iface;
202     LPITEMIDLIST parent_pidl;
203     HRESULT ret;
204
205     TRACE("(%p,%p)\n", iface, ppsi);
206
207     ret = ShellItem_get_parent_pidl(This, &parent_pidl);
208     if (SUCCEEDED(ret))
209     {
210         ret = SHCreateShellItem(NULL, NULL, parent_pidl, ppsi);
211         ILFree(parent_pidl);
212     }
213
214     return ret;
215 }
216
217 static HRESULT WINAPI ShellItem_GetDisplayName(IShellItem *iface, SIGDN sigdnName,
218     LPWSTR *ppszName)
219 {
220     ShellItem *This = (ShellItem*)iface;
221     TRACE("(%p,%x,%p)\n", iface, sigdnName, ppszName);
222
223     return SHGetNameFromIDList(This->pidl, sigdnName, ppszName);
224 }
225
226 static HRESULT WINAPI ShellItem_GetAttributes(IShellItem *iface, SFGAOF sfgaoMask,
227     SFGAOF *psfgaoAttribs)
228 {
229     ShellItem *This = (ShellItem*)iface;
230     IShellFolder *parent_folder;
231     LPITEMIDLIST child_pidl;
232     HRESULT ret;
233
234     TRACE("(%p,%x,%p)\n", iface, sfgaoMask, psfgaoAttribs);
235
236     ret = ShellItem_get_parent_shellfolder(This, &parent_folder);
237     if (SUCCEEDED(ret))
238     {
239         child_pidl = ILFindLastID(This->pidl);
240         *psfgaoAttribs = sfgaoMask;
241         ret = IShellFolder_GetAttributesOf(parent_folder, 1, (LPCITEMIDLIST*)&child_pidl, psfgaoAttribs);
242         IShellFolder_Release(parent_folder);
243     }
244
245     return ret;
246 }
247
248 static HRESULT WINAPI ShellItem_Compare(IShellItem *iface, IShellItem *oth,
249     SICHINTF hint, int *piOrder)
250 {
251     LPWSTR dispname, dispname_oth;
252     HRESULT ret;
253     TRACE("(%p,%p,%x,%p)\n", iface, oth, hint, piOrder);
254
255     if(hint & (SICHINT_CANONICAL | SICHINT_ALLFIELDS))
256         FIXME("Unsupported flags 0x%08x\n", hint);
257
258     ret = IShellItem_GetDisplayName(iface, SIGDN_DESKTOPABSOLUTEEDITING, &dispname);
259     if(SUCCEEDED(ret))
260     {
261         ret = IShellItem_GetDisplayName(oth, SIGDN_DESKTOPABSOLUTEEDITING, &dispname_oth);
262         if(SUCCEEDED(ret))
263         {
264             *piOrder = lstrcmpiW(dispname, dispname_oth);
265             CoTaskMemFree(dispname_oth);
266         }
267         CoTaskMemFree(dispname);
268     }
269
270     if(SUCCEEDED(ret) && *piOrder &&
271        (hint & SICHINT_TEST_FILESYSPATH_IF_NOT_EQUAL))
272     {
273         LPWSTR dispname, dispname_oth;
274
275         TRACE("Testing filesystem path.\n");
276         ret = IShellItem_GetDisplayName(iface, SIGDN_FILESYSPATH, &dispname);
277         if(SUCCEEDED(ret))
278         {
279             ret = IShellItem_GetDisplayName(oth, SIGDN_FILESYSPATH, &dispname_oth);
280             if(SUCCEEDED(ret))
281             {
282                 *piOrder = lstrcmpiW(dispname, dispname_oth);
283                 CoTaskMemFree(dispname_oth);
284             }
285             CoTaskMemFree(dispname);
286         }
287     }
288
289     if(FAILED(ret))
290         return ret;
291
292     if(*piOrder)
293         return S_FALSE;
294     else
295         return S_OK;
296 }
297
298 static const IShellItemVtbl ShellItem_Vtbl = {
299     ShellItem_QueryInterface,
300     ShellItem_AddRef,
301     ShellItem_Release,
302     ShellItem_BindToHandler,
303     ShellItem_GetParent,
304     ShellItem_GetDisplayName,
305     ShellItem_GetAttributes,
306     ShellItem_Compare
307 };
308
309
310 static HRESULT ShellItem_GetClassID(ShellItem* This, CLSID *pClassID)
311 {
312     TRACE("(%p,%p)\n", This, pClassID);
313
314     *pClassID = CLSID_ShellItem;
315     return S_OK;
316 }
317
318
319 static HRESULT WINAPI ShellItem_IPersistIDList_QueryInterface(IPersistIDList *iface,
320     REFIID riid, void **ppv)
321 {
322     ShellItem *This = impl_from_IPersistIDList(iface);
323     return ShellItem_QueryInterface((IShellItem*)This, riid, ppv);
324 }
325
326 static ULONG WINAPI ShellItem_IPersistIDList_AddRef(IPersistIDList *iface)
327 {
328     ShellItem *This = impl_from_IPersistIDList(iface);
329     return ShellItem_AddRef((IShellItem*)This);
330 }
331
332 static ULONG WINAPI ShellItem_IPersistIDList_Release(IPersistIDList *iface)
333 {
334     ShellItem *This = impl_from_IPersistIDList(iface);
335     return ShellItem_Release((IShellItem*)This);
336 }
337
338 static HRESULT WINAPI ShellItem_IPersistIDList_GetClassID(IPersistIDList* iface,
339     CLSID *pClassID)
340 {
341     ShellItem *This = impl_from_IPersistIDList(iface);
342
343     return ShellItem_GetClassID(This, pClassID);
344 }
345
346 static HRESULT WINAPI ShellItem_IPersistIDList_SetIDList(IPersistIDList* iface,
347     LPCITEMIDLIST pidl)
348 {
349     ShellItem *This = impl_from_IPersistIDList(iface);
350     LPITEMIDLIST new_pidl;
351
352     TRACE("(%p,%p)\n", This, pidl);
353
354     new_pidl = ILClone(pidl);
355
356     if (new_pidl)
357     {
358         ILFree(This->pidl);
359         This->pidl = new_pidl;
360         return S_OK;
361     }
362     else
363         return E_OUTOFMEMORY;
364 }
365
366 static HRESULT WINAPI ShellItem_IPersistIDList_GetIDList(IPersistIDList* iface,
367     LPITEMIDLIST *ppidl)
368 {
369     ShellItem *This = impl_from_IPersistIDList(iface);
370
371     TRACE("(%p,%p)\n", This, ppidl);
372
373     *ppidl = ILClone(This->pidl);
374     if (*ppidl)
375         return S_OK;
376     else
377         return E_OUTOFMEMORY;
378 }
379
380 static const IPersistIDListVtbl ShellItem_IPersistIDList_Vtbl = {
381     ShellItem_IPersistIDList_QueryInterface,
382     ShellItem_IPersistIDList_AddRef,
383     ShellItem_IPersistIDList_Release,
384     ShellItem_IPersistIDList_GetClassID,
385     ShellItem_IPersistIDList_SetIDList,
386     ShellItem_IPersistIDList_GetIDList
387 };
388
389
390 HRESULT WINAPI IShellItem_Constructor(IUnknown *pUnkOuter, REFIID riid, void **ppv)
391 {
392     ShellItem *This;
393     HRESULT ret;
394
395     TRACE("(%p,%s)\n",pUnkOuter, debugstr_guid(riid));
396
397     *ppv = NULL;
398
399     if (pUnkOuter) return CLASS_E_NOAGGREGATION;
400
401     This = HeapAlloc(GetProcessHeap(), 0, sizeof(ShellItem));
402     This->lpIShellItemVtbl = &ShellItem_Vtbl;
403     This->ref = 1;
404     This->pidl = NULL;
405     This->lpIPersistIDListVtbl = &ShellItem_IPersistIDList_Vtbl;
406
407     ret = ShellItem_QueryInterface((IShellItem*)This, riid, ppv);
408     ShellItem_Release((IShellItem*)This);
409
410     return ret;
411 }
412
413 HRESULT WINAPI SHCreateShellItem(LPCITEMIDLIST pidlParent,
414     IShellFolder *psfParent, LPCITEMIDLIST pidl, IShellItem **ppsi)
415 {
416     ShellItem *This;
417     LPITEMIDLIST new_pidl;
418     HRESULT ret;
419
420     TRACE("(%p,%p,%p,%p)\n", pidlParent, psfParent, pidl, ppsi);
421
422     if (!pidl)
423     {
424         return E_INVALIDARG;
425     }
426     else if (pidlParent || psfParent)
427     {
428         LPITEMIDLIST temp_parent=NULL;
429         if (!pidlParent)
430         {
431             IPersistFolder2* ppf2Parent;
432
433             if (FAILED(IPersistFolder2_QueryInterface(psfParent, &IID_IPersistFolder2, (void**)&ppf2Parent)))
434             {
435                 FIXME("couldn't get IPersistFolder2 interface of parent\n");
436                 return E_NOINTERFACE;
437             }
438
439             if (FAILED(IPersistFolder2_GetCurFolder(ppf2Parent, &temp_parent)))
440             {
441                 FIXME("couldn't get parent PIDL\n");
442                 IPersistFolder2_Release(ppf2Parent);
443                 return E_NOINTERFACE;
444             }
445
446             pidlParent = temp_parent;
447             IPersistFolder2_Release(ppf2Parent);
448         }
449
450         new_pidl = ILCombine(pidlParent, pidl);
451         ILFree(temp_parent);
452
453         if (!new_pidl)
454             return E_OUTOFMEMORY;
455     }
456     else
457     {
458         new_pidl = ILClone(pidl);
459         if (!new_pidl)
460             return E_OUTOFMEMORY;
461     }
462
463     ret = IShellItem_Constructor(NULL, &IID_IShellItem, (void**)&This);
464     if (This)
465     {
466         *ppsi = (IShellItem*)This;
467         This->pidl = new_pidl;
468     }
469     else
470     {
471         *ppsi = NULL;
472         ILFree(new_pidl);
473     }
474     return ret;
475 }
476
477 HRESULT WINAPI SHCreateItemFromParsingName(PCWSTR pszPath,
478     IBindCtx *pbc, REFIID riid, void **ppv)
479 {
480     LPITEMIDLIST pidl;
481     HRESULT ret;
482
483     *ppv = NULL;
484
485     ret = SHParseDisplayName(pszPath, pbc, &pidl, 0, NULL);
486     if(SUCCEEDED(ret))
487     {
488         ShellItem *This;
489         ret = IShellItem_Constructor(NULL, riid, (void**)&This);
490
491         if(SUCCEEDED(ret))
492         {
493             This->pidl = pidl;
494             *ppv = (void*)This;
495         }
496         else
497         {
498             ILFree(pidl);
499         }
500     }
501     return ret;
502 }
503
504 HRESULT WINAPI SHCreateItemFromIDList(PCIDLIST_ABSOLUTE pidl, REFIID riid, void **ppv)
505 {
506     ShellItem *psiimpl;
507     HRESULT ret;
508
509     if(!pidl)
510         return E_INVALIDARG;
511
512     ret = IShellItem_Constructor(NULL, riid, ppv);
513     if(SUCCEEDED(ret))
514     {
515         psiimpl = (ShellItem*)*ppv;
516         psiimpl->pidl = ILClone(pidl);
517     }
518
519     return ret;
520 }
521
522 HRESULT WINAPI SHGetItemFromDataObject(IDataObject *pdtobj,
523     DATAOBJ_GET_ITEM_FLAGS dwFlags, REFIID riid, void **ppv)
524 {
525     FORMATETC fmt;
526     STGMEDIUM medium;
527     HRESULT ret;
528
529     TRACE("%p, %x, %s, %p\n", pdtobj, dwFlags, debugstr_guid(riid), ppv);
530
531     if(!pdtobj)
532         return E_INVALIDARG;
533
534     fmt.cfFormat = RegisterClipboardFormatW(CFSTR_SHELLIDLISTW);
535     fmt.ptd = NULL;
536     fmt.dwAspect = DVASPECT_CONTENT;
537     fmt.lindex = -1;
538     fmt.tymed = TYMED_HGLOBAL;
539
540     ret = IDataObject_GetData(pdtobj, &fmt, &medium);
541     if(SUCCEEDED(ret))
542     {
543         LPIDA pida = GlobalLock(medium.u.hGlobal);
544
545         if((pida->cidl > 1 && !(dwFlags & DOGIF_ONLY_IF_ONE)) ||
546            pida->cidl == 1)
547         {
548             LPITEMIDLIST pidl;
549
550             /* Get the first pidl (parent + child1) */
551             pidl = ILCombine((LPCITEMIDLIST) ((LPBYTE)pida+pida->aoffset[0]),
552                              (LPCITEMIDLIST) ((LPBYTE)pida+pida->aoffset[1]));
553
554             ret = SHCreateItemFromIDList(pidl, riid, ppv);
555             ILFree(pidl);
556         }
557         else
558         {
559             ret = E_FAIL;
560         }
561
562         GlobalUnlock(medium.u.hGlobal);
563         GlobalFree(medium.u.hGlobal);
564     }
565
566     if(FAILED(ret) && !(dwFlags & DOGIF_NO_HDROP))
567     {
568         TRACE("Attempting to fall back on CF_HDROP.\n");
569
570         fmt.cfFormat = CF_HDROP;
571         fmt.ptd = NULL;
572         fmt.dwAspect = DVASPECT_CONTENT;
573         fmt.lindex = -1;
574         fmt.tymed = TYMED_HGLOBAL;
575
576         ret = IDataObject_GetData(pdtobj, &fmt, &medium);
577         if(SUCCEEDED(ret))
578         {
579             DROPFILES *df = GlobalLock(medium.u.hGlobal);
580             LPBYTE files = (LPBYTE)df + df->pFiles;
581             BOOL multiple_files = FALSE;
582
583             ret = E_FAIL;
584             if(!df->fWide)
585             {
586                 WCHAR filename[MAX_PATH];
587                 PCSTR first_file = (PCSTR)files;
588                 if(*(files + lstrlenA(first_file) + 1) != 0)
589                     multiple_files = TRUE;
590
591                 if( !(multiple_files && (dwFlags & DOGIF_ONLY_IF_ONE)) )
592                 {
593                     MultiByteToWideChar(CP_ACP, 0, first_file, -1, filename, MAX_PATH);
594                     ret = SHCreateItemFromParsingName(filename, NULL, riid, ppv);
595                 }
596             }
597             else
598             {
599                 PCWSTR first_file = (PCWSTR)files;
600                 if(*((PCWSTR)files + lstrlenW(first_file) + 1) != 0)
601                     multiple_files = TRUE;
602
603                 if( !(multiple_files && (dwFlags & DOGIF_ONLY_IF_ONE)) )
604                     ret = SHCreateItemFromParsingName(first_file, NULL, riid, ppv);
605             }
606
607             GlobalUnlock(medium.u.hGlobal);
608             GlobalFree(medium.u.hGlobal);
609         }
610     }
611
612     if(FAILED(ret) && !(dwFlags & DOGIF_NO_URL))
613     {
614         FIXME("Failed to create item, should try CF_URL.\n");
615     }
616
617     return ret;
618 }
619
620 HRESULT WINAPI SHGetItemFromObject(IUnknown *punk, REFIID riid, void **ppv)
621 {
622     LPITEMIDLIST pidl;
623     HRESULT ret;
624
625     ret = SHGetIDListFromObject(punk, &pidl);
626     if(SUCCEEDED(ret))
627     {
628         ret = SHCreateItemFromIDList(pidl, riid, ppv);
629         ILFree(pidl);
630     }
631
632     return ret;
633 }
634
635 /*************************************************************************
636  * IShellItemArray implementation
637  */
638 typedef struct {
639     const IShellItemArrayVtbl *lpVtbl;
640     LONG ref;
641
642     IShellItem **array;
643     DWORD item_count;
644 } IShellItemArrayImpl;
645
646 static HRESULT WINAPI IShellItemArray_fnQueryInterface(IShellItemArray *iface,
647                                                        REFIID riid,
648                                                        void **ppvObject)
649 {
650     IShellItemArrayImpl *This = (IShellItemArrayImpl *)iface;
651     TRACE("%p (%s, %p)\n", This, shdebugstr_guid(riid), ppvObject);
652
653     *ppvObject = NULL;
654     if(IsEqualIID(riid, &IID_IShellItemArray) ||
655        IsEqualIID(riid, &IID_IUnknown))
656     {
657         *ppvObject = This;
658     }
659
660     if(*ppvObject)
661     {
662         IUnknown_AddRef((IUnknown*)*ppvObject);
663         return S_OK;
664     }
665
666     return E_NOINTERFACE;
667 }
668
669 static ULONG WINAPI IShellItemArray_fnAddRef(IShellItemArray *iface)
670 {
671     IShellItemArrayImpl *This = (IShellItemArrayImpl *)iface;
672     LONG ref = InterlockedIncrement(&This->ref);
673     TRACE("%p - ref %d\n", This, ref);
674
675     return ref;
676 }
677
678 static ULONG WINAPI IShellItemArray_fnRelease(IShellItemArray *iface)
679 {
680     IShellItemArrayImpl *This = (IShellItemArrayImpl *)iface;
681     LONG ref = InterlockedDecrement(&This->ref);
682     TRACE("%p - ref %d\n", This, ref);
683
684     if(!ref)
685     {
686         UINT i;
687         TRACE("Freeing.\n");
688
689         for(i = 0; i < This->item_count; i++)
690             IShellItem_Release(This->array[i]);
691
692         HeapFree(GetProcessHeap(), 0, This->array);
693         HeapFree(GetProcessHeap(), 0, This);
694         return 0;
695     }
696
697     return ref;
698 }
699
700 static HRESULT WINAPI IShellItemArray_fnBindToHandler(IShellItemArray *iface,
701                                                       IBindCtx *pbc,
702                                                       REFGUID bhid,
703                                                       REFIID riid,
704                                                       void **ppvOut)
705 {
706     IShellItemArrayImpl *This = (IShellItemArrayImpl *)iface;
707     FIXME("Stub: %p (%p, %s, %s, %p)\n",
708           This, pbc, shdebugstr_guid(bhid), shdebugstr_guid(riid), ppvOut);
709
710     return E_NOTIMPL;
711 }
712
713 static HRESULT WINAPI IShellItemArray_fnGetPropertyStore(IShellItemArray *iface,
714                                                          GETPROPERTYSTOREFLAGS flags,
715                                                          REFIID riid,
716                                                          void **ppv)
717 {
718     IShellItemArrayImpl *This = (IShellItemArrayImpl *)iface;
719     FIXME("Stub: %p (%x, %s, %p)\n", This, flags, shdebugstr_guid(riid), ppv);
720
721     return E_NOTIMPL;
722 }
723
724 static HRESULT WINAPI IShellItemArray_fnGetPropertyDescriptionList(IShellItemArray *iface,
725                                                                    REFPROPERTYKEY keyType,
726                                                                    REFIID riid,
727                                                                    void **ppv)
728 {
729     IShellItemArrayImpl *This = (IShellItemArrayImpl *)iface;
730     FIXME("Stub: %p (%p, %s, %p)\n",
731           This, keyType, shdebugstr_guid(riid), ppv);
732
733     return E_NOTIMPL;
734 }
735
736 static HRESULT WINAPI IShellItemArray_fnGetAttributes(IShellItemArray *iface,
737                                                       SIATTRIBFLAGS AttribFlags,
738                                                       SFGAOF sfgaoMask,
739                                                       SFGAOF *psfgaoAttribs)
740 {
741     IShellItemArrayImpl *This = (IShellItemArrayImpl *)iface;
742     FIXME("Stub: %p (%x, %x, %p)\n", This, AttribFlags, sfgaoMask, psfgaoAttribs);
743
744     return E_NOTIMPL;
745 }
746
747 static HRESULT WINAPI IShellItemArray_fnGetCount(IShellItemArray *iface,
748                                                  DWORD *pdwNumItems)
749 {
750     IShellItemArrayImpl *This = (IShellItemArrayImpl *)iface;
751     TRACE("%p (%p)\n", This, pdwNumItems);
752
753     *pdwNumItems = This->item_count;
754
755     return S_OK;
756 }
757
758 static HRESULT WINAPI IShellItemArray_fnGetItemAt(IShellItemArray *iface,
759                                                   DWORD dwIndex,
760                                                   IShellItem **ppsi)
761 {
762     IShellItemArrayImpl *This = (IShellItemArrayImpl *)iface;
763     TRACE("%p (%x, %p)\n", This, dwIndex, ppsi);
764
765     /* zero indexed */
766     if(dwIndex + 1 > This->item_count)
767         return E_FAIL;
768
769     *ppsi = This->array[dwIndex];
770     IShellItem_AddRef(*ppsi);
771
772     return S_OK;
773 }
774
775 static HRESULT WINAPI IShellItemArray_fnEnumItems(IShellItemArray *iface,
776                                                   IEnumShellItems **ppenumShellItems)
777 {
778     IShellItemArrayImpl *This = (IShellItemArrayImpl *)iface;
779     FIXME("Stub: %p (%p)\n", This, ppenumShellItems);
780
781     return E_NOTIMPL;
782 }
783
784 static const IShellItemArrayVtbl vt_IShellItemArray = {
785     IShellItemArray_fnQueryInterface,
786     IShellItemArray_fnAddRef,
787     IShellItemArray_fnRelease,
788     IShellItemArray_fnBindToHandler,
789     IShellItemArray_fnGetPropertyStore,
790     IShellItemArray_fnGetPropertyDescriptionList,
791     IShellItemArray_fnGetAttributes,
792     IShellItemArray_fnGetCount,
793     IShellItemArray_fnGetItemAt,
794     IShellItemArray_fnEnumItems
795 };
796
797 static HRESULT IShellItemArray_Constructor(IUnknown *pUnkOuter, REFIID riid, void **ppv)
798 {
799     IShellItemArrayImpl *This;
800     HRESULT ret;
801
802     TRACE("(%p, %s, %p)\n",pUnkOuter, debugstr_guid(riid), ppv);
803
804     if(pUnkOuter)
805         return CLASS_E_NOAGGREGATION;
806
807     This = HeapAlloc(GetProcessHeap(), 0, sizeof(IShellItemArrayImpl));
808     if(!This)
809         return E_OUTOFMEMORY;
810
811     This->ref = 1;
812     This->lpVtbl = &vt_IShellItemArray;
813     This->array = NULL;
814     This->item_count = 0;
815
816     ret = IShellItemArray_QueryInterface((IShellItemArray*)This, riid, ppv);
817     IShellItemArray_Release((IShellItemArray*)This);
818
819     return ret;
820 }
821
822 HRESULT WINAPI SHCreateShellItemArray(PCIDLIST_ABSOLUTE pidlParent,
823                                       IShellFolder *psf,
824                                       UINT cidl,
825                                       PCUITEMID_CHILD_ARRAY ppidl,
826                                       IShellItemArray **ppsiItemArray)
827 {
828     IShellItemArrayImpl *This;
829     IShellItem **array;
830     HRESULT ret = E_FAIL;
831     UINT i;
832
833     TRACE("%p, %p, %d, %p, %p\n", pidlParent, psf, cidl, ppidl, ppsiItemArray);
834
835     if(!pidlParent && !psf)
836         return E_POINTER;
837
838     if(!ppidl)
839         return E_INVALIDARG;
840
841     array = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cidl*sizeof(IShellItem*));
842     if(!array)
843         return E_OUTOFMEMORY;
844
845     for(i = 0; i < cidl; i++)
846     {
847         ret = SHCreateShellItem(pidlParent, psf, ppidl[i], &array[i]);
848         if(FAILED(ret)) break;
849     }
850
851     if(SUCCEEDED(ret))
852     {
853         ret = IShellItemArray_Constructor(NULL, &IID_IShellItemArray, (void**)&This);
854         if(SUCCEEDED(ret))
855         {
856             This->array = array;
857             This->item_count = cidl;
858             *ppsiItemArray = (IShellItemArray*)This;
859
860             return ret;
861         }
862     }
863
864     /* Something failed, clean up. */
865     for(i = 0; i < cidl; i++)
866         if(array[i]) IShellItem_Release(array[i]);
867     HeapFree(GetProcessHeap(), 0, array);
868     *ppsiItemArray = NULL;
869     return ret;
870 }
871
872 HRESULT WINAPI SHCreateShellItemArrayFromShellItem(IShellItem *psi, REFIID riid, void **ppv)
873 {
874     IShellItemArrayImpl *This;
875     IShellItem **array;
876     HRESULT ret;
877
878     TRACE("%p, %s, %p\n", psi, shdebugstr_guid(riid), ppv);
879
880     array = HeapAlloc(GetProcessHeap(), 0, sizeof(IShellItem*));
881     if(!array)
882         return E_OUTOFMEMORY;
883
884     ret = IShellItemArray_Constructor(NULL, riid, (void**)&This);
885     if(SUCCEEDED(ret))
886     {
887         array[0] = psi;
888         IShellItem_AddRef(psi);
889         This->array = array;
890         This->item_count = 1;
891         *ppv = This;
892     }
893     else
894     {
895         HeapFree(GetProcessHeap(), 0, array);
896         *ppv = NULL;
897     }
898
899     return ret;
900 }
901
902 HRESULT WINAPI SHCreateShellItemArrayFromDataObject(IDataObject *pdo, REFIID riid, void **ppv)
903 {
904     IShellItemArray *psia;
905     FORMATETC fmt;
906     STGMEDIUM medium;
907     HRESULT ret;
908
909     TRACE("%p, %s, %p\n", pdo, shdebugstr_guid(riid), ppv);
910
911     if(!pdo)
912         return E_INVALIDARG;
913
914     *ppv = NULL;
915
916     fmt.cfFormat = RegisterClipboardFormatW(CFSTR_SHELLIDLISTW);
917     fmt.ptd = NULL;
918     fmt.dwAspect = DVASPECT_CONTENT;
919     fmt.lindex = -1;
920     fmt.tymed = TYMED_HGLOBAL;
921
922     ret = IDataObject_GetData(pdo, &fmt, &medium);
923     if(SUCCEEDED(ret))
924     {
925         LPIDA pida = GlobalLock(medium.u.hGlobal);
926         LPCITEMIDLIST parent_pidl;
927         LPCITEMIDLIST *children;
928         UINT i;
929         TRACE("Converting %d objects.\n", pida->cidl);
930
931         parent_pidl = (LPCITEMIDLIST) ((LPBYTE)pida+pida->aoffset[0]);
932
933         children = HeapAlloc(GetProcessHeap(), 0, sizeof(LPCITEMIDLIST)*pida->cidl);
934         for(i = 0; i < pida->cidl; i++)
935             children[i] = (LPCITEMIDLIST) ((LPBYTE)pida+pida->aoffset[i+1]);
936
937         ret = SHCreateShellItemArray(parent_pidl, NULL, pida->cidl, children, (IShellItemArray**)&psia);
938
939         HeapFree(GetProcessHeap(), 0, children);
940
941         GlobalUnlock(medium.u.hGlobal);
942         GlobalFree(medium.u.hGlobal);
943     }
944
945     if(SUCCEEDED(ret))
946     {
947         ret = IShellItemArray_QueryInterface(psia, riid, ppv);
948         IShellItemArray_Release(psia);
949     }
950
951     return ret;
952 }