xref: /reactos/dll/win32/shell32/CIDLDataObj.cpp (revision 36873c49)
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     virtual HRESULT WINAPI Next(ULONG celt, FORMATETC *rgelt, ULONG *pceltFethed);
32     virtual HRESULT WINAPI Skip(ULONG celt);
33     virtual HRESULT WINAPI Reset();
34     virtual HRESULT WINAPI Clone(LPENUMFORMATETC* ppenum);
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     virtual HRESULT WINAPI GetData(LPFORMATETC pformatetcIn, STGMEDIUM *pmedium);
150     virtual HRESULT WINAPI GetDataHere(LPFORMATETC pformatetc, STGMEDIUM *pmedium);
151     virtual HRESULT WINAPI QueryGetData(LPFORMATETC pformatetc);
152     virtual HRESULT WINAPI GetCanonicalFormatEtc(LPFORMATETC pformatectIn, LPFORMATETC pformatetcOut);
153     virtual HRESULT WINAPI SetData(LPFORMATETC pformatetc, STGMEDIUM *pmedium, BOOL fRelease);
154     virtual HRESULT WINAPI EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC **ppenumFormatEtc);
155     virtual HRESULT WINAPI DAdvise(FORMATETC *pformatetc, DWORD advf, IAdviseSink *pAdvSink, DWORD *pdwConnection);
156     virtual HRESULT WINAPI DUnadvise(DWORD dwConnection);
157     virtual HRESULT WINAPI EnumDAdvise(IEnumSTATDATA **ppenumAdvise);
158 
159     // *** IAsyncOperation methods ***
160     virtual HRESULT WINAPI SetAsyncMode(BOOL fDoOpAsync);
161     virtual HRESULT WINAPI GetAsyncMode(BOOL *pfIsOpAsync);
162     virtual HRESULT WINAPI StartOperation(IBindCtx *pbcReserved);
163     virtual HRESULT WINAPI InOperation(BOOL *pfInAsyncOp);
164     virtual HRESULT WINAPI EndOperation(HRESULT hResult, IBindCtx *pbcReserved, DWORD dwEffects);
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     for (int n = 0; n < m_Formats.GetSize(); ++n)
240     {
241         const FORMATETC& fmt = m_Formats[n];
242         if (fmt.cfFormat == pformatetcIn->cfFormat &&
243             fmt.dwAspect == pformatetcIn->dwAspect &&
244             fmt.tymed == pformatetcIn->tymed)
245         {
246             if (pformatetcIn->tymed != TYMED_HGLOBAL)
247             {
248                 UNIMPLEMENTED;
249                 return E_INVALIDARG;
250             }
251             else
252             {
253                 *pmedium = m_Storage[n];
254                 return QueryInterface(IID_PPV_ARG(IUnknown, &pmedium->pUnkForRelease));
255             }
256         }
257     }
258 
259     return E_INVALIDARG;
260 }
261 
262 HRESULT WINAPI CIDLDataObj::GetDataHere(LPFORMATETC pformatetc, STGMEDIUM *pmedium)
263 {
264     FIXME("(%p)->()\n", this);
265     return E_NOTIMPL;
266 }
267 
268 HRESULT WINAPI CIDLDataObj::QueryGetData(LPFORMATETC pformatetc)
269 {
270     TRACE("(%p)->(fmt=0x%08x tym=0x%08x)\n", this, pformatetc->cfFormat, pformatetc->tymed);
271 
272     for (int n = 0; n < m_Formats.GetSize(); ++n)
273     {
274         const FORMATETC& fmt = m_Formats[n];
275         if (fmt.cfFormat == pformatetc->cfFormat &&
276             fmt.dwAspect == pformatetc->dwAspect &&
277             fmt.tymed == pformatetc->tymed)
278         {
279             return S_OK;
280         }
281     }
282 
283     return S_FALSE;
284 }
285 
286 HRESULT WINAPI CIDLDataObj::GetCanonicalFormatEtc(LPFORMATETC pformatectIn, LPFORMATETC pformatetcOut)
287 {
288     //FIXME("(%p)->()\n", this);
289     return DATA_S_SAMEFORMATETC;
290 }
291 
292 HRESULT WINAPI CIDLDataObj::SetData(LPFORMATETC pformatetc, STGMEDIUM *pmedium, BOOL fRelease)
293 {
294     if (!fRelease)
295         return E_INVALIDARG;
296 
297     for (int n = 0; n < m_Formats.GetSize(); ++n)
298     {
299         const FORMATETC& fmt = m_Formats[n];
300         if (fmt.cfFormat == pformatetc->cfFormat &&
301             fmt.dwAspect == pformatetc->dwAspect &&
302             fmt.tymed == pformatetc->tymed)
303         {
304             ReleaseStgMedium(&m_Storage[n]);
305             m_Storage[n] = *pmedium;
306             return S_OK;
307         }
308     }
309 
310     m_Formats.Add(*pformatetc);
311     m_Storage.Add(*pmedium);
312 
313     return S_OK;
314 }
315 
316 HRESULT WINAPI CIDLDataObj::EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC **ppenumFormatEtc)
317 {
318     TRACE("(%p)->()\n", this);
319     *ppenumFormatEtc = NULL;
320 
321     /* only get data */
322     if (DATADIR_GET == dwDirection)
323     {
324         return IEnumFORMATETC_Constructor(m_Formats.GetSize(), m_Formats.GetData(), ppenumFormatEtc);
325     }
326 
327     return E_NOTIMPL;
328 }
329 
330 HRESULT WINAPI CIDLDataObj::DAdvise(FORMATETC *pformatetc, DWORD advf, IAdviseSink *pAdvSink, DWORD *pdwConnection)
331 {
332     return OLE_E_ADVISENOTSUPPORTED;
333 }
334 
335 HRESULT WINAPI CIDLDataObj::DUnadvise(DWORD dwConnection)
336 {
337     return OLE_E_ADVISENOTSUPPORTED;
338 }
339 
340 HRESULT WINAPI CIDLDataObj::EnumDAdvise(IEnumSTATDATA **ppenumAdvise)
341 {
342     return OLE_E_ADVISENOTSUPPORTED;
343 }
344 
345 HRESULT WINAPI CIDLDataObj::GetAsyncMode(BOOL *pfIsOpAsync)
346 {
347     TRACE("(%p)->()\n", this);
348     *pfIsOpAsync = m_doasync;
349     return S_OK;
350 }
351 HRESULT WINAPI CIDLDataObj::InOperation(BOOL *pfInAsyncOp)
352 {
353     FIXME("(%p)->()\n", this);
354     return E_NOTIMPL;
355 }
356 HRESULT WINAPI CIDLDataObj::SetAsyncMode(BOOL fDoOpAsync)
357 {
358     TRACE("(%p)->()\n", this);
359     m_doasync = fDoOpAsync;
360     return S_OK;
361 }
362 
363 HRESULT WINAPI CIDLDataObj::StartOperation(IBindCtx *pbcReserved)
364 {
365     TRACE("(%p)->()\n", this);
366     return E_NOTIMPL;
367 }
368 HRESULT WINAPI CIDLDataObj::EndOperation(HRESULT hResult, IBindCtx *pbcReserved, DWORD dwEffects)
369 {
370     TRACE("(%p)->()\n", this);
371     return E_NOTIMPL;
372 }
373 
374 
375 
376 /**************************************************************************
377  *  IDataObject_Constructor
378  */
379 HRESULT IDataObject_Constructor(HWND hwndOwner, PCIDLIST_ABSOLUTE pMyPidl, PCUIDLIST_RELATIVE_ARRAY apidl, UINT cidl, BOOL bExtendedObject, IDataObject **dataObject)
380 {
381     if (!dataObject)
382         return E_INVALIDARG;
383     return ShellObjectCreatorInit<CIDLDataObj>(hwndOwner, pMyPidl, apidl, cidl, bExtendedObject, IID_PPV_ARG(IDataObject, dataObject));
384 }
385 
386 /*************************************************************************
387  * SHCreateDataObject            [SHELL32.@]
388  *
389  */
390 
391 HRESULT WINAPI SHCreateDataObject(PCIDLIST_ABSOLUTE pidlFolder, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, IDataObject *pdtInner, REFIID riid, void **ppv)
392 {
393     if (IsEqualIID(riid, IID_IDataObject))
394     {
395         if (pdtInner)
396             UNIMPLEMENTED;
397         return IDataObject_Constructor(NULL, pidlFolder, apidl, cidl, TRUE, (IDataObject **)ppv);
398     }
399     return E_FAIL;
400 }
401