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