xref: /reactos/dll/win32/shell32/CShellItem.cpp (revision 84344399)
1 /*
2  * IShellItem implementation
3  *
4  * Copyright 2008 Vincent Povirk for CodeWeavers
5  * Copyright 2009 Andrew Hill
6  * Copyright 2013 Katayama Hirofumi MZ
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  */
22 
23 #include "precomp.h"
24 
25 WINE_DEFAULT_DEBUG_CHANNEL(shell);
26 
27 EXTERN_C HRESULT WINAPI SHCreateShellItem(PCIDLIST_ABSOLUTE pidlParent,
28     IShellFolder *psfParent, PCUITEMID_CHILD pidl, IShellItem **ppsi);
29 
30 CShellItem::CShellItem() :
31     m_pidl(NULL)
32 {
33 }
34 
35 CShellItem::~CShellItem()
36 {
37     ILFree(m_pidl);
38 }
39 
40 HRESULT CShellItem::get_parent_pidl(LPITEMIDLIST *parent_pidl)
41 {
42     *parent_pidl = ILClone(m_pidl);
43     if (*parent_pidl)
44     {
45         if (ILRemoveLastID(*parent_pidl))
46             return S_OK;
47         else
48         {
49             ILFree(*parent_pidl);
50             *parent_pidl = NULL;
51             return E_INVALIDARG;
52         }
53     }
54     else
55     {
56         *parent_pidl = NULL;
57         return E_OUTOFMEMORY;
58     }
59 }
60 
61 HRESULT CShellItem::get_parent_shellfolder(IShellFolder **ppsf)
62 {
63     HRESULT hr;
64     LPITEMIDLIST parent_pidl;
65     CComPtr<IShellFolder>        desktop;
66 
67     hr = get_parent_pidl(&parent_pidl);
68     if (SUCCEEDED(hr))
69     {
70         hr = SHGetDesktopFolder(&desktop);
71         if (SUCCEEDED(hr))
72             hr = desktop->BindToObject(parent_pidl, NULL, IID_PPV_ARG(IShellFolder, ppsf));
73         ILFree(parent_pidl);
74     }
75 
76     return hr;
77 }
78 
79 HRESULT CShellItem::get_shellfolder(IBindCtx *pbc, REFIID riid, void **ppvOut)
80 {
81     CComPtr<IShellFolder> psf;
82     CComPtr<IShellFolder> psfDesktop;
83     HRESULT ret;
84 
85     ret = SHGetDesktopFolder(&psfDesktop);
86     if (FAILED_UNEXPECTEDLY(ret))
87         return ret;
88 
89     if (_ILIsDesktop(m_pidl))
90         psf = psfDesktop;
91     else
92     {
93         ret = psfDesktop->BindToObject(m_pidl, pbc, IID_PPV_ARG(IShellFolder, &psf));
94         if (FAILED_UNEXPECTEDLY(ret))
95             return ret;
96     }
97 
98     return psf->QueryInterface(riid, ppvOut);
99 }
100 
101 HRESULT WINAPI CShellItem::BindToHandler(IBindCtx *pbc, REFGUID rbhid, REFIID riid, void **ppvOut)
102 {
103     HRESULT ret;
104     TRACE("(%p, %p,%s,%p,%p)\n", this, pbc, shdebugstr_guid(&rbhid), riid, ppvOut);
105 
106     *ppvOut = NULL;
107     if (IsEqualGUID(rbhid, BHID_SFObject))
108     {
109         return get_shellfolder(pbc, riid, ppvOut);
110     }
111     else if (IsEqualGUID(rbhid, BHID_SFUIObject))
112     {
113         CComPtr<IShellFolder> psf_parent;
114         if (_ILIsDesktop(m_pidl))
115             ret = SHGetDesktopFolder(&psf_parent);
116         else
117             ret = get_parent_shellfolder(&psf_parent);
118         if (FAILED_UNEXPECTEDLY(ret))
119             return ret;
120 
121         LPCITEMIDLIST pidl = ILFindLastID(m_pidl);
122         return psf_parent->GetUIObjectOf(NULL, 1, &pidl, riid, NULL, ppvOut);
123     }
124     else if (IsEqualGUID(rbhid, BHID_DataObject))
125     {
126         return BindToHandler(pbc, BHID_SFUIObject, IID_IDataObject, ppvOut);
127     }
128     else if (IsEqualGUID(rbhid, BHID_SFViewObject))
129     {
130         CComPtr<IShellFolder> psf;
131         ret = get_shellfolder(NULL, IID_PPV_ARG(IShellFolder, &psf));
132         if (FAILED_UNEXPECTEDLY(ret))
133             return ret;
134 
135         return psf->CreateViewObject(NULL, riid, ppvOut);
136     }
137 
138     FIXME("Unsupported BHID %s.\n", debugstr_guid(&rbhid));
139 
140     return MK_E_NOOBJECT;
141 }
142 
143 HRESULT WINAPI CShellItem::GetParent(IShellItem **ppsi)
144 {
145     HRESULT hr;
146     LPITEMIDLIST parent_pidl;
147 
148     TRACE("(%p,%p)\n", this, ppsi);
149 
150     hr = get_parent_pidl(&parent_pidl);
151     if (SUCCEEDED(hr))
152     {
153         hr = SHCreateShellItem(NULL, NULL, parent_pidl, ppsi);
154         ILFree(parent_pidl);
155     }
156 
157     return hr;
158 }
159 
160 HRESULT WINAPI CShellItem::GetDisplayName(SIGDN sigdnName, LPWSTR *ppszName)
161 {
162     return SHGetNameFromIDList(m_pidl, sigdnName, ppszName);
163 }
164 
165 HRESULT WINAPI CShellItem::GetAttributes(SFGAOF sfgaoMask, SFGAOF *psfgaoAttribs)
166 {
167     CComPtr<IShellFolder>        parent_folder;
168     LPCITEMIDLIST child_pidl;
169     HRESULT hr;
170 
171     TRACE("(%p,%x,%p)\n", this, sfgaoMask, psfgaoAttribs);
172 
173     if (_ILIsDesktop(m_pidl))
174         hr = SHGetDesktopFolder(&parent_folder);
175     else
176         hr = get_parent_shellfolder(&parent_folder);
177     if (FAILED_UNEXPECTEDLY(hr))
178         return hr;
179 
180     child_pidl = ILFindLastID(m_pidl);
181     *psfgaoAttribs = sfgaoMask;
182     hr = parent_folder->GetAttributesOf(1, &child_pidl, psfgaoAttribs);
183     *psfgaoAttribs &= sfgaoMask;
184 
185     if (FAILED_UNEXPECTEDLY(hr))
186         return hr;
187 
188     return (sfgaoMask == *psfgaoAttribs) ? S_OK : S_FALSE;
189 }
190 
191 HRESULT WINAPI CShellItem::Compare(IShellItem *oth, SICHINTF hint, int *piOrder)
192 {
193     HRESULT hr;
194     CComPtr<IPersistIDList>      pIDList;
195     CComPtr<IShellFolder>        psfDesktop;
196     LPITEMIDLIST pidl;
197 
198     TRACE("(%p,%p,%x,%p)\n", this, oth, hint, piOrder);
199 
200     if (piOrder == NULL || oth == NULL)
201         return E_POINTER;
202 
203     hr = oth->QueryInterface(IID_PPV_ARG(IPersistIDList, &pIDList));
204     if (SUCCEEDED(hr))
205     {
206         hr = pIDList->GetIDList(&pidl);
207         if (SUCCEEDED(hr))
208         {
209             hr = SHGetDesktopFolder(&psfDesktop);
210             if (SUCCEEDED(hr))
211             {
212                 hr = psfDesktop->CompareIDs(hint, m_pidl, pidl);
213                 *piOrder = (int)(short)SCODE_CODE(hr);
214             }
215             ILFree(pidl);
216         }
217     }
218 
219     if(FAILED(hr))
220         return hr;
221 
222     if(*piOrder)
223         return S_FALSE;
224     else
225         return S_OK;
226 }
227 
228 HRESULT WINAPI CShellItem::GetClassID(CLSID *pClassID)
229 {
230     TRACE("(%p,%p)\n", this, pClassID);
231 
232     *pClassID = CLSID_ShellItem;
233     return S_OK;
234 }
235 
236 HRESULT WINAPI CShellItem::SetIDList(PCIDLIST_ABSOLUTE pidlx)
237 {
238     LPITEMIDLIST new_pidl;
239 
240     TRACE("(%p,%p)\n", this, pidlx);
241 
242     new_pidl = ILClone(pidlx);
243     if (new_pidl)
244     {
245         ILFree(m_pidl);
246         m_pidl = new_pidl;
247         return S_OK;
248     }
249     else
250         return E_OUTOFMEMORY;
251 }
252 
253 HRESULT WINAPI CShellItem::GetIDList(PIDLIST_ABSOLUTE *ppidl)
254 {
255     TRACE("(%p,%p)\n", this, ppidl);
256 
257     *ppidl = ILClone(m_pidl);
258     if (*ppidl)
259         return S_OK;
260     else
261         return E_OUTOFMEMORY;
262 }
263 
264 HRESULT WINAPI SHCreateShellItem(PCIDLIST_ABSOLUTE pidlParent,
265     IShellFolder *psfParent, PCUITEMID_CHILD pidl, IShellItem **ppsi)
266 {
267     HRESULT hr;
268     CComPtr<IShellItem> newShellItem;
269     LPITEMIDLIST new_pidl;
270     CComPtr<IPersistIDList>            newPersistIDList;
271 
272     TRACE("(%p,%p,%p,%p)\n", pidlParent, psfParent, pidl, ppsi);
273 
274     *ppsi = NULL;
275 
276     if (!pidl)
277         return E_INVALIDARG;
278 
279     if (pidlParent || psfParent)
280     {
281         LPITEMIDLIST temp_parent = NULL;
282         if (!pidlParent)
283         {
284             CComPtr<IPersistFolder2>    ppf2Parent;
285 
286             if (FAILED(psfParent->QueryInterface(IID_PPV_ARG(IPersistFolder2, &ppf2Parent))))
287             {
288                 FIXME("couldn't get IPersistFolder2 interface of parent\n");
289                 return E_NOINTERFACE;
290             }
291 
292             if (FAILED(ppf2Parent->GetCurFolder(&temp_parent)))
293             {
294                 FIXME("couldn't get parent PIDL\n");
295                 return E_NOINTERFACE;
296             }
297 
298             pidlParent = temp_parent;
299         }
300 
301         new_pidl = ILCombine(pidlParent, pidl);
302         ILFree(temp_parent);
303 
304         if (!new_pidl)
305             return E_OUTOFMEMORY;
306     }
307     else
308     {
309         new_pidl = ILClone(pidl);
310         if (!new_pidl)
311             return E_OUTOFMEMORY;
312     }
313 
314     hr = CShellItem::_CreatorClass::CreateInstance(NULL, IID_PPV_ARG(IShellItem, &newShellItem));
315     if (FAILED(hr))
316     {
317         ILFree(new_pidl);
318         return hr;
319     }
320     hr = newShellItem->QueryInterface(IID_PPV_ARG(IPersistIDList, &newPersistIDList));
321     if (FAILED(hr))
322     {
323         ILFree(new_pidl);
324         return hr;
325     }
326     hr = newPersistIDList->SetIDList(new_pidl);
327     if (FAILED(hr))
328     {
329         ILFree(new_pidl);
330         return hr;
331     }
332     ILFree(new_pidl);
333 
334     *ppsi = newShellItem.Detach();
335 
336     return hr;
337 }
338 
339 class CShellItemArray :
340     public CComCoClass<CShellItemArray, &CLSID_NULL>,
341     public CComObjectRootEx<CComMultiThreadModelNoCS>,
342     public IShellItemArray
343 {
344     CIDA *m_pCIDA;
345     STGMEDIUM m_Medium;
346 
347 public:
348     CShellItemArray() : m_pCIDA(NULL)
349     {
350         m_Medium.tymed = TYMED_NULL;
351     }
352 
353     virtual ~CShellItemArray()
354     {
355         CDataObjectHIDA::DestroyCIDA(m_pCIDA, m_Medium);
356     }
357 
358     HRESULT Initialize(IDataObject *pdo)
359     {
360         return CDataObjectHIDA::CreateCIDA(pdo, &m_pCIDA, m_Medium);
361     }
362 
363     inline UINT GetCount() const { return m_pCIDA->cidl; }
364 
365     // IShellItemArray
366     STDMETHODIMP BindToHandler(IBindCtx *pbc, REFGUID rbhid, REFIID riid, void **ppv) override
367     {
368         UNIMPLEMENTED;
369         *ppv = NULL;
370         return E_NOTIMPL;
371     }
372 
373     STDMETHODIMP GetPropertyStore(GETPROPERTYSTOREFLAGS flags, REFIID riid, void **ppv) override
374     {
375         UNIMPLEMENTED;
376         *ppv = NULL;
377         return E_NOTIMPL;
378     }
379 
380     STDMETHODIMP GetPropertyDescriptionList(REFPROPERTYKEY keyType, REFIID riid, void **ppv) override
381     {
382         UNIMPLEMENTED;
383         *ppv = NULL;
384         return E_NOTIMPL;
385     }
386 
387     STDMETHODIMP GetAttributes(SIATTRIBFLAGS dwAttribFlags, SFGAOF sfgaoMask, SFGAOF *psfgaoAttribs) override
388     {
389         UNIMPLEMENTED;
390         *psfgaoAttribs = 0;
391         return E_NOTIMPL;
392     }
393 
394     STDMETHODIMP GetCount(DWORD*pCount) override
395     {
396         *pCount = m_pCIDA ? GetCount() : 0;
397         return S_OK;
398     }
399 
400     STDMETHODIMP GetItemAt(DWORD nIndex, IShellItem **ppItem) override
401     {
402         if (!ppItem)
403             return E_INVALIDARG;
404         *ppItem = NULL;
405         if (!m_pCIDA)
406             return E_UNEXPECTED;
407         if (nIndex >= GetCount())
408             return E_FAIL;
409         return SHCreateShellItem(HIDA_GetPIDLFolder(m_pCIDA), NULL,
410                                  HIDA_GetPIDLItem(m_pCIDA, nIndex), ppItem);
411     }
412 
413     STDMETHODIMP EnumItems(IEnumShellItems **ppESI) override
414     {
415         UNIMPLEMENTED;
416         *ppESI = NULL;
417         return E_NOTIMPL;
418     }
419 
420 DECLARE_NO_REGISTRY()
421 DECLARE_NOT_AGGREGATABLE(CShellItemArray)
422 
423 BEGIN_COM_MAP(CShellItemArray)
424     COM_INTERFACE_ENTRY_IID(IID_IShellItemArray, IShellItemArray)
425 END_COM_MAP()
426 };
427 
428 EXTERN_C HRESULT WINAPI
429 SHCreateShellItemArrayFromDataObject(_In_ IDataObject *pdo, _In_ REFIID riid, _Out_ void **ppv)
430 {
431     return ShellObjectCreatorInit<CShellItemArray>(pdo, riid, ppv);
432 }
433