xref: /reactos/dll/win32/shell32/CIDLDataObj.cpp (revision 49b2b1da)
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 
41 IEnumFORMATETCImpl::IEnumFORMATETCImpl()
42 {
43     posFmt = 0;
44     countFmt = 0;
45     pFmt = NULL;
46 }
47 
48 IEnumFORMATETCImpl::~IEnumFORMATETCImpl()
49 {
50 }
51 
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 
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 
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 
100 HRESULT WINAPI IEnumFORMATETCImpl::Reset()
101 {
102     TRACE("(%p)->()\n", this);
103 
104     posFmt = 0;
105     return S_OK;
106 }
107 
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 
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 public:
144     CIDLDataObj();
145     ~CIDLDataObj();
146     HRESULT WINAPI Initialize(HWND hwndOwner, PCIDLIST_ABSOLUTE pMyPidl, PCUIDLIST_RELATIVE_ARRAY apidlx, UINT cidlx, BOOL bAddAdditionalFormats);
147 
148     // *** IDataObject methods ***
149     STDMETHOD(GetData)(LPFORMATETC pformatetcIn, STGMEDIUM *pmedium) override;
150     STDMETHOD(GetDataHere)(LPFORMATETC pformatetc, STGMEDIUM *pmedium) override;
151     STDMETHOD(QueryGetData)(LPFORMATETC pformatetc) override;
152     STDMETHOD(GetCanonicalFormatEtc)(LPFORMATETC pformatectIn, LPFORMATETC pformatetcOut) override;
153     STDMETHOD(SetData)(LPFORMATETC pformatetc, STGMEDIUM *pmedium, BOOL fRelease) override;
154     STDMETHOD(EnumFormatEtc)(DWORD dwDirection, IEnumFORMATETC **ppenumFormatEtc) override;
155     STDMETHOD(DAdvise)(FORMATETC *pformatetc, DWORD advf, IAdviseSink *pAdvSink, DWORD *pdwConnection) override;
156     STDMETHOD(DUnadvise)(DWORD dwConnection) override;
157     STDMETHOD(EnumDAdvise)(IEnumSTATDATA **ppenumAdvise) override;
158 
159     // *** IAsyncOperation methods ***
160     STDMETHOD(SetAsyncMode)(BOOL fDoOpAsync) override;
161     STDMETHOD(GetAsyncMode)(BOOL *pfIsOpAsync) override;
162     STDMETHOD(StartOperation)(IBindCtx *pbcReserved) override;
163     STDMETHOD(InOperation)(BOOL *pfInAsyncOp) override;
164     STDMETHOD(EndOperation)(HRESULT hResult, IBindCtx *pbcReserved, DWORD dwEffects) override;
165 
166 BEGIN_COM_MAP(CIDLDataObj)
167     COM_INTERFACE_ENTRY_IID(IID_IDataObject, IDataObject)
168     COM_INTERFACE_ENTRY_IID(IID_IAsyncOperation,  IAsyncOperation)
169 END_COM_MAP()
170 };
171 
172 CIDLDataObj::CIDLDataObj()
173 {
174     m_cfShellIDList = 0;
175     m_doasync = FALSE;
176 }
177 
178 CIDLDataObj::~CIDLDataObj()
179 {
180     TRACE(" destroying IDataObject(%p)\n", this);
181 
182     for (int n = 0; n < m_Storage.GetSize(); ++n)
183     {
184         ReleaseStgMedium(&m_Storage[n]);
185     }
186     m_Formats.RemoveAll();
187     m_Storage.RemoveAll();
188 }
189 
190 HRESULT WINAPI CIDLDataObj::Initialize(HWND hwndOwner, PCIDLIST_ABSOLUTE pMyPidl, PCUIDLIST_RELATIVE_ARRAY apidlx, UINT cidlx, BOOL bAddAdditionalFormats)
191 {
192     HGLOBAL hida = RenderSHELLIDLIST((LPITEMIDLIST)pMyPidl, (LPITEMIDLIST*)apidlx, cidlx);
193     if (!hida)
194     {
195         ERR("Failed to render " CFSTR_SHELLIDLISTA "\n");
196         return E_OUTOFMEMORY;
197     }
198 
199     m_cfShellIDList = RegisterClipboardFormatW(CFSTR_SHELLIDLIST);
200 
201     FORMATETC Format = { (CLIPFORMAT)m_cfShellIDList, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
202     STGMEDIUM medium = {0};
203     medium.tymed = TYMED_HGLOBAL;
204     medium.hGlobal = hida;
205     HRESULT hr = SetData(&Format, &medium, TRUE);
206     if (!FAILED_UNEXPECTEDLY(hr) && bAddAdditionalFormats)
207     {
208         Format.cfFormat = CF_HDROP;
209         medium.hGlobal = RenderHDROP((LPITEMIDLIST)pMyPidl, (LPITEMIDLIST*)apidlx, cidlx);
210         hr = SetData(&Format, &medium, TRUE);
211         if (FAILED_UNEXPECTEDLY(hr))
212             return hr;
213 
214         Format.cfFormat = RegisterClipboardFormatA(CFSTR_FILENAMEA);
215         medium.hGlobal = RenderFILENAMEA((LPITEMIDLIST)pMyPidl, (LPITEMIDLIST*)apidlx, cidlx);
216         hr = SetData(&Format, &medium, TRUE);
217         if (FAILED_UNEXPECTEDLY(hr))
218             return hr;
219 
220         Format.cfFormat = RegisterClipboardFormatW(CFSTR_FILENAMEW);
221         medium.hGlobal = RenderFILENAMEW((LPITEMIDLIST)pMyPidl, (LPITEMIDLIST*)apidlx, cidlx);
222         hr = SetData(&Format, &medium, TRUE);
223         if (FAILED_UNEXPECTEDLY(hr))
224             return hr;
225     }
226 
227     return hr;
228 }
229 
230 
231 HRESULT WINAPI CIDLDataObj::GetData(LPFORMATETC pformatetcIn, STGMEDIUM *pmedium)
232 {
233     if (TRACE_ON(shell))
234     {
235         char szTemp[256] = {0};
236         GetClipboardFormatNameA (pformatetcIn->cfFormat, szTemp, 256);
237         TRACE("(%p)->(%p %p format=%s)\n", this, pformatetcIn, pmedium, szTemp);
238     }
239     pmedium->hGlobal = NULL;
240     pmedium->pUnkForRelease = NULL;
241     for (int n = 0; n < m_Formats.GetSize(); ++n)
242     {
243         const FORMATETC& fmt = m_Formats[n];
244         if (fmt.cfFormat == pformatetcIn->cfFormat &&
245             fmt.dwAspect == pformatetcIn->dwAspect &&
246             fmt.tymed == pformatetcIn->tymed)
247         {
248             if (pformatetcIn->tymed != TYMED_HGLOBAL)
249             {
250                 UNIMPLEMENTED;
251                 return E_INVALIDARG;
252             }
253             else
254             {
255                 *pmedium = m_Storage[n];
256                 return QueryInterface(IID_PPV_ARG(IUnknown, &pmedium->pUnkForRelease));
257             }
258         }
259     }
260 
261     return E_INVALIDARG;
262 }
263 
264 HRESULT WINAPI CIDLDataObj::GetDataHere(LPFORMATETC pformatetc, STGMEDIUM *pmedium)
265 {
266     FIXME("(%p)->()\n", this);
267     return E_NOTIMPL;
268 }
269 
270 HRESULT WINAPI CIDLDataObj::QueryGetData(LPFORMATETC pformatetc)
271 {
272     TRACE("(%p)->(fmt=0x%08x tym=0x%08x)\n", this, pformatetc->cfFormat, pformatetc->tymed);
273 
274     for (int n = 0; n < m_Formats.GetSize(); ++n)
275     {
276         const FORMATETC& fmt = m_Formats[n];
277         if (fmt.cfFormat == pformatetc->cfFormat &&
278             fmt.dwAspect == pformatetc->dwAspect &&
279             fmt.tymed == pformatetc->tymed)
280         {
281             return S_OK;
282         }
283     }
284 
285     return S_FALSE;
286 }
287 
288 HRESULT WINAPI CIDLDataObj::GetCanonicalFormatEtc(LPFORMATETC pformatectIn, LPFORMATETC pformatetcOut)
289 {
290     //FIXME("(%p)->()\n", this);
291     return DATA_S_SAMEFORMATETC;
292 }
293 
294 HRESULT WINAPI CIDLDataObj::SetData(LPFORMATETC pformatetc, STGMEDIUM *pmedium, BOOL fRelease)
295 {
296     if (!fRelease)
297         return E_INVALIDARG;
298 
299     for (int n = 0; n < m_Formats.GetSize(); ++n)
300     {
301         const FORMATETC& fmt = m_Formats[n];
302         if (fmt.cfFormat == pformatetc->cfFormat &&
303             fmt.dwAspect == pformatetc->dwAspect &&
304             fmt.tymed == pformatetc->tymed)
305         {
306             ReleaseStgMedium(&m_Storage[n]);
307             m_Storage[n] = *pmedium;
308             return S_OK;
309         }
310     }
311 
312     m_Formats.Add(*pformatetc);
313     m_Storage.Add(*pmedium);
314 
315     return S_OK;
316 }
317 
318 HRESULT WINAPI CIDLDataObj::EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC **ppenumFormatEtc)
319 {
320     TRACE("(%p)->()\n", this);
321     *ppenumFormatEtc = NULL;
322 
323     /* only get data */
324     if (DATADIR_GET == dwDirection)
325     {
326         return IEnumFORMATETC_Constructor(m_Formats.GetSize(), m_Formats.GetData(), ppenumFormatEtc);
327     }
328 
329     return E_NOTIMPL;
330 }
331 
332 HRESULT WINAPI CIDLDataObj::DAdvise(FORMATETC *pformatetc, DWORD advf, IAdviseSink *pAdvSink, DWORD *pdwConnection)
333 {
334     return OLE_E_ADVISENOTSUPPORTED;
335 }
336 
337 HRESULT WINAPI CIDLDataObj::DUnadvise(DWORD dwConnection)
338 {
339     return OLE_E_ADVISENOTSUPPORTED;
340 }
341 
342 HRESULT WINAPI CIDLDataObj::EnumDAdvise(IEnumSTATDATA **ppenumAdvise)
343 {
344     return OLE_E_ADVISENOTSUPPORTED;
345 }
346 
347 HRESULT WINAPI CIDLDataObj::GetAsyncMode(BOOL *pfIsOpAsync)
348 {
349     TRACE("(%p)->()\n", this);
350     *pfIsOpAsync = m_doasync;
351     return S_OK;
352 }
353 HRESULT WINAPI CIDLDataObj::InOperation(BOOL *pfInAsyncOp)
354 {
355     FIXME("(%p)->()\n", this);
356     return E_NOTIMPL;
357 }
358 HRESULT WINAPI CIDLDataObj::SetAsyncMode(BOOL fDoOpAsync)
359 {
360     TRACE("(%p)->()\n", this);
361     m_doasync = fDoOpAsync;
362     return S_OK;
363 }
364 
365 HRESULT WINAPI CIDLDataObj::StartOperation(IBindCtx *pbcReserved)
366 {
367     TRACE("(%p)->()\n", this);
368     return E_NOTIMPL;
369 }
370 HRESULT WINAPI CIDLDataObj::EndOperation(HRESULT hResult, IBindCtx *pbcReserved, DWORD dwEffects)
371 {
372     TRACE("(%p)->()\n", this);
373     return E_NOTIMPL;
374 }
375 
376 
377 
378 /**************************************************************************
379  *  IDataObject_Constructor
380  */
381 HRESULT IDataObject_Constructor(HWND hwndOwner, PCIDLIST_ABSOLUTE pMyPidl, PCUIDLIST_RELATIVE_ARRAY apidl, UINT cidl, BOOL bExtendedObject, IDataObject **dataObject)
382 {
383     if (!dataObject)
384         return E_INVALIDARG;
385     return ShellObjectCreatorInit<CIDLDataObj>(hwndOwner, pMyPidl, apidl, cidl, bExtendedObject, IID_PPV_ARG(IDataObject, dataObject));
386 }
387 
388 /*************************************************************************
389  * SHCreateDataObject            [SHELL32.@]
390  *
391  */
392 
393 HRESULT WINAPI SHCreateDataObject(PCIDLIST_ABSOLUTE pidlFolder, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, IDataObject *pdtInner, REFIID riid, void **ppv)
394 {
395     if (IsEqualIID(riid, IID_IDataObject))
396     {
397         if (pdtInner)
398             UNIMPLEMENTED;
399         return IDataObject_Constructor(NULL, pidlFolder, apidl, cidl, TRUE, (IDataObject **)ppv);
400     }
401     return E_FAIL;
402 }
403 
404 /*************************************************************************
405  * SHCreateFileDataObject       [SHELL32.740]
406  *
407  */
408 
409 HRESULT WINAPI SHCreateFileDataObject(PCIDLIST_ABSOLUTE pidlFolder, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, IDataObject* pDataInner, IDataObject** ppDataObj)
410 {
411     if (pDataInner)
412         UNIMPLEMENTED;
413     return IDataObject_Constructor(NULL, pidlFolder, apidl, cidl, TRUE, ppDataObj);
414 }
415