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