quartz: Use unicode calls in SystemClock.
[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     const IShellItemArrayVtbl *lpVtbl;
660     LONG ref;
661
662     IShellItem **array;
663     DWORD item_count;
664 } IShellItemArrayImpl;
665
666 static HRESULT WINAPI IShellItemArray_fnQueryInterface(IShellItemArray *iface,
667                                                        REFIID riid,
668                                                        void **ppvObject)
669 {
670     IShellItemArrayImpl *This = (IShellItemArrayImpl *)iface;
671     TRACE("%p (%s, %p)\n", This, shdebugstr_guid(riid), ppvObject);
672
673     *ppvObject = NULL;
674     if(IsEqualIID(riid, &IID_IShellItemArray) ||
675        IsEqualIID(riid, &IID_IUnknown))
676     {
677         *ppvObject = This;
678     }
679
680     if(*ppvObject)
681     {
682         IUnknown_AddRef((IUnknown*)*ppvObject);
683         return S_OK;
684     }
685
686     return E_NOINTERFACE;
687 }
688
689 static ULONG WINAPI IShellItemArray_fnAddRef(IShellItemArray *iface)
690 {
691     IShellItemArrayImpl *This = (IShellItemArrayImpl *)iface;
692     LONG ref = InterlockedIncrement(&This->ref);
693     TRACE("%p - ref %d\n", This, ref);
694
695     return ref;
696 }
697
698 static ULONG WINAPI IShellItemArray_fnRelease(IShellItemArray *iface)
699 {
700     IShellItemArrayImpl *This = (IShellItemArrayImpl *)iface;
701     LONG ref = InterlockedDecrement(&This->ref);
702     TRACE("%p - ref %d\n", This, ref);
703
704     if(!ref)
705     {
706         UINT i;
707         TRACE("Freeing.\n");
708
709         for(i = 0; i < This->item_count; i++)
710             IShellItem_Release(This->array[i]);
711
712         HeapFree(GetProcessHeap(), 0, This->array);
713         HeapFree(GetProcessHeap(), 0, This);
714         return 0;
715     }
716
717     return ref;
718 }
719
720 static HRESULT WINAPI IShellItemArray_fnBindToHandler(IShellItemArray *iface,
721                                                       IBindCtx *pbc,
722                                                       REFGUID bhid,
723                                                       REFIID riid,
724                                                       void **ppvOut)
725 {
726     IShellItemArrayImpl *This = (IShellItemArrayImpl *)iface;
727     FIXME("Stub: %p (%p, %s, %s, %p)\n",
728           This, pbc, shdebugstr_guid(bhid), shdebugstr_guid(riid), ppvOut);
729
730     return E_NOTIMPL;
731 }
732
733 static HRESULT WINAPI IShellItemArray_fnGetPropertyStore(IShellItemArray *iface,
734                                                          GETPROPERTYSTOREFLAGS flags,
735                                                          REFIID riid,
736                                                          void **ppv)
737 {
738     IShellItemArrayImpl *This = (IShellItemArrayImpl *)iface;
739     FIXME("Stub: %p (%x, %s, %p)\n", This, flags, shdebugstr_guid(riid), ppv);
740
741     return E_NOTIMPL;
742 }
743
744 static HRESULT WINAPI IShellItemArray_fnGetPropertyDescriptionList(IShellItemArray *iface,
745                                                                    REFPROPERTYKEY keyType,
746                                                                    REFIID riid,
747                                                                    void **ppv)
748 {
749     IShellItemArrayImpl *This = (IShellItemArrayImpl *)iface;
750     FIXME("Stub: %p (%p, %s, %p)\n",
751           This, keyType, shdebugstr_guid(riid), ppv);
752
753     return E_NOTIMPL;
754 }
755
756 static HRESULT WINAPI IShellItemArray_fnGetAttributes(IShellItemArray *iface,
757                                                       SIATTRIBFLAGS AttribFlags,
758                                                       SFGAOF sfgaoMask,
759                                                       SFGAOF *psfgaoAttribs)
760 {
761     IShellItemArrayImpl *This = (IShellItemArrayImpl *)iface;
762     FIXME("Stub: %p (%x, %x, %p)\n", This, AttribFlags, sfgaoMask, psfgaoAttribs);
763
764     return E_NOTIMPL;
765 }
766
767 static HRESULT WINAPI IShellItemArray_fnGetCount(IShellItemArray *iface,
768                                                  DWORD *pdwNumItems)
769 {
770     IShellItemArrayImpl *This = (IShellItemArrayImpl *)iface;
771     TRACE("%p (%p)\n", This, pdwNumItems);
772
773     *pdwNumItems = This->item_count;
774
775     return S_OK;
776 }
777
778 static HRESULT WINAPI IShellItemArray_fnGetItemAt(IShellItemArray *iface,
779                                                   DWORD dwIndex,
780                                                   IShellItem **ppsi)
781 {
782     IShellItemArrayImpl *This = (IShellItemArrayImpl *)iface;
783     TRACE("%p (%x, %p)\n", This, dwIndex, ppsi);
784
785     /* zero indexed */
786     if(dwIndex + 1 > This->item_count)
787         return E_FAIL;
788
789     *ppsi = This->array[dwIndex];
790     IShellItem_AddRef(*ppsi);
791
792     return S_OK;
793 }
794
795 static HRESULT WINAPI IShellItemArray_fnEnumItems(IShellItemArray *iface,
796                                                   IEnumShellItems **ppenumShellItems)
797 {
798     IShellItemArrayImpl *This = (IShellItemArrayImpl *)iface;
799     FIXME("Stub: %p (%p)\n", This, ppenumShellItems);
800
801     return E_NOTIMPL;
802 }
803
804 static const IShellItemArrayVtbl vt_IShellItemArray = {
805     IShellItemArray_fnQueryInterface,
806     IShellItemArray_fnAddRef,
807     IShellItemArray_fnRelease,
808     IShellItemArray_fnBindToHandler,
809     IShellItemArray_fnGetPropertyStore,
810     IShellItemArray_fnGetPropertyDescriptionList,
811     IShellItemArray_fnGetAttributes,
812     IShellItemArray_fnGetCount,
813     IShellItemArray_fnGetItemAt,
814     IShellItemArray_fnEnumItems
815 };
816
817 static HRESULT IShellItemArray_Constructor(IUnknown *pUnkOuter, REFIID riid, void **ppv)
818 {
819     IShellItemArrayImpl *This;
820     HRESULT ret;
821
822     TRACE("(%p, %s, %p)\n",pUnkOuter, debugstr_guid(riid), ppv);
823
824     if(pUnkOuter)
825         return CLASS_E_NOAGGREGATION;
826
827     This = HeapAlloc(GetProcessHeap(), 0, sizeof(IShellItemArrayImpl));
828     if(!This)
829         return E_OUTOFMEMORY;
830
831     This->ref = 1;
832     This->lpVtbl = &vt_IShellItemArray;
833     This->array = NULL;
834     This->item_count = 0;
835
836     ret = IShellItemArray_QueryInterface((IShellItemArray*)This, riid, ppv);
837     IShellItemArray_Release((IShellItemArray*)This);
838
839     return ret;
840 }
841
842 HRESULT WINAPI SHCreateShellItemArray(PCIDLIST_ABSOLUTE pidlParent,
843                                       IShellFolder *psf,
844                                       UINT cidl,
845                                       PCUITEMID_CHILD_ARRAY ppidl,
846                                       IShellItemArray **ppsiItemArray)
847 {
848     IShellItemArrayImpl *This;
849     IShellItem **array;
850     HRESULT ret = E_FAIL;
851     UINT i;
852
853     TRACE("%p, %p, %d, %p, %p\n", pidlParent, psf, cidl, ppidl, ppsiItemArray);
854
855     if(!pidlParent && !psf)
856         return E_POINTER;
857
858     if(!ppidl)
859         return E_INVALIDARG;
860
861     array = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cidl*sizeof(IShellItem*));
862     if(!array)
863         return E_OUTOFMEMORY;
864
865     for(i = 0; i < cidl; i++)
866     {
867         ret = SHCreateShellItem(pidlParent, psf, ppidl[i], &array[i]);
868         if(FAILED(ret)) break;
869     }
870
871     if(SUCCEEDED(ret))
872     {
873         ret = IShellItemArray_Constructor(NULL, &IID_IShellItemArray, (void**)&This);
874         if(SUCCEEDED(ret))
875         {
876             This->array = array;
877             This->item_count = cidl;
878             *ppsiItemArray = (IShellItemArray*)This;
879
880             return ret;
881         }
882     }
883
884     /* Something failed, clean up. */
885     for(i = 0; i < cidl; i++)
886         if(array[i]) IShellItem_Release(array[i]);
887     HeapFree(GetProcessHeap(), 0, array);
888     *ppsiItemArray = NULL;
889     return ret;
890 }
891
892 HRESULT WINAPI SHCreateShellItemArrayFromShellItem(IShellItem *psi, REFIID riid, void **ppv)
893 {
894     IShellItemArrayImpl *This;
895     IShellItem **array;
896     HRESULT ret;
897
898     TRACE("%p, %s, %p\n", psi, shdebugstr_guid(riid), ppv);
899
900     array = HeapAlloc(GetProcessHeap(), 0, sizeof(IShellItem*));
901     if(!array)
902         return E_OUTOFMEMORY;
903
904     ret = IShellItemArray_Constructor(NULL, riid, (void**)&This);
905     if(SUCCEEDED(ret))
906     {
907         array[0] = psi;
908         IShellItem_AddRef(psi);
909         This->array = array;
910         This->item_count = 1;
911         *ppv = This;
912     }
913     else
914     {
915         HeapFree(GetProcessHeap(), 0, array);
916         *ppv = NULL;
917     }
918
919     return ret;
920 }
921
922 HRESULT WINAPI SHCreateShellItemArrayFromDataObject(IDataObject *pdo, REFIID riid, void **ppv)
923 {
924     IShellItemArray *psia;
925     FORMATETC fmt;
926     STGMEDIUM medium;
927     HRESULT ret;
928
929     TRACE("%p, %s, %p\n", pdo, shdebugstr_guid(riid), ppv);
930
931     if(!pdo)
932         return E_INVALIDARG;
933
934     *ppv = NULL;
935
936     fmt.cfFormat = RegisterClipboardFormatW(CFSTR_SHELLIDLISTW);
937     fmt.ptd = NULL;
938     fmt.dwAspect = DVASPECT_CONTENT;
939     fmt.lindex = -1;
940     fmt.tymed = TYMED_HGLOBAL;
941
942     ret = IDataObject_GetData(pdo, &fmt, &medium);
943     if(SUCCEEDED(ret))
944     {
945         LPIDA pida = GlobalLock(medium.u.hGlobal);
946         LPCITEMIDLIST parent_pidl;
947         LPCITEMIDLIST *children;
948         UINT i;
949         TRACE("Converting %d objects.\n", pida->cidl);
950
951         parent_pidl = (LPCITEMIDLIST) ((LPBYTE)pida+pida->aoffset[0]);
952
953         children = HeapAlloc(GetProcessHeap(), 0, sizeof(LPCITEMIDLIST)*pida->cidl);
954         for(i = 0; i < pida->cidl; i++)
955             children[i] = (LPCITEMIDLIST) ((LPBYTE)pida+pida->aoffset[i+1]);
956
957         ret = SHCreateShellItemArray(parent_pidl, NULL, pida->cidl, children, (IShellItemArray**)&psia);
958
959         HeapFree(GetProcessHeap(), 0, children);
960
961         GlobalUnlock(medium.u.hGlobal);
962         GlobalFree(medium.u.hGlobal);
963     }
964
965     if(SUCCEEDED(ret))
966     {
967         ret = IShellItemArray_QueryInterface(psia, riid, ppv);
968         IShellItemArray_Release(psia);
969     }
970
971     return ret;
972 }