Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

676 lines
16 KiB

//+-------------------------------------------------------------------------
//
// 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<IDataObject *>(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<IUnknown *>(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; i<cidl ; i++) {
cbTotal += ILGetSize(apidl[i]);
}
hida = GlobalAlloc(GPTR, cbTotal); // This MUST be GlobalAlloc!!!
if (hida)
{
LPIDA pida = (LPIDA)hida; // no need to lock
LPCITEMIDLIST pidlNext;
pida->cidl = 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;
}