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