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 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 173 CIDLDataObj::CIDLDataObj() 174 { 175 m_cfShellIDList = 0; 176 m_doasync = FALSE; 177 m_FailGetHDrop = false; 178 } 179 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 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 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 278 HRESULT WINAPI CIDLDataObj::GetDataHere(LPFORMATETC pformatetc, STGMEDIUM *pmedium) 279 { 280 FIXME("(%p)->()\n", this); 281 return E_NOTIMPL; 282 } 283 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 302 HRESULT WINAPI CIDLDataObj::GetCanonicalFormatEtc(LPFORMATETC pformatectIn, LPFORMATETC pformatetcOut) 303 { 304 //FIXME("(%p)->()\n", this); 305 return DATA_S_SAMEFORMATETC; 306 } 307 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 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 346 HRESULT WINAPI CIDLDataObj::DAdvise(FORMATETC *pformatetc, DWORD advf, IAdviseSink *pAdvSink, DWORD *pdwConnection) 347 { 348 return OLE_E_ADVISENOTSUPPORTED; 349 } 350 351 HRESULT WINAPI CIDLDataObj::DUnadvise(DWORD dwConnection) 352 { 353 return OLE_E_ADVISENOTSUPPORTED; 354 } 355 356 HRESULT WINAPI CIDLDataObj::EnumDAdvise(IEnumSTATDATA **ppenumAdvise) 357 { 358 return OLE_E_ADVISENOTSUPPORTED; 359 } 360 361 HRESULT WINAPI CIDLDataObj::GetAsyncMode(BOOL *pfIsOpAsync) 362 { 363 TRACE("(%p)->()\n", this); 364 *pfIsOpAsync = m_doasync; 365 return S_OK; 366 } 367 HRESULT WINAPI CIDLDataObj::InOperation(BOOL *pfInAsyncOp) 368 { 369 FIXME("(%p)->()\n", this); 370 return E_NOTIMPL; 371 } 372 HRESULT WINAPI CIDLDataObj::SetAsyncMode(BOOL fDoOpAsync) 373 { 374 TRACE("(%p)->()\n", this); 375 m_doasync = fDoOpAsync; 376 return S_OK; 377 } 378 379 HRESULT WINAPI CIDLDataObj::StartOperation(IBindCtx *pbcReserved) 380 { 381 TRACE("(%p)->()\n", this); 382 return E_NOTIMPL; 383 } 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 */ 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 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 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