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