[SDK][SHELL32] Augment the internally used IDataObject with some extra formats
[reactos.git] / dll / win32 / shell32 / CIDLDataObj.cpp
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 }