2 * IShellItem and IShellItemArray implementations
4 * Copyright 2008 Vincent Povirk for CodeWeavers
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.
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.
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
22 #include "wine/port.h"
28 #define NONAMELESSUNION
29 #define NONAMELESSSTRUCT
33 #include "wine/debug.h"
36 #include "shell32_main.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(shell);
41 typedef struct _ShellItem {
42 const IShellItemVtbl *lpIShellItemVtbl;
45 const IPersistIDListVtbl *lpIPersistIDListVtbl;
49 static inline ShellItem *impl_from_IPersistIDList( IPersistIDList *iface )
51 return (ShellItem*)((char*)iface - FIELD_OFFSET(ShellItem, lpIPersistIDListVtbl));
55 static HRESULT WINAPI ShellItem_QueryInterface(IShellItem *iface, REFIID riid,
58 ShellItem *This = (ShellItem*)iface;
60 TRACE("(%p,%p,%p)\n", iface, riid, ppv);
62 if (!ppv) return E_INVALIDARG;
64 if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IShellItem, riid))
68 else if (IsEqualIID(&IID_IPersist, riid) || IsEqualIID(&IID_IPersistIDList, riid))
70 *ppv = &(This->lpIPersistIDListVtbl);
73 FIXME("not implemented for %s\n", shdebugstr_guid(riid));
78 IUnknown_AddRef((IUnknown*)*ppv);
82 static ULONG WINAPI ShellItem_AddRef(IShellItem *iface)
84 ShellItem *This = (ShellItem*)iface;
85 ULONG ref = InterlockedIncrement(&This->ref);
87 TRACE("(%p), new refcount=%i\n", iface, ref);
92 static ULONG WINAPI ShellItem_Release(IShellItem *iface)
94 ShellItem *This = (ShellItem*)iface;
95 ULONG ref = InterlockedDecrement(&This->ref);
97 TRACE("(%p), new refcount=%i\n", iface, ref);
102 HeapFree(GetProcessHeap(), 0, This);
108 static HRESULT ShellItem_get_parent_pidl(ShellItem *This, LPITEMIDLIST *parent_pidl)
110 *parent_pidl = ILClone(This->pidl);
113 if (ILRemoveLastID(*parent_pidl))
117 ILFree(*parent_pidl);
125 return E_OUTOFMEMORY;
129 static HRESULT ShellItem_get_parent_shellfolder(ShellItem *This, IShellFolder **ppsf)
131 LPITEMIDLIST parent_pidl;
132 IShellFolder *desktop;
135 ret = ShellItem_get_parent_pidl(This, &parent_pidl);
138 ret = SHGetDesktopFolder(&desktop);
141 ret = IShellFolder_BindToObject(desktop, parent_pidl, NULL, &IID_IShellFolder, (void**)ppsf);
142 IShellFolder_Release(desktop);
150 static HRESULT WINAPI ShellItem_BindToHandler(IShellItem *iface, IBindCtx *pbc,
151 REFGUID rbhid, REFIID riid, void **ppvOut)
153 FIXME("(%p,%p,%s,%p,%p)\n", iface, pbc, shdebugstr_guid(rbhid), riid, ppvOut);
160 static HRESULT WINAPI ShellItem_GetParent(IShellItem *iface, IShellItem **ppsi)
162 ShellItem *This = (ShellItem*)iface;
163 LPITEMIDLIST parent_pidl;
166 TRACE("(%p,%p)\n", iface, ppsi);
168 ret = ShellItem_get_parent_pidl(This, &parent_pidl);
171 ret = SHCreateShellItem(NULL, NULL, parent_pidl, ppsi);
178 static HRESULT WINAPI ShellItem_GetDisplayName(IShellItem *iface, SIGDN sigdnName,
181 ShellItem *This = (ShellItem*)iface;
182 TRACE("(%p,%x,%p)\n", iface, sigdnName, ppszName);
184 return SHGetNameFromIDList(This->pidl, sigdnName, ppszName);
187 static HRESULT WINAPI ShellItem_GetAttributes(IShellItem *iface, SFGAOF sfgaoMask,
188 SFGAOF *psfgaoAttribs)
190 ShellItem *This = (ShellItem*)iface;
191 IShellFolder *parent_folder;
192 LPITEMIDLIST child_pidl;
195 TRACE("(%p,%x,%p)\n", iface, sfgaoMask, psfgaoAttribs);
197 ret = ShellItem_get_parent_shellfolder(This, &parent_folder);
200 child_pidl = ILFindLastID(This->pidl);
201 *psfgaoAttribs = sfgaoMask;
202 ret = IShellFolder_GetAttributesOf(parent_folder, 1, (LPCITEMIDLIST*)&child_pidl, psfgaoAttribs);
203 IShellFolder_Release(parent_folder);
209 static HRESULT WINAPI ShellItem_Compare(IShellItem *iface, IShellItem *oth,
210 SICHINTF hint, int *piOrder)
212 LPWSTR dispname, dispname_oth;
214 TRACE("(%p,%p,%x,%p)\n", iface, oth, hint, piOrder);
216 if(hint & (SICHINT_CANONICAL | SICHINT_ALLFIELDS))
217 FIXME("Unsupported flags 0x%08x\n", hint);
219 ret = IShellItem_GetDisplayName(iface, SIGDN_DESKTOPABSOLUTEEDITING, &dispname);
222 ret = IShellItem_GetDisplayName(oth, SIGDN_DESKTOPABSOLUTEEDITING, &dispname_oth);
225 *piOrder = lstrcmpiW(dispname, dispname_oth);
226 CoTaskMemFree(dispname_oth);
228 CoTaskMemFree(dispname);
231 if(SUCCEEDED(ret) && *piOrder &&
232 (hint & SICHINT_TEST_FILESYSPATH_IF_NOT_EQUAL))
234 LPWSTR dispname, dispname_oth;
236 TRACE("Testing filesystem path.\n");
237 ret = IShellItem_GetDisplayName(iface, SIGDN_FILESYSPATH, &dispname);
240 ret = IShellItem_GetDisplayName(oth, SIGDN_FILESYSPATH, &dispname_oth);
243 *piOrder = lstrcmpiW(dispname, dispname_oth);
244 CoTaskMemFree(dispname_oth);
246 CoTaskMemFree(dispname);
259 static const IShellItemVtbl ShellItem_Vtbl = {
260 ShellItem_QueryInterface,
263 ShellItem_BindToHandler,
265 ShellItem_GetDisplayName,
266 ShellItem_GetAttributes,
271 static HRESULT ShellItem_GetClassID(ShellItem* This, CLSID *pClassID)
273 TRACE("(%p,%p)\n", This, pClassID);
275 *pClassID = CLSID_ShellItem;
280 static HRESULT WINAPI ShellItem_IPersistIDList_QueryInterface(IPersistIDList *iface,
281 REFIID riid, void **ppv)
283 ShellItem *This = impl_from_IPersistIDList(iface);
284 return ShellItem_QueryInterface((IShellItem*)This, riid, ppv);
287 static ULONG WINAPI ShellItem_IPersistIDList_AddRef(IPersistIDList *iface)
289 ShellItem *This = impl_from_IPersistIDList(iface);
290 return ShellItem_AddRef((IShellItem*)This);
293 static ULONG WINAPI ShellItem_IPersistIDList_Release(IPersistIDList *iface)
295 ShellItem *This = impl_from_IPersistIDList(iface);
296 return ShellItem_Release((IShellItem*)This);
299 static HRESULT WINAPI ShellItem_IPersistIDList_GetClassID(IPersistIDList* iface,
302 ShellItem *This = impl_from_IPersistIDList(iface);
304 return ShellItem_GetClassID(This, pClassID);
307 static HRESULT WINAPI ShellItem_IPersistIDList_SetIDList(IPersistIDList* iface,
310 ShellItem *This = impl_from_IPersistIDList(iface);
311 LPITEMIDLIST new_pidl;
313 TRACE("(%p,%p)\n", This, pidl);
315 new_pidl = ILClone(pidl);
320 This->pidl = new_pidl;
324 return E_OUTOFMEMORY;
327 static HRESULT WINAPI ShellItem_IPersistIDList_GetIDList(IPersistIDList* iface,
330 ShellItem *This = impl_from_IPersistIDList(iface);
332 TRACE("(%p,%p)\n", This, ppidl);
334 *ppidl = ILClone(This->pidl);
338 return E_OUTOFMEMORY;
341 static const IPersistIDListVtbl ShellItem_IPersistIDList_Vtbl = {
342 ShellItem_IPersistIDList_QueryInterface,
343 ShellItem_IPersistIDList_AddRef,
344 ShellItem_IPersistIDList_Release,
345 ShellItem_IPersistIDList_GetClassID,
346 ShellItem_IPersistIDList_SetIDList,
347 ShellItem_IPersistIDList_GetIDList
351 HRESULT WINAPI IShellItem_Constructor(IUnknown *pUnkOuter, REFIID riid, void **ppv)
356 TRACE("(%p,%s)\n",pUnkOuter, debugstr_guid(riid));
360 if (pUnkOuter) return CLASS_E_NOAGGREGATION;
362 This = HeapAlloc(GetProcessHeap(), 0, sizeof(ShellItem));
363 This->lpIShellItemVtbl = &ShellItem_Vtbl;
366 This->lpIPersistIDListVtbl = &ShellItem_IPersistIDList_Vtbl;
368 ret = ShellItem_QueryInterface((IShellItem*)This, riid, ppv);
369 ShellItem_Release((IShellItem*)This);
374 HRESULT WINAPI SHCreateShellItem(LPCITEMIDLIST pidlParent,
375 IShellFolder *psfParent, LPCITEMIDLIST pidl, IShellItem **ppsi)
378 LPITEMIDLIST new_pidl;
381 TRACE("(%p,%p,%p,%p)\n", pidlParent, psfParent, pidl, ppsi);
387 else if (pidlParent || psfParent)
389 LPITEMIDLIST temp_parent=NULL;
392 IPersistFolder2* ppf2Parent;
394 if (FAILED(IPersistFolder2_QueryInterface(psfParent, &IID_IPersistFolder2, (void**)&ppf2Parent)))
396 FIXME("couldn't get IPersistFolder2 interface of parent\n");
397 return E_NOINTERFACE;
400 if (FAILED(IPersistFolder2_GetCurFolder(ppf2Parent, &temp_parent)))
402 FIXME("couldn't get parent PIDL\n");
403 IPersistFolder2_Release(ppf2Parent);
404 return E_NOINTERFACE;
407 pidlParent = temp_parent;
408 IPersistFolder2_Release(ppf2Parent);
411 new_pidl = ILCombine(pidlParent, pidl);
415 return E_OUTOFMEMORY;
419 new_pidl = ILClone(pidl);
421 return E_OUTOFMEMORY;
424 ret = IShellItem_Constructor(NULL, &IID_IShellItem, (void**)&This);
427 *ppsi = (IShellItem*)This;
428 This->pidl = new_pidl;
438 HRESULT WINAPI SHCreateItemFromParsingName(PCWSTR pszPath,
439 IBindCtx *pbc, REFIID riid, void **ppv)
446 ret = SHParseDisplayName(pszPath, pbc, &pidl, 0, NULL);
450 ret = IShellItem_Constructor(NULL, riid, (void**)&This);
465 HRESULT WINAPI SHCreateItemFromIDList(PCIDLIST_ABSOLUTE pidl, REFIID riid, void **ppv)
473 ret = IShellItem_Constructor(NULL, riid, ppv);
476 psiimpl = (ShellItem*)*ppv;
477 psiimpl->pidl = ILClone(pidl);
483 HRESULT WINAPI SHGetItemFromDataObject(IDataObject *pdtobj,
484 DATAOBJ_GET_ITEM_FLAGS dwFlags, REFIID riid, void **ppv)
490 TRACE("%p, %x, %s, %p\n", pdtobj, dwFlags, debugstr_guid(riid), ppv);
495 fmt.cfFormat = RegisterClipboardFormatW(CFSTR_SHELLIDLISTW);
497 fmt.dwAspect = DVASPECT_CONTENT;
499 fmt.tymed = TYMED_HGLOBAL;
501 ret = IDataObject_GetData(pdtobj, &fmt, &medium);
504 LPIDA pida = GlobalLock(medium.u.hGlobal);
506 if((pida->cidl > 1 && !(dwFlags & DOGIF_ONLY_IF_ONE)) ||
511 /* Get the first pidl (parent + child1) */
512 pidl = ILCombine((LPCITEMIDLIST) ((LPBYTE)pida+pida->aoffset[0]),
513 (LPCITEMIDLIST) ((LPBYTE)pida+pida->aoffset[1]));
515 ret = SHCreateItemFromIDList(pidl, riid, ppv);
523 GlobalUnlock(medium.u.hGlobal);
524 GlobalFree(medium.u.hGlobal);
527 if(FAILED(ret) && !(dwFlags & DOGIF_NO_HDROP))
529 TRACE("Attempting to fall back on CF_HDROP.\n");
531 fmt.cfFormat = CF_HDROP;
533 fmt.dwAspect = DVASPECT_CONTENT;
535 fmt.tymed = TYMED_HGLOBAL;
537 ret = IDataObject_GetData(pdtobj, &fmt, &medium);
540 DROPFILES *df = GlobalLock(medium.u.hGlobal);
541 LPBYTE files = (LPBYTE)df + df->pFiles;
542 BOOL multiple_files = FALSE;
547 WCHAR filename[MAX_PATH];
548 PCSTR first_file = (PCSTR)files;
549 if(*(files + lstrlenA(first_file) + 1) != 0)
550 multiple_files = TRUE;
552 if( !(multiple_files && (dwFlags & DOGIF_ONLY_IF_ONE)) )
554 MultiByteToWideChar(CP_ACP, 0, first_file, -1, filename, MAX_PATH);
555 ret = SHCreateItemFromParsingName(filename, NULL, riid, ppv);
560 PCWSTR first_file = (PCWSTR)files;
561 if(*((PCWSTR)files + lstrlenW(first_file) + 1) != 0)
562 multiple_files = TRUE;
564 if( !(multiple_files && (dwFlags & DOGIF_ONLY_IF_ONE)) )
565 ret = SHCreateItemFromParsingName(first_file, NULL, riid, ppv);
568 GlobalUnlock(medium.u.hGlobal);
569 GlobalFree(medium.u.hGlobal);
573 if(FAILED(ret) && !(dwFlags & DOGIF_NO_URL))
575 FIXME("Failed to create item, should try CF_URL.\n");
581 HRESULT WINAPI SHGetItemFromObject(IUnknown *punk, REFIID riid, void **ppv)
586 ret = SHGetIDListFromObject(punk, &pidl);
589 ret = SHCreateItemFromIDList(pidl, riid, ppv);
596 /*************************************************************************
597 * IShellItemArray implementation
600 const IShellItemArrayVtbl *lpVtbl;
605 } IShellItemArrayImpl;
607 static HRESULT WINAPI IShellItemArray_fnQueryInterface(IShellItemArray *iface,
611 IShellItemArrayImpl *This = (IShellItemArrayImpl *)iface;
612 TRACE("%p (%s, %p)\n", This, shdebugstr_guid(riid), ppvObject);
615 if(IsEqualIID(riid, &IID_IShellItemArray) ||
616 IsEqualIID(riid, &IID_IUnknown))
623 IUnknown_AddRef((IUnknown*)*ppvObject);
627 return E_NOINTERFACE;
630 static ULONG WINAPI IShellItemArray_fnAddRef(IShellItemArray *iface)
632 IShellItemArrayImpl *This = (IShellItemArrayImpl *)iface;
633 LONG ref = InterlockedIncrement(&This->ref);
634 TRACE("%p - ref %d\n", This, ref);
639 static ULONG WINAPI IShellItemArray_fnRelease(IShellItemArray *iface)
641 IShellItemArrayImpl *This = (IShellItemArrayImpl *)iface;
642 LONG ref = InterlockedDecrement(&This->ref);
643 TRACE("%p - ref %d\n", This, ref);
650 for(i = 0; i < This->item_count; i++)
651 IShellItem_Release(This->array[i]);
653 HeapFree(GetProcessHeap(), 0, This->array);
654 HeapFree(GetProcessHeap(), 0, This);
661 static HRESULT WINAPI IShellItemArray_fnBindToHandler(IShellItemArray *iface,
667 IShellItemArrayImpl *This = (IShellItemArrayImpl *)iface;
668 FIXME("Stub: %p (%p, %s, %s, %p)\n",
669 This, pbc, shdebugstr_guid(bhid), shdebugstr_guid(riid), ppvOut);
674 static HRESULT WINAPI IShellItemArray_fnGetPropertyStore(IShellItemArray *iface,
675 GETPROPERTYSTOREFLAGS flags,
679 IShellItemArrayImpl *This = (IShellItemArrayImpl *)iface;
680 FIXME("Stub: %p (%x, %s, %p)\n", This, flags, shdebugstr_guid(riid), ppv);
685 static HRESULT WINAPI IShellItemArray_fnGetPropertyDescriptionList(IShellItemArray *iface,
686 REFPROPERTYKEY keyType,
690 IShellItemArrayImpl *This = (IShellItemArrayImpl *)iface;
691 FIXME("Stub: %p (%p, %s, %p)\n",
692 This, keyType, shdebugstr_guid(riid), ppv);
697 static HRESULT WINAPI IShellItemArray_fnGetAttributes(IShellItemArray *iface,
698 SIATTRIBFLAGS AttribFlags,
700 SFGAOF *psfgaoAttribs)
702 IShellItemArrayImpl *This = (IShellItemArrayImpl *)iface;
703 FIXME("Stub: %p (%x, %x, %p)\n", This, AttribFlags, sfgaoMask, psfgaoAttribs);
708 static HRESULT WINAPI IShellItemArray_fnGetCount(IShellItemArray *iface,
711 IShellItemArrayImpl *This = (IShellItemArrayImpl *)iface;
712 TRACE("%p (%p)\n", This, pdwNumItems);
714 *pdwNumItems = This->item_count;
719 static HRESULT WINAPI IShellItemArray_fnGetItemAt(IShellItemArray *iface,
723 IShellItemArrayImpl *This = (IShellItemArrayImpl *)iface;
724 TRACE("%p (%x, %p)\n", This, dwIndex, ppsi);
727 if(dwIndex + 1 > This->item_count)
730 *ppsi = This->array[dwIndex];
731 IShellItem_AddRef(*ppsi);
736 static HRESULT WINAPI IShellItemArray_fnEnumItems(IShellItemArray *iface,
737 IEnumShellItems **ppenumShellItems)
739 IShellItemArrayImpl *This = (IShellItemArrayImpl *)iface;
740 FIXME("Stub: %p (%p)\n", This, ppenumShellItems);
745 static const IShellItemArrayVtbl vt_IShellItemArray = {
746 IShellItemArray_fnQueryInterface,
747 IShellItemArray_fnAddRef,
748 IShellItemArray_fnRelease,
749 IShellItemArray_fnBindToHandler,
750 IShellItemArray_fnGetPropertyStore,
751 IShellItemArray_fnGetPropertyDescriptionList,
752 IShellItemArray_fnGetAttributes,
753 IShellItemArray_fnGetCount,
754 IShellItemArray_fnGetItemAt,
755 IShellItemArray_fnEnumItems
758 static HRESULT IShellItemArray_Constructor(IUnknown *pUnkOuter, REFIID riid, void **ppv)
760 IShellItemArrayImpl *This;
763 TRACE("(%p, %s, %p)\n",pUnkOuter, debugstr_guid(riid), ppv);
766 return CLASS_E_NOAGGREGATION;
768 This = HeapAlloc(GetProcessHeap(), 0, sizeof(IShellItemArrayImpl));
770 return E_OUTOFMEMORY;
773 This->lpVtbl = &vt_IShellItemArray;
775 This->item_count = 0;
777 ret = IShellItemArray_QueryInterface((IShellItemArray*)This, riid, ppv);
778 IShellItemArray_Release((IShellItemArray*)This);
783 HRESULT WINAPI SHCreateShellItemArray(PCIDLIST_ABSOLUTE pidlParent,
786 PCUITEMID_CHILD_ARRAY ppidl,
787 IShellItemArray **ppsiItemArray)
789 IShellItemArrayImpl *This;
791 HRESULT ret = E_FAIL;
794 TRACE("%p, %p, %d, %p, %p\n", pidlParent, psf, cidl, ppidl, ppsiItemArray);
796 if(!pidlParent && !psf)
802 array = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cidl*sizeof(IShellItem*));
804 return E_OUTOFMEMORY;
806 for(i = 0; i < cidl; i++)
808 ret = SHCreateShellItem(pidlParent, psf, ppidl[i], &array[i]);
809 if(FAILED(ret)) break;
814 ret = IShellItemArray_Constructor(NULL, &IID_IShellItemArray, (void**)&This);
818 This->item_count = cidl;
819 *ppsiItemArray = (IShellItemArray*)This;
825 /* Something failed, clean up. */
826 for(i = 0; i < cidl; i++)
827 if(array[i]) IShellItem_Release(array[i]);
828 HeapFree(GetProcessHeap(), 0, array);
829 *ppsiItemArray = NULL;
833 HRESULT WINAPI SHCreateShellItemArrayFromShellItem(IShellItem *psi, REFIID riid, void **ppv)
835 IShellItemArrayImpl *This;
839 TRACE("%p, %s, %p\n", psi, shdebugstr_guid(riid), ppv);
841 array = HeapAlloc(GetProcessHeap(), 0, sizeof(IShellItem*));
843 return E_OUTOFMEMORY;
845 ret = IShellItemArray_Constructor(NULL, riid, (void**)&This);
849 IShellItem_AddRef(psi);
851 This->item_count = 1;
856 HeapFree(GetProcessHeap(), 0, array);
863 HRESULT WINAPI SHCreateShellItemArrayFromDataObject(IDataObject *pdo, REFIID riid, void **ppv)
865 IShellItemArray *psia;
870 TRACE("%p, %s, %p\n", pdo, shdebugstr_guid(riid), ppv);
877 fmt.cfFormat = RegisterClipboardFormatW(CFSTR_SHELLIDLISTW);
879 fmt.dwAspect = DVASPECT_CONTENT;
881 fmt.tymed = TYMED_HGLOBAL;
883 ret = IDataObject_GetData(pdo, &fmt, &medium);
886 LPIDA pida = GlobalLock(medium.u.hGlobal);
887 LPCITEMIDLIST parent_pidl;
888 LPCITEMIDLIST *children;
890 TRACE("Converting %d objects.\n", pida->cidl);
892 parent_pidl = (LPCITEMIDLIST) ((LPBYTE)pida+pida->aoffset[0]);
894 children = HeapAlloc(GetProcessHeap(), 0, sizeof(LPCITEMIDLIST)*pida->cidl);
895 for(i = 0; i < pida->cidl; i++)
896 children[i] = (LPCITEMIDLIST) ((LPBYTE)pida+pida->aoffset[i+1]);
898 ret = SHCreateShellItemArray(parent_pidl, NULL, pida->cidl, children, (IShellItemArray**)&psia);
900 HeapFree(GetProcessHeap(), 0, children);
902 GlobalUnlock(medium.u.hGlobal);
903 GlobalFree(medium.u.hGlobal);
908 ret = IShellItemArray_QueryInterface(psia, riid, ppv);
909 IShellItemArray_Release(psia);