//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1997 - 1999 // // File: idldata.cpp // //-------------------------------------------------------------------------- #include "pch.h" #pragma hdrstop #include "fmtetc.h" #include "idldata.h" #include "shsemip.h" CLIPFORMAT CIDLData::m_rgcfGlobal[ICF_MAX] = { CF_HDROP, 0 }; const LARGE_INTEGER CIDLData::m_LargeIntZero; // // For those who prefer a function (rather than a ctor) to create an object, // this static function will return a pointer to the IDataObject interface. // If the function fails, no object is created. // HRESULT CIDLData::CreateInstance( IDataObject **ppOut, LPCITEMIDLIST pidlFolder, UINT cidl, LPCITEMIDLIST *apidl, IShellFolder *psfOwner, IDataObject *pdtInner ) { CIDLData *pidlData; HRESULT hr = CreateInstance(&pidlData, pidlFolder, cidl, apidl, psfOwner, pdtInner); if (SUCCEEDED(hr)) { pidlData->AddRef(); hr = pidlData->QueryInterface(IID_IDataObject, (void **)ppOut); pidlData->Release(); } else { *ppOut = NULL; } return hr; } // // For those who prefer a function (rather than a ctor) to create an object, // this static function will return a pointer to the CIDLData object. // If the function fails, no object is created. Note that the returned object // has a ref count of 0. Therefore, it acts as a normal C++ object. If you // want it to participate as a COM object, QI for IDataObject or use the // IDataObject version of CreateInstance() above. // HRESULT CIDLData::CreateInstance( CIDLData **ppOut, LPCITEMIDLIST pidlFolder, UINT cidl, LPCITEMIDLIST *apidl, IShellFolder *psfOwner, IDataObject *pdtInner ) { HRESULT hr = E_OUTOFMEMORY; CIDLData *pidlData = new CIDLData(pidlFolder, cidl, apidl, psfOwner, pdtInner); if (NULL != pidlData) { hr = pidlData->CtorResult(); if (SUCCEEDED(hr)) { *ppOut = pidlData; } else { delete pidlData; } } return hr; } CIDLData::CIDLData( LPCITEMIDLIST pidlFolder, UINT cidl, LPCITEMIDLIST *apidl, IShellFolder *psfOwner, // Optional. Default is NULL. IDataObject *pdtobjInner // Optional. Default is NULL. ) : m_cRef(0), m_hrCtor(NOERROR), m_psfOwner(NULL), m_dwOwnerData(0), m_pdtobjInner(pdtobjInner), m_bEnumFormatCalled(false) { // // Initialize the global clipboard formats. // InitializeClipboardFormats(); ZeroMemory(m_rgMedium, sizeof(m_rgMedium)); ZeroMemory(m_rgFmtEtc, sizeof(m_rgFmtEtc)); if (NULL != m_pdtobjInner) m_pdtobjInner->AddRef(); // // Empty array is valid input. // if (NULL != apidl) { HIDA hida = HIDA_Create(pidlFolder, cidl, apidl); if (NULL != hida) { m_hrCtor = DataObject_SetGlobal(static_cast(this), g_cfHIDA, hida); if (SUCCEEDED(m_hrCtor)) { if (NULL != psfOwner) { m_psfOwner = psfOwner; m_psfOwner->AddRef(); } } } else { m_hrCtor = E_OUTOFMEMORY; } } } CIDLData::~CIDLData( void ) { for (int i = 0; i < ARRAYSIZE(m_rgMedium); i++) { if (m_rgMedium[i].hGlobal) ReleaseStgMedium(&(m_rgMedium[i])); } if (NULL != m_psfOwner) m_psfOwner->Release(); if (NULL != m_pdtobjInner) m_pdtobjInner->Release(); } STDMETHODIMP CIDLData::QueryInterface( REFIID riid, void **ppv ) { static const QITAB qit[] = { QITABENT(CIDLData, IDataObject), { 0 }, }; return QISearch(this, qit, riid, ppv); } STDMETHODIMP_(ULONG) CIDLData::AddRef( void ) { return InterlockedIncrement(&m_cRef); } STDMETHODIMP_(ULONG) CIDLData::Release( void ) { if (InterlockedDecrement(&m_cRef)) return m_cRef; delete this; return 0; } HRESULT CIDLData::GetData( FORMATETC *pFmtEtc, STGMEDIUM *pMedium ) { HRESULT hr = E_INVALIDARG; pMedium->hGlobal = NULL; pMedium->pUnkForRelease = NULL; for (int i = 0; i < ARRAYSIZE(m_rgFmtEtc); i++) { if ((m_rgFmtEtc[i].cfFormat == pFmtEtc->cfFormat) && (m_rgFmtEtc[i].tymed & pFmtEtc->tymed) && (m_rgFmtEtc[i].dwAspect == pFmtEtc->dwAspect)) { *pMedium = m_rgMedium[i]; if (NULL != pMedium->hGlobal) { // // Indicate that the caller should not release hmem. // if (TYMED_HGLOBAL == pMedium->tymed) { InterlockedIncrement(&m_cRef); pMedium->pUnkForRelease = static_cast(this); return S_OK; } // // If the type is stream then clone the stream. // if (TYMED_ISTREAM == pMedium->tymed) { hr = CreateStreamOnHGlobal(NULL, TRUE, &(pMedium->pstm)); if (SUCCEEDED(hr)) { STGMEDIUM& medium = m_rgMedium[i]; STATSTG stat; // // Get the Current Stream size // hr = medium.pstm->Stat(&stat, STATFLAG_NONAME); if (SUCCEEDED(hr)) { // // Seek the source stream to the beginning. // medium.pstm->Seek(m_LargeIntZero, STREAM_SEEK_SET, NULL); // // Copy the entire source into the destination. // Since the destination stream is created using CreateStreamOnHGlobal, // it seek pointer is at the beginning. // hr = medium.pstm->CopyTo(pMedium->pstm, stat.cbSize, NULL, NULL); // // Before returning Set the destination seek pointer back at the beginning. // pMedium->pstm->Seek(m_LargeIntZero, STREAM_SEEK_SET, NULL); return hr; } else { hr = E_OUTOFMEMORY; } } } } } } if (E_INVALIDARG == hr && NULL != m_pdtobjInner) { hr = m_pdtobjInner->GetData(pFmtEtc, pMedium); } return hr; } STDMETHODIMP CIDLData::GetDataHere( FORMATETC *pFmtEtc, STGMEDIUM *pMedium ) { HRESULT hr = E_NOTIMPL; if (NULL != m_pdtobjInner) { hr = m_pdtobjInner->GetDataHere(pFmtEtc, pMedium); } return hr; } HRESULT CIDLData::QueryGetData( FORMATETC *pFmtEtc ) { HRESULT hr = S_FALSE; for (int i = 0; i < ARRAYSIZE(m_rgFmtEtc); i++) { if ((m_rgFmtEtc[i].cfFormat == pFmtEtc->cfFormat) && (m_rgFmtEtc[i].tymed & pFmtEtc->tymed) && (m_rgFmtEtc[i].dwAspect == pFmtEtc->dwAspect)) { return S_OK; } } if (NULL != m_pdtobjInner) { hr = m_pdtobjInner->QueryGetData(pFmtEtc); } return hr; } STDMETHODIMP CIDLData::GetCanonicalFormatEtc( FORMATETC *pFmtEtcIn, FORMATETC *pFmtEtcOut ) { // // This is the simplest implemtation. It means we always return // the data in the format requested. // return DATA_S_SAMEFORMATETC; } STDMETHODIMP CIDLData::SetData( FORMATETC *pFmtEtc, STGMEDIUM *pMedium, BOOL fRelease ) { HRESULT hr; TraceAssert(pFmtEtc->tymed == pMedium->tymed); if (fRelease) { int i; // // First add it if that format is already present // on a NULL medium (render on demand) // for (i = 0; i < ARRAYSIZE(m_rgFmtEtc); i++) { if ((m_rgFmtEtc[i].cfFormat == pFmtEtc->cfFormat) && (m_rgFmtEtc[i].tymed == pFmtEtc->tymed) && (m_rgFmtEtc[i].dwAspect == pFmtEtc->dwAspect)) { // // We are simply adding a format, ignore. // if (NULL == pMedium->hGlobal) { return S_OK; } // // If we are set twice on the same object // if (NULL != m_rgMedium[i].hGlobal) ReleaseStgMedium(&m_rgMedium[i]); m_rgMedium[i] = *pMedium; return S_OK; } } // // now look for a free slot // for (i = 0; i < ARRAYSIZE(m_rgFmtEtc); i++) { if (0 == m_rgFmtEtc[i].cfFormat) { // // found a free slot // m_rgMedium[i] = *pMedium; m_rgFmtEtc[i] = *pFmtEtc; return S_OK; } } // // fixed size table // hr = E_OUTOFMEMORY; } else hr = E_INVALIDARG; return hr; } STDMETHODIMP CIDLData::EnumFormatEtc( DWORD dwDirection, LPENUMFORMATETC *ppenumFormatEtc ) { HRESULT hr = NOERROR; // // If this is the first time, build the format list by calling // QueryGetData with each clipboard format. // if (!m_bEnumFormatCalled) { FORMATETC fmte = { 0, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; STGMEDIUM medium = { TYMED_HGLOBAL, NULL, NULL }; for (int i = 0; i < ARRAYSIZE(m_rgcfGlobal); i++) { fmte.cfFormat = m_rgcfGlobal[i]; if (S_OK == QueryGetData(&fmte)) { SetData(&fmte, &medium, TRUE); } } m_bEnumFormatCalled = true; } // // Get the number of formatetc // UINT cfmt; for (cfmt = 0; cfmt < ARRAYSIZE(m_rgFmtEtc); cfmt++) { if (0 == m_rgFmtEtc[cfmt].cfFormat) break; } /* return SHCreateStdEnumFmtEtcEx(cfmt, m_rgFmtEtc, m_pdtobjInner, ppenumFormatEtc); */ CEnumFormatEtc *pEnumFmtEtc = new CEnumFormatEtc(cfmt, m_rgFmtEtc); if (NULL != pEnumFmtEtc) { pEnumFmtEtc->AddRef(); // // Ask derived classes to add their formats. // hr = ProvideFormats(pEnumFmtEtc); if (SUCCEEDED(hr)) { hr = pEnumFmtEtc->QueryInterface(IID_IEnumFORMATETC, (void **)ppenumFormatEtc); } pEnumFmtEtc->Release(); } else hr = E_OUTOFMEMORY; return hr; } HRESULT CIDLData::ProvideFormats( CEnumFormatEtc *pEnumFmtEtc ) { // // Base class default does nothing. Our formats are added to the enumerator // in EnumFormatEtc(). // return NOERROR; } STDMETHODIMP CIDLData::DAdvise( FORMATETC *pFmtEtc, DWORD advf, LPADVISESINK pAdvSink, DWORD *pdwConnection ) { return OLE_E_ADVISENOTSUPPORTED; } STDMETHODIMP CIDLData::DUnadvise( DWORD dwConnection ) { return OLE_E_ADVISENOTSUPPORTED; } STDMETHODIMP CIDLData::EnumDAdvise( LPENUMSTATDATA *ppenumAdvise ) { return OLE_E_ADVISENOTSUPPORTED; } IShellFolder * CIDLData::GetFolder( void ) const { return m_psfOwner; } // // Clone DataObject only for MOVE/COPY operation // HRESULT CIDLData::Clone( UINT *acf, UINT ccf, IDataObject **ppdtobjOut ) { HRESULT hr = NOERROR; CIDLData *pidlData = new CIDLData(NULL, 0, NULL); if (NULL == pidlData) { hr = E_OUTOFMEMORY; } else { FORMATETC fmte = { 0, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; for (UINT i = 0; i < ccf; i++) { HRESULT hrT; STGMEDIUM medium; fmte.cfFormat = (CLIPFORMAT) acf[i]; hrT = GetData(&fmte, &medium); if (SUCCEEDED(hrT)) { HGLOBAL hmem; if (NULL != medium.pUnkForRelease) { // // We need to clone the hGlobal. // SIZE_T cbMem = GlobalSize(medium.hGlobal); hmem = GlobalAlloc(GPTR, cbMem); if (NULL != hmem) { hmemcpy((LPVOID)hmem, GlobalLock(medium.hGlobal), cbMem); GlobalUnlock(medium.hGlobal); } ReleaseStgMedium(&medium); } else { // // We don't need to clone the hGlobal. // hmem = medium.hGlobal; } if (hmem) DataObject_SetGlobal(*ppdtobjOut, (CLIPFORMAT)acf[i], hmem); } } } return hr; } HRESULT CIDLData::CloneForMoveCopy( IDataObject **ppdtobjOut ) { return E_NOTIMPL; /* UINT acf[] = { g_cfHIDA, g_cfOFFSETS, CF_HDROP, g_cfFileNameMapW, g_cfFileNameMap }; return Clone(acf, ARRAYSIZE(acf), ppdtobjOut); */ } #define RCF(x) (CLIPFORMAT) RegisterClipboardFormat(x) void CIDLData::InitializeClipboardFormats( void ) { if (g_cfHIDA == 0) { g_cfHIDA = RCF(CFSTR_SHELLIDLIST); g_cfOFFSETS = RCF(CFSTR_SHELLIDLISTOFFSET); g_cfNetResource = RCF(CFSTR_NETRESOURCES); g_cfFileContents = RCF(CFSTR_FILECONTENTS); // "FileContents" g_cfFileGroupDescriptorA = RCF(CFSTR_FILEDESCRIPTORA); // "FileGroupDescriptor" g_cfFileGroupDescriptorW = RCF(CFSTR_FILEDESCRIPTORW); // "FileGroupDescriptor" g_cfPrivateShellData = RCF(CFSTR_SHELLIDLISTP); g_cfFileName = RCF(CFSTR_FILENAMEA); // "FileName" g_cfFileNameW = RCF(CFSTR_FILENAMEW); // "FileNameW" g_cfFileNameMap = RCF(CFSTR_FILENAMEMAP); // "FileNameMap" g_cfFileNameMapW = RCF(CFSTR_FILENAMEMAPW); // "FileNameMapW" g_cfPrinterFriendlyName = RCF(CFSTR_PRINTERGROUP); g_cfHTML = RCF(TEXT("HTML Format")); g_cfPreferredDropEffect = RCF(CFSTR_PREFERREDDROPEFFECT); // "Preferred DropEffect" g_cfPerformedDropEffect = RCF(CFSTR_PERFORMEDDROPEFFECT); // "Performed DropEffect" g_cfLogicalPerformedDropEffect = RCF(CFSTR_LOGICALPERFORMEDDROPEFFECT); g_cfShellURL = RCF(CFSTR_SHELLURL); // "Uniform Resource Locator" g_cfInDragLoop = RCF(CFSTR_INDRAGLOOP); // "InShellDragLoop" g_cfDragContext = RCF(CFSTR_DRAGCONTEXT); // "DragContext" g_cfTargetCLSID = RCF(TEXT("TargetCLSID")); // who the drag drop went to } } // // This is normally a private shell function. // #define HIDA_GetPIDLItem(pida, i) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[i+1]) CIDLData::HIDA CIDLData::HIDA_Create( LPCITEMIDLIST pidlFolder, UINT cidl, LPCITEMIDLIST *apidl ) { HIDA hida; #if _MSC_VER == 1100 // Workaround code generate bug in VC5 X86 compiler (12/30 version). volatile #endif UINT i; UINT offset = sizeof(CIDA) + sizeof(UINT)*cidl; UINT cbTotal = offset + ILGetSize(pidlFolder); for (i=0; icidl = cidl; for (i=0, pidlNext=pidlFolder; ; pidlNext=apidl[i++]) { UINT cbSize = ILGetSize(pidlNext); pida->aoffset[i] = offset; MoveMemory(((LPBYTE)pida)+offset, pidlNext, cbSize); offset += cbSize; TraceAssert(ILGetSize(HIDA_GetPIDLItem(pida,i-1)) == cbSize); if (i==cidl) break; } TraceAssert(offset == cbTotal); } return hida; }