xref: /reactos/dll/win32/shell32/CIDLDataObj.cpp (revision 63935f85)
1 /*
2  * PROJECT:     shell32
3  * LICENSE:     LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
4  * PURPOSE:     IEnumFORMATETC, IDataObject implementation
5  * COPYRIGHT:   Copyright 1998, 1999 <juergen.schmied@metronet.de>
6  *              Copyright 2019 Mark Jansen (mark.jansen@reactos.org)
7  */
8 
9 #include "precomp.h"
10 
11 WINE_DEFAULT_DEBUG_CHANNEL(shell);
12 
13 /***********************************************************************
14 *   IEnumFORMATETC implementation
15 */
16 
17 class IEnumFORMATETCImpl :
18     public CComObjectRootEx<CComMultiThreadModelNoCS>,
19     public IEnumFORMATETC
20 {
21 private:
22     UINT                        posFmt;
23     UINT                        countFmt;
24     LPFORMATETC                    pFmt;
25 public:
26     IEnumFORMATETCImpl();
27     ~IEnumFORMATETCImpl();
28     HRESULT WINAPI Initialize(UINT cfmt, const FORMATETC afmt[]);
29 
30     // *****************
31     STDMETHOD(Next)(ULONG celt, FORMATETC *rgelt, ULONG *pceltFethed) override;
32     STDMETHOD(Skip)(ULONG celt) override;
33     STDMETHOD(Reset)() override;
34     STDMETHOD(Clone)(LPENUMFORMATETC* ppenum) override;
35 
36 BEGIN_COM_MAP(IEnumFORMATETCImpl)
37     COM_INTERFACE_ENTRY_IID(IID_IEnumFORMATETC, IEnumFORMATETC)
38 END_COM_MAP()
39 };
40 
IEnumFORMATETCImpl()41 IEnumFORMATETCImpl::IEnumFORMATETCImpl()
42 {
43     posFmt = 0;
44     countFmt = 0;
45     pFmt = NULL;
46 }
47 
~IEnumFORMATETCImpl()48 IEnumFORMATETCImpl::~IEnumFORMATETCImpl()
49 {
50 }
51 
Initialize(UINT cfmt,const FORMATETC afmt[])52 HRESULT WINAPI IEnumFORMATETCImpl::Initialize(UINT cfmt, const FORMATETC afmt[])
53 {
54     DWORD size;
55 
56     size = cfmt * sizeof(FORMATETC);
57     countFmt = cfmt;
58     pFmt = (LPFORMATETC)SHAlloc(size);
59     if (pFmt == NULL)
60         return E_OUTOFMEMORY;
61 
62     memcpy(pFmt, afmt, size);
63     return S_OK;
64 }
65 
Next(ULONG celt,FORMATETC * rgelt,ULONG * pceltFethed)66 HRESULT WINAPI IEnumFORMATETCImpl::Next(ULONG celt, FORMATETC *rgelt, ULONG *pceltFethed)
67 {
68     UINT i;
69 
70     TRACE("(%p)->(%u,%p)\n", this, celt, rgelt);
71 
72     if (!pFmt)
73         return S_FALSE;
74     if (!rgelt)
75         return E_INVALIDARG;
76     if (pceltFethed)
77         *pceltFethed = 0;
78 
79     for (i = 0; posFmt < countFmt && celt > i; i++)
80     {
81         *rgelt++ = pFmt[posFmt++];
82     }
83 
84     if (pceltFethed)
85         *pceltFethed = i;
86 
87     return ((i == celt) ? S_OK : S_FALSE);
88 }
89 
Skip(ULONG celt)90 HRESULT WINAPI IEnumFORMATETCImpl::Skip(ULONG celt)
91 {
92     TRACE("(%p)->(num=%u)\n", this, celt);
93 
94     if (posFmt + celt >= countFmt)
95         return S_FALSE;
96     posFmt += celt;
97     return S_OK;
98 }
99 
Reset()100 HRESULT WINAPI IEnumFORMATETCImpl::Reset()
101 {
102     TRACE("(%p)->()\n", this);
103 
104     posFmt = 0;
105     return S_OK;
106 }
107 
Clone(LPENUMFORMATETC * ppenum)108 HRESULT WINAPI IEnumFORMATETCImpl::Clone(LPENUMFORMATETC* ppenum)
109 {
110     HRESULT hResult;
111 
112     TRACE("(%p)->(ppenum=%p)\n", this, ppenum);
113 
114     if (!ppenum) return E_INVALIDARG;
115     hResult = IEnumFORMATETC_Constructor(countFmt, pFmt, ppenum);
116     if (FAILED_UNEXPECTEDLY(hResult))
117         return hResult;
118     return (*ppenum)->Skip(posFmt);
119 }
120 
IEnumFORMATETC_Constructor(UINT cfmt,const FORMATETC afmt[],IEnumFORMATETC ** ppFormat)121 HRESULT IEnumFORMATETC_Constructor(UINT cfmt, const FORMATETC afmt[], IEnumFORMATETC **ppFormat)
122 {
123     return ShellObjectCreatorInit<IEnumFORMATETCImpl>(cfmt, afmt, IID_PPV_ARG(IEnumFORMATETC, ppFormat));
124 }
125 
126 
127 /***********************************************************************
128 *   IDataObject implementation
129 *   For now (2019-10-12) it's compatible with 2k3's data object
130 *   See shell32_apitest!CIDLData for changes between versions
131 */
132 
133 class CIDLDataObj :
134     public CComObjectRootEx<CComMultiThreadModelNoCS>,
135     public IDataObject,
136     public IAsyncOperation
137 {
138 private:
139     CSimpleArray<FORMATETC> m_Formats;
140     CSimpleArray<STGMEDIUM> m_Storage;
141     UINT m_cfShellIDList;
142     BOOL m_doasync;
143     bool m_FailGetHDrop;
144 public:
145     CIDLDataObj();
146     ~CIDLDataObj();
147     HRESULT WINAPI Initialize(HWND hwndOwner, PCIDLIST_ABSOLUTE pMyPidl, PCUIDLIST_RELATIVE_ARRAY apidlx, UINT cidlx, BOOL bAddAdditionalFormats);
148 
149     // *** IDataObject methods ***
150     STDMETHOD(GetData)(LPFORMATETC pformatetcIn, STGMEDIUM *pmedium) override;
151     STDMETHOD(GetDataHere)(LPFORMATETC pformatetc, STGMEDIUM *pmedium) override;
152     STDMETHOD(QueryGetData)(LPFORMATETC pformatetc) override;
153     STDMETHOD(GetCanonicalFormatEtc)(LPFORMATETC pformatectIn, LPFORMATETC pformatetcOut) override;
154     STDMETHOD(SetData)(LPFORMATETC pformatetc, STGMEDIUM *pmedium, BOOL fRelease) override;
155     STDMETHOD(EnumFormatEtc)(DWORD dwDirection, IEnumFORMATETC **ppenumFormatEtc) override;
156     STDMETHOD(DAdvise)(FORMATETC *pformatetc, DWORD advf, IAdviseSink *pAdvSink, DWORD *pdwConnection) override;
157     STDMETHOD(DUnadvise)(DWORD dwConnection) override;
158     STDMETHOD(EnumDAdvise)(IEnumSTATDATA **ppenumAdvise) override;
159 
160     // *** IAsyncOperation methods ***
161     STDMETHOD(SetAsyncMode)(BOOL fDoOpAsync) override;
162     STDMETHOD(GetAsyncMode)(BOOL *pfIsOpAsync) override;
163     STDMETHOD(StartOperation)(IBindCtx *pbcReserved) override;
164     STDMETHOD(InOperation)(BOOL *pfInAsyncOp) override;
165     STDMETHOD(EndOperation)(HRESULT hResult, IBindCtx *pbcReserved, DWORD dwEffects) override;
166 
167 BEGIN_COM_MAP(CIDLDataObj)
168     COM_INTERFACE_ENTRY_IID(IID_IDataObject, IDataObject)
169     COM_INTERFACE_ENTRY_IID(IID_IAsyncOperation,  IAsyncOperation)
170 END_COM_MAP()
171 };
172 
CIDLDataObj()173 CIDLDataObj::CIDLDataObj()
174 {
175     m_cfShellIDList = 0;
176     m_doasync = FALSE;
177     m_FailGetHDrop = false;
178 }
179 
~CIDLDataObj()180 CIDLDataObj::~CIDLDataObj()
181 {
182     TRACE(" destroying IDataObject(%p)\n", this);
183 
184     for (int n = 0; n < m_Storage.GetSize(); ++n)
185     {
186         ReleaseStgMedium(&m_Storage[n]);
187     }
188     m_Formats.RemoveAll();
189     m_Storage.RemoveAll();
190 }
191 
Initialize(HWND hwndOwner,PCIDLIST_ABSOLUTE pMyPidl,PCUIDLIST_RELATIVE_ARRAY apidlx,UINT cidlx,BOOL bAddAdditionalFormats)192 HRESULT WINAPI CIDLDataObj::Initialize(HWND hwndOwner, PCIDLIST_ABSOLUTE pMyPidl, PCUIDLIST_RELATIVE_ARRAY apidlx, UINT cidlx, BOOL bAddAdditionalFormats)
193 {
194     HGLOBAL hida = RenderSHELLIDLIST((LPITEMIDLIST)pMyPidl, (LPITEMIDLIST*)apidlx, cidlx);
195     if (!hida)
196     {
197         ERR("Failed to render " CFSTR_SHELLIDLISTA "\n");
198         return E_OUTOFMEMORY;
199     }
200 
201     m_cfShellIDList = RegisterClipboardFormatW(CFSTR_SHELLIDLIST);
202 
203     FORMATETC Format = { (CLIPFORMAT)m_cfShellIDList, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
204     STGMEDIUM medium = {0};
205     medium.tymed = TYMED_HGLOBAL;
206     medium.hGlobal = hida;
207     HRESULT hr = SetData(&Format, &medium, TRUE);
208     if (!FAILED_UNEXPECTEDLY(hr) && bAddAdditionalFormats)
209     {
210         /* The Windows default shell IDataObject::GetData fails with DV_E_CLIPFORMAT if the desktop is present.
211          * Windows does return HDROP in EnumFormatEtc and does not fail until GetData is called.
212          * Failing GetData causes 7-Zip 23.01 to not add its menu to the desktop folder. */
213         for (UINT i = 0; i < cidlx; ++i)
214         {
215             if (ILIsEmpty(apidlx[i]) && ILIsEmpty(pMyPidl))
216                 m_FailGetHDrop = true;
217         }
218 
219         Format.cfFormat = CF_HDROP;
220         medium.hGlobal = RenderHDROP((LPITEMIDLIST)pMyPidl, (LPITEMIDLIST*)apidlx, cidlx);
221         hr = SetData(&Format, &medium, TRUE);
222         if (FAILED_UNEXPECTEDLY(hr))
223             return hr;
224 
225         Format.cfFormat = RegisterClipboardFormatA(CFSTR_FILENAMEA);
226         medium.hGlobal = RenderFILENAMEA((LPITEMIDLIST)pMyPidl, (LPITEMIDLIST*)apidlx, cidlx);
227         hr = SetData(&Format, &medium, TRUE);
228         if (FAILED_UNEXPECTEDLY(hr))
229             return hr;
230 
231         Format.cfFormat = RegisterClipboardFormatW(CFSTR_FILENAMEW);
232         medium.hGlobal = RenderFILENAMEW((LPITEMIDLIST)pMyPidl, (LPITEMIDLIST*)apidlx, cidlx);
233         hr = SetData(&Format, &medium, TRUE);
234         if (FAILED_UNEXPECTEDLY(hr))
235             return hr;
236     }
237 
238     return hr;
239 }
240 
241 
GetData(LPFORMATETC pformatetcIn,STGMEDIUM * pmedium)242 HRESULT WINAPI CIDLDataObj::GetData(LPFORMATETC pformatetcIn, STGMEDIUM *pmedium)
243 {
244     if (TRACE_ON(shell))
245     {
246         char szTemp[256] = {0};
247         GetClipboardFormatNameA (pformatetcIn->cfFormat, szTemp, 256);
248         TRACE("(%p)->(%p %p format=%s)\n", this, pformatetcIn, pmedium, szTemp);
249     }
250     pmedium->hGlobal = NULL;
251     pmedium->pUnkForRelease = NULL;
252     for (int n = 0; n < m_Formats.GetSize(); ++n)
253     {
254         const FORMATETC& fmt = m_Formats[n];
255         if (fmt.cfFormat == pformatetcIn->cfFormat &&
256             fmt.dwAspect == pformatetcIn->dwAspect &&
257             fmt.tymed == pformatetcIn->tymed)
258         {
259             if (m_FailGetHDrop && fmt.cfFormat == CF_HDROP)
260                 return DV_E_CLIPFORMAT;
261 
262             if (pformatetcIn->tymed != TYMED_HGLOBAL)
263             {
264                 UNIMPLEMENTED;
265                 return E_INVALIDARG;
266             }
267             else
268             {
269                 *pmedium = m_Storage[n];
270                 return QueryInterface(IID_PPV_ARG(IUnknown, &pmedium->pUnkForRelease));
271             }
272         }
273     }
274 
275     return E_INVALIDARG;
276 }
277 
GetDataHere(LPFORMATETC pformatetc,STGMEDIUM * pmedium)278 HRESULT WINAPI CIDLDataObj::GetDataHere(LPFORMATETC pformatetc, STGMEDIUM *pmedium)
279 {
280     FIXME("(%p)->()\n", this);
281     return E_NOTIMPL;
282 }
283 
QueryGetData(LPFORMATETC pformatetc)284 HRESULT WINAPI CIDLDataObj::QueryGetData(LPFORMATETC pformatetc)
285 {
286     TRACE("(%p)->(fmt=0x%08x tym=0x%08x)\n", this, pformatetc->cfFormat, pformatetc->tymed);
287 
288     for (int n = 0; n < m_Formats.GetSize(); ++n)
289     {
290         const FORMATETC& fmt = m_Formats[n];
291         if (fmt.cfFormat == pformatetc->cfFormat &&
292             fmt.dwAspect == pformatetc->dwAspect &&
293             fmt.tymed == pformatetc->tymed)
294         {
295             return S_OK;
296         }
297     }
298 
299     return S_FALSE;
300 }
301 
GetCanonicalFormatEtc(LPFORMATETC pformatectIn,LPFORMATETC pformatetcOut)302 HRESULT WINAPI CIDLDataObj::GetCanonicalFormatEtc(LPFORMATETC pformatectIn, LPFORMATETC pformatetcOut)
303 {
304     //FIXME("(%p)->()\n", this);
305     return DATA_S_SAMEFORMATETC;
306 }
307 
SetData(LPFORMATETC pformatetc,STGMEDIUM * pmedium,BOOL fRelease)308 HRESULT WINAPI CIDLDataObj::SetData(LPFORMATETC pformatetc, STGMEDIUM *pmedium, BOOL fRelease)
309 {
310     if (!fRelease)
311         return E_INVALIDARG;
312 
313     for (int n = 0; n < m_Formats.GetSize(); ++n)
314     {
315         const FORMATETC& fmt = m_Formats[n];
316         if (fmt.cfFormat == pformatetc->cfFormat &&
317             fmt.dwAspect == pformatetc->dwAspect &&
318             fmt.tymed == pformatetc->tymed)
319         {
320             ReleaseStgMedium(&m_Storage[n]);
321             m_Storage[n] = *pmedium;
322             return S_OK;
323         }
324     }
325 
326     m_Formats.Add(*pformatetc);
327     m_Storage.Add(*pmedium);
328 
329     return S_OK;
330 }
331 
EnumFormatEtc(DWORD dwDirection,IEnumFORMATETC ** ppenumFormatEtc)332 HRESULT WINAPI CIDLDataObj::EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC **ppenumFormatEtc)
333 {
334     TRACE("(%p)->()\n", this);
335     *ppenumFormatEtc = NULL;
336 
337     /* only get data */
338     if (DATADIR_GET == dwDirection)
339     {
340         return IEnumFORMATETC_Constructor(m_Formats.GetSize(), m_Formats.GetData(), ppenumFormatEtc);
341     }
342 
343     return E_NOTIMPL;
344 }
345 
DAdvise(FORMATETC * pformatetc,DWORD advf,IAdviseSink * pAdvSink,DWORD * pdwConnection)346 HRESULT WINAPI CIDLDataObj::DAdvise(FORMATETC *pformatetc, DWORD advf, IAdviseSink *pAdvSink, DWORD *pdwConnection)
347 {
348     return OLE_E_ADVISENOTSUPPORTED;
349 }
350 
DUnadvise(DWORD dwConnection)351 HRESULT WINAPI CIDLDataObj::DUnadvise(DWORD dwConnection)
352 {
353     return OLE_E_ADVISENOTSUPPORTED;
354 }
355 
EnumDAdvise(IEnumSTATDATA ** ppenumAdvise)356 HRESULT WINAPI CIDLDataObj::EnumDAdvise(IEnumSTATDATA **ppenumAdvise)
357 {
358     return OLE_E_ADVISENOTSUPPORTED;
359 }
360 
GetAsyncMode(BOOL * pfIsOpAsync)361 HRESULT WINAPI CIDLDataObj::GetAsyncMode(BOOL *pfIsOpAsync)
362 {
363     TRACE("(%p)->()\n", this);
364     *pfIsOpAsync = m_doasync;
365     return S_OK;
366 }
InOperation(BOOL * pfInAsyncOp)367 HRESULT WINAPI CIDLDataObj::InOperation(BOOL *pfInAsyncOp)
368 {
369     FIXME("(%p)->()\n", this);
370     return E_NOTIMPL;
371 }
SetAsyncMode(BOOL fDoOpAsync)372 HRESULT WINAPI CIDLDataObj::SetAsyncMode(BOOL fDoOpAsync)
373 {
374     TRACE("(%p)->()\n", this);
375     m_doasync = fDoOpAsync;
376     return S_OK;
377 }
378 
StartOperation(IBindCtx * pbcReserved)379 HRESULT WINAPI CIDLDataObj::StartOperation(IBindCtx *pbcReserved)
380 {
381     TRACE("(%p)->()\n", this);
382     return E_NOTIMPL;
383 }
EndOperation(HRESULT hResult,IBindCtx * pbcReserved,DWORD dwEffects)384 HRESULT WINAPI CIDLDataObj::EndOperation(HRESULT hResult, IBindCtx *pbcReserved, DWORD dwEffects)
385 {
386     TRACE("(%p)->()\n", this);
387     return E_NOTIMPL;
388 }
389 
390 
391 
392 /**************************************************************************
393  *  IDataObject_Constructor
394  */
IDataObject_Constructor(HWND hwndOwner,PCIDLIST_ABSOLUTE pMyPidl,PCUIDLIST_RELATIVE_ARRAY apidl,UINT cidl,BOOL bExtendedObject,IDataObject ** dataObject)395 HRESULT IDataObject_Constructor(HWND hwndOwner, PCIDLIST_ABSOLUTE pMyPidl, PCUIDLIST_RELATIVE_ARRAY apidl, UINT cidl, BOOL bExtendedObject, IDataObject **dataObject)
396 {
397     if (!dataObject)
398         return E_INVALIDARG;
399     return ShellObjectCreatorInit<CIDLDataObj>(hwndOwner, pMyPidl, apidl, cidl, bExtendedObject, IID_PPV_ARG(IDataObject, dataObject));
400 }
401 
402 /*************************************************************************
403  * SHCreateDataObject            [SHELL32.@]
404  *
405  */
406 
SHCreateDataObject(PCIDLIST_ABSOLUTE pidlFolder,UINT cidl,PCUITEMID_CHILD_ARRAY apidl,IDataObject * pdtInner,REFIID riid,void ** ppv)407 HRESULT WINAPI SHCreateDataObject(PCIDLIST_ABSOLUTE pidlFolder, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, IDataObject *pdtInner, REFIID riid, void **ppv)
408 {
409     if (IsEqualIID(riid, IID_IDataObject))
410     {
411         if (pdtInner)
412             UNIMPLEMENTED;
413         return IDataObject_Constructor(NULL, pidlFolder, apidl, cidl, TRUE, (IDataObject **)ppv);
414     }
415     return E_FAIL;
416 }
417 
418 /*************************************************************************
419  * SHCreateFileDataObject       [SHELL32.740]
420  *
421  */
422 
SHCreateFileDataObject(PCIDLIST_ABSOLUTE pidlFolder,UINT cidl,PCUITEMID_CHILD_ARRAY apidl,IDataObject * pDataInner,IDataObject ** ppDataObj)423 HRESULT WINAPI SHCreateFileDataObject(PCIDLIST_ABSOLUTE pidlFolder, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, IDataObject* pDataInner, IDataObject** ppDataObj)
424 {
425     if (pDataInner)
426         UNIMPLEMENTED;
427     return IDataObject_Constructor(NULL, pidlFolder, apidl, cidl, TRUE, ppDataObj);
428 }
429