diff --git a/dll/win32/shell32/CShellItem.cpp b/dll/win32/shell32/CShellItem.cpp index b95eb4cf0f..44961cd08a 100644 --- a/dll/win32/shell32/CShellItem.cpp +++ b/dll/win32/shell32/CShellItem.cpp @@ -1,9 +1,9 @@ /* - * IShellItem implementation + * IShellItem and IShellItemArray implementation * * Copyright 2008 Vincent Povirk for CodeWeavers * Copyright 2009 Andrew Hill - * Copyright 2013 Katayama Hirofumi MZ + * Copyright 2013-2020 Katayama Hirofumi MZ * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -229,6 +229,9 @@ HRESULT WINAPI CShellItem::GetClassID(CLSID *pClassID) { TRACE("(%p,%p)\n", this, pClassID); + if (pClassID == NULL) + return E_POINTER; + *pClassID = CLSID_ShellItem; return S_OK; } @@ -239,6 +242,9 @@ HRESULT WINAPI CShellItem::SetIDList(PCIDLIST_ABSOLUTE pidlx) TRACE("(%p,%p)\n", this, pidlx); + if (!pidlx) + return E_POINTER; + new_pidl = ILClone(pidlx); if (new_pidl) { @@ -254,6 +260,9 @@ HRESULT WINAPI CShellItem::GetIDList(PIDLIST_ABSOLUTE *ppidl) { TRACE("(%p,%p)\n", this, ppidl); + if (!ppidl) + return E_POINTER; + *ppidl = ILClone(m_pidl); if (*ppidl) return S_OK; @@ -261,6 +270,7 @@ HRESULT WINAPI CShellItem::GetIDList(PIDLIST_ABSOLUTE *ppidl) return E_OUTOFMEMORY; } +EXTERN_C HRESULT WINAPI SHCreateShellItem(PCIDLIST_ABSOLUTE pidlParent, IShellFolder *psfParent, PCUITEMID_CHILD pidl, IShellItem **ppsi) { @@ -335,3 +345,486 @@ HRESULT WINAPI SHCreateShellItem(PCIDLIST_ABSOLUTE pidlParent, return hr; } + +EXTERN_C HRESULT WINAPI +SHCreateItemFromParsingName(PCWSTR pszPath, IBindCtx *pbc, REFIID riid, void **ppv) +{ + HRESULT ret; + LPITEMIDLIST pidl; + CComPtr pItem; + CComPtr pPersistIDList; + + if (!ppv) + return E_POINTER; + + *ppv = NULL; + + ret = SHParseDisplayName(pszPath, pbc, &pidl, 0, NULL); + if (SUCCEEDED(ret)) + { + ret = CShellItem::_CreatorClass::CreateInstance(NULL, IID_PPV_ARG(IShellItem, &pItem)); + if (SUCCEEDED(ret)) + { + ret = pItem->QueryInterface(IID_PPV_ARG(IPersistIDList, &pPersistIDList)); + if (SUCCEEDED(ret)) + { + ret = pPersistIDList->SetIDList(pidl); + if (SUCCEEDED(ret)) + { + *ppv = static_cast(pItem.Detach()); + } + } + } + ILFree(pidl); + } + + return ret; +} + +EXTERN_C HRESULT WINAPI +SHCreateItemFromIDList(PCIDLIST_ABSOLUTE pidl, REFIID riid, void **ppv) +{ + HRESULT ret; + CComPtr pItem; + CComPtr pPersistIDList; + + if (!pidl) + return E_POINTER; + + *ppv = NULL; + + ret = CShellItem::_CreatorClass::CreateInstance(NULL, IID_PPV_ARG(IShellItem, &pItem)); + if (SUCCEEDED(ret)) + { + ret = pItem->QueryInterface(IID_PPV_ARG(IPersistIDList, &pPersistIDList)); + if (SUCCEEDED(ret)) + { + ret = pPersistIDList->SetIDList(pidl); + if (SUCCEEDED(ret)) + { + *ppv = static_cast(pItem.Detach()); + } + } + } + + return ret; +} + +EXTERN_C HRESULT WINAPI +SHGetItemFromDataObject(IDataObject *pdtobj, DATAOBJ_GET_ITEM_FLAGS dwFlags, + REFIID riid, void **ppv) +{ + HRESULT ret; + FORMATETC fmt; + STGMEDIUM medium; + + TRACE("%p, %x, %s, %p\n", pdtobj, dwFlags, debugstr_guid(&riid), ppv); + + if (!pdtobj) + return E_POINTER; + + fmt.cfFormat = RegisterClipboardFormatW(CFSTR_SHELLIDLISTW); + fmt.ptd = NULL; + fmt.dwAspect = DVASPECT_CONTENT; + fmt.lindex = -1; + fmt.tymed = TYMED_HGLOBAL; + + ret = pdtobj->GetData(&fmt, &medium); + if (SUCCEEDED(ret)) + { + LPIDA pida = static_cast(GlobalLock(medium.hGlobal)); + + if ((pida->cidl > 1 && !(dwFlags & DOGIF_ONLY_IF_ONE)) || + pida->cidl == 1) + { + PCIDLIST_ABSOLUTE pidl_parent = HIDA_GetPIDLFolder(pida); + PCITEMID_CHILD pidl_child = HIDA_GetPIDLItem(pida, 0); + + // Get the first pidl (parent + child1) + CComHeapPtr pidl(ILCombine(pidl_parent, pidl_child)); + ret = SHCreateItemFromIDList(pidl, riid, ppv); + } + else + { + ret = E_FAIL; + } + + GlobalUnlock(medium.hGlobal); + GlobalFree(medium.hGlobal); + } + + if (FAILED(ret) && !(dwFlags & DOGIF_NO_HDROP)) + { + TRACE("Attempting to fall back on CF_HDROP.\n"); + + fmt.cfFormat = CF_HDROP; + fmt.ptd = NULL; + fmt.dwAspect = DVASPECT_CONTENT; + fmt.lindex = -1; + fmt.tymed = TYMED_HGLOBAL; + + ret = pdtobj->GetData(&fmt, &medium); + if (SUCCEEDED(ret)) + { + DROPFILES *df = reinterpret_cast(GlobalLock(medium.hGlobal)); + LPBYTE files = reinterpret_cast(df) + df->pFiles; + BOOL multiple_files = FALSE; + + ret = E_FAIL; + if (!df->fWide) + { + WCHAR filename[MAX_PATH]; + PCSTR first_file = reinterpret_cast(files); + if (*(files + lstrlenA(first_file) + 1) != 0) + multiple_files = TRUE; + + if (!(multiple_files && (dwFlags & DOGIF_ONLY_IF_ONE))) + { + MultiByteToWideChar(CP_ACP, 0, first_file, -1, filename, MAX_PATH); + ret = SHCreateItemFromParsingName(filename, NULL, riid, ppv); + } + } + else + { + PCWSTR first_file = reinterpret_cast(files); + if (*(reinterpret_cast(files) + lstrlenW(first_file) + 1) != 0) + multiple_files = TRUE; + + if (!(multiple_files && (dwFlags & DOGIF_ONLY_IF_ONE))) + ret = SHCreateItemFromParsingName(first_file, NULL, riid, ppv); + } + + GlobalUnlock(medium.hGlobal); + GlobalFree(medium.hGlobal); + } + } + + if (FAILED(ret) && !(dwFlags & DOGIF_NO_URL)) + { + FIXME("Failed to create item, should try CF_URL.\n"); + } + + return ret; +} + +EXTERN_C HRESULT WINAPI +SHGetIDListFromObject(IUnknown *punk, PIDLIST_ABSOLUTE *ppidl) +{ + HRESULT ret; + CComPtr ppersidl; + CComPtr ppf2; + CComPtr pdo; + CComPtr pfv; + CComPtr psi; + CComPtr psf; + + if (!punk) + return E_NOINTERFACE; + + *ppidl = NULL; + + // Try IPersistIDList + ret = punk->QueryInterface(IID_PPV_ARG(IPersistIDList, &ppersidl)); + if (SUCCEEDED(ret)) + { + TRACE("IPersistIDList (%p)\n", &ppersidl); + ret = ppersidl->GetIDList(ppidl); + if (SUCCEEDED(ret)) + return ret; + } + + // Try IPersistFolder2 + ret = punk->QueryInterface(IID_PPV_ARG(IPersistFolder2, &ppf2)); + if (SUCCEEDED(ret)) + { + TRACE("IPersistFolder2 (%p)\n", &ppf2); + ret = ppf2->GetCurFolder(ppidl); + if (SUCCEEDED(ret)) + return ret; + } + + // Try IDataObject + ret = punk->QueryInterface(IID_PPV_ARG(IDataObject, &pdo)); + if (SUCCEEDED(ret)) + { + TRACE("IDataObject (%p)\n", &pdo); + ret = SHGetItemFromDataObject( + pdo, DOGIF_ONLY_IF_ONE, IID_PPV_ARG(IShellItem, &psi)); + if (SUCCEEDED(ret)) + ret = SHGetIDListFromObject(static_cast(psi), ppidl); + if (SUCCEEDED(ret)) + return ret; + } + + // Try IFolderView + ret = punk->QueryInterface(IID_PPV_ARG(IFolderView, &pfv)); + if (SUCCEEDED(ret)) + { + TRACE("IFolderView (%p)\n", &pfv); + ret = pfv->GetFolder(IID_PPV_ARG(IShellFolder, &psf)); + if (SUCCEEDED(ret)) + { + // We might be able to get IPersistFolder2 from a shellfolder. + ret = SHGetIDListFromObject(static_cast(psf), ppidl); + } + return ret; + } + + return ret; +} + +EXTERN_C HRESULT WINAPI +SHGetItemFromObject(IUnknown *punk, REFIID riid, void **ppv) +{ + LPITEMIDLIST pidl; + HRESULT ret; + + ret = SHGetIDListFromObject(punk, &pidl); + if (SUCCEEDED(ret)) + { + ret = SHCreateItemFromIDList(pidl, riid, ppv); + ILFree(pidl); + } + + return ret; +} + +const CLSID CLSID_ShellItemArray = + { 0xB63EA76D, 0x1F85, 0x456F, {0xA1, 0x9C, 0x48, 0x15, 0x9E, 0xFA, 0x85, 0x8B} }; + +CShellItemArray::CShellItemArray() + : m_array(NULL) + , m_count(0) +{ +} + +CShellItemArray::~CShellItemArray() +{ + for (DWORD i = 0; i < m_count; i++) + { + if (m_array[i]) + m_array[i]->Release(); + } + CoTaskMemFree(m_array); +} + +STDMETHODIMP +CShellItemArray::BindToHandler( + IBindCtx *pbc, + REFGUID bhid, + REFIID riid, + void **ppvOut) +{ + FIXME("Stub: %p (%p, %s, %s, %p)\n", + this, pbc, shdebugstr_guid(&bhid), shdebugstr_guid(&riid), ppvOut); + return E_NOTIMPL; +} + +STDMETHODIMP +CShellItemArray::GetPropertyStore( + GETPROPERTYSTOREFLAGS flags, + REFIID riid, + void **ppv) +{ + FIXME("Stub: %p (%x, %s, %p)\n", this, flags, shdebugstr_guid(&riid), ppv); + return E_NOTIMPL; +} + +STDMETHODIMP +CShellItemArray::GetPropertyDescriptionList( + REFPROPERTYKEY keyType, + REFIID riid, + void **ppv) +{ + FIXME("Stub: %p (%p, %s, %p)\n", + this, keyType, shdebugstr_guid(&riid), ppv); + return E_NOTIMPL; +} + +STDMETHODIMP +CShellItemArray::GetAttributes( + SIATTRIBFLAGS AttribFlags, + SFGAOF sfgaoMask, + SFGAOF *psfgaoAttribs) +{ + FIXME("Stub: %p (%x, %x, %p)\n", this, AttribFlags, sfgaoMask, psfgaoAttribs); + return E_NOTIMPL; +} + +STDMETHODIMP +CShellItemArray::GetCount(DWORD *pdwNumItems) +{ + TRACE("%p (%p)\n", this, pdwNumItems); + + if (!pdwNumItems) + return E_POINTER; + + *pdwNumItems = m_count; + return S_OK; +} + +STDMETHODIMP +CShellItemArray::GetItemAt(DWORD dwIndex, IShellItem **ppsi) +{ + TRACE("%p (%x, %p)\n", this, dwIndex, ppsi); + + if (!ppsi) + return E_POINTER; + + if (dwIndex >= m_count) + return E_FAIL; + + m_array[dwIndex]->AddRef(); + *ppsi = m_array[dwIndex]; + + return S_OK; +} + +STDMETHODIMP +CShellItemArray::EnumItems(IEnumShellItems **ppenumShellItems) +{ + FIXME("Stub: %p (%p)\n", this, ppenumShellItems); + return E_NOTIMPL; +} + +EXTERN_C HRESULT WINAPI +SHCreateShellItemArray(PCIDLIST_ABSOLUTE pidlParent, + IShellFolder *psf, + UINT cidl, + PCUITEMID_CHILD_ARRAY ppidl, + IShellItemArray **ppsiItemArray) +{ + HRESULT ret = E_FAIL; + IShellItemArray *pArray; + IShellItem **array; + UINT i; + + TRACE("%p, %p, %d, %p, %p\n", pidlParent, psf, cidl, ppidl, ppsiItemArray); + + if (!pidlParent && !psf) + return E_POINTER; + + if (!ppsiItemArray) + return E_POINTER; + + if (!ppidl) + return E_INVALIDARG; + + array = static_cast(CoTaskMemAlloc(cidl * sizeof(IShellItem *))); + if (!array) + return E_OUTOFMEMORY; + + for (i = 0; i < cidl; i++) + { + ret = SHCreateShellItem(pidlParent, psf, ppidl[i], &array[i]); + if (FAILED(ret)) + break; + } + + if (SUCCEEDED(ret)) + { + ret = CShellItemArray::_CreatorClass::CreateInstance(NULL, IID_PPV_ARG(IShellItemArray, &pArray)); + if (SUCCEEDED(ret)) + { + CShellItemArray *ary = static_cast(pArray); + ary->m_array = array; + ary->m_count = cidl; + *ppsiItemArray = pArray; + return ret; + } + } + + // failed. clean up + for (i = 0; i < cidl; i++) + if (array[i]) array[i]->Release(); + CoTaskMemFree(array); + *ppsiItemArray = NULL; + return ret; +} + +EXTERN_C HRESULT WINAPI +SHCreateShellItemArrayFromShellItem(IShellItem *psi, REFIID riid, void **ppv) +{ + HRESULT ret; + CComPtr pArray; + IShellItem **array; + + TRACE("%p, %s, %p\n", psi, shdebugstr_guid(&riid), ppv); + + *ppv = NULL; + + array = static_cast(CoTaskMemAlloc(sizeof(IShellItem *))); + if (!array) + return E_OUTOFMEMORY; + + ret = CShellItemArray::_CreatorClass::CreateInstance(NULL, riid, reinterpret_cast(&pArray)); + if (SUCCEEDED(ret)) + { + array[0] = psi; + psi->AddRef(); + pArray->m_array = array; + pArray->m_count = 1; + *ppv = static_cast(pArray.Detach()); + } + else + { + CoTaskMemFree(array); + } + + return ret; +} + +EXTERN_C HRESULT WINAPI +SHCreateShellItemArrayFromDataObject(IDataObject *pdo, REFIID riid, void **ppv) +{ + HRESULT ret; + CComPtr pArray; + FORMATETC fmt; + STGMEDIUM medium; + UINT i; + + TRACE("%p, %s, %p\n", pdo, shdebugstr_guid(&riid), ppv); + + *ppv = NULL; + + if (!pdo) + return E_INVALIDARG; + + fmt.cfFormat = RegisterClipboardFormatW(CFSTR_SHELLIDLISTW); + fmt.ptd = NULL; + fmt.dwAspect = DVASPECT_CONTENT; + fmt.lindex = -1; + fmt.tymed = TYMED_HGLOBAL; + + ret = pdo->GetData(&fmt, &medium); + if (SUCCEEDED(ret)) + { + LPIDA pida = static_cast(GlobalLock(medium.hGlobal)); + + TRACE("Converting %d objects.\n", pida->cidl); + + PCIDLIST_ABSOLUTE parent_pidl = HIDA_GetPIDLFolder(pida); + + SIZE_T size = sizeof(LPCITEMIDLIST) * pida->cidl; + + CComHeapPtr + children(reinterpret_cast(CoTaskMemAlloc(size))); + + for (i = 0; i < pida->cidl; i++) + { + children[i] = HIDA_GetPIDLItem(pida, i); + } + + ret = SHCreateShellItemArray(parent_pidl, NULL, pida->cidl, children, &pArray); + + GlobalUnlock(medium.hGlobal); + GlobalFree(medium.hGlobal); + + if (SUCCEEDED(ret)) + { + ret = pArray->QueryInterface(riid, ppv); + } + } + + return ret; +} diff --git a/dll/win32/shell32/CShellItem.h b/dll/win32/shell32/CShellItem.h index 555501d8cd..8771c69817 100644 --- a/dll/win32/shell32/CShellItem.h +++ b/dll/win32/shell32/CShellItem.h @@ -1,8 +1,9 @@ /* - * IShellItem implementation + * IShellItem and IShellItemArray implementation * * Copyright 2008 Vincent Povirk for CodeWeavers * Copyright 2009 Andrew Hill + * Copyright 2020 Katayama Hirofumi MZ * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -61,4 +62,52 @@ BEGIN_COM_MAP(CShellItem) END_COM_MAP() }; +extern const CLSID CLSID_ShellItemArray; + +class CShellItemArray : + public CComCoClass, + public CComObjectRootEx, + public IShellItemArray +{ +private: + IShellItem **m_array; // allocated by CoTaskMemAlloc + DWORD m_count; + + friend HRESULT WINAPI + SHCreateShellItemArray(PCIDLIST_ABSOLUTE, + IShellFolder *, + UINT, + PCUITEMID_CHILD_ARRAY, + IShellItemArray **); + + friend HRESULT WINAPI + SHCreateShellItemArrayFromShellItem(IShellItem *, REFIID, void **); + +public: + CShellItemArray(); + +protected: + virtual ~CShellItemArray(); + +public: + // IShellItemArray interface + STDMETHODIMP BindToHandler(IBindCtx *pbc, REFGUID bhid, REFIID riid, void **ppvOut); + STDMETHODIMP GetPropertyStore(GETPROPERTYSTOREFLAGS flags, REFIID riid, void **ppv); + STDMETHODIMP GetPropertyDescriptionList(REFPROPERTYKEY keyType, REFIID riid, void **ppv); + STDMETHODIMP GetAttributes(SIATTRIBFLAGS AttribFlags, SFGAOF sfgaoMask, SFGAOF *psfgaoAttribs); + STDMETHODIMP GetCount(DWORD *pdwNumItems); + STDMETHODIMP GetItemAt(DWORD dwIndex, IShellItem **ppsi); + STDMETHODIMP EnumItems(IEnumShellItems **ppenumShellItems); + +public: + DECLARE_NO_REGISTRY() + DECLARE_NOT_AGGREGATABLE(CShellItemArray) + + DECLARE_PROTECT_FINAL_CONSTRUCT() + + BEGIN_COM_MAP(CShellItemArray) + COM_INTERFACE_ENTRY_IID(IID_IShellItemArray, IShellItemArray) + END_COM_MAP() +}; + #endif /* _SHELLITEM_H_ */ diff --git a/dll/win32/shell32/shell32.cpp b/dll/win32/shell32/shell32.cpp index c244477cf6..96e3532dc0 100644 --- a/dll/win32/shell32/shell32.cpp +++ b/dll/win32/shell32/shell32.cpp @@ -276,6 +276,7 @@ BEGIN_OBJECT_MAP(ObjectMap) OBJECT_ENTRY(CLSID_MyComputer, CDrivesFolder) OBJECT_ENTRY(CLSID_ShellDesktop, CDesktopFolder) OBJECT_ENTRY(CLSID_ShellItem, CShellItem) + OBJECT_ENTRY(CLSID_ShellItemArray, CShellItemArray) OBJECT_ENTRY(CLSID_ShellLink, CShellLink) OBJECT_ENTRY(CLSID_Shell, CShellDispatch) OBJECT_ENTRY(CLSID_DragDropHelper, CDropTargetHelper)