windows-nt/Source/XPSP1/NT/shell/shell32/idldata.cpp
2020-09-26 16:20:57 +08:00

443 lines
14 KiB
C++

#include "shellprv.h"
#pragma hdrstop
#include "bookmk.h"
#include "idldata.h"
#include "datautil.h"
#include <brfcasep.h>
// External prototypes
CLIPFORMAT g_acfIDLData[ICF_MAX] = { CF_HDROP, 0 };
#define RCF(x) (CLIPFORMAT) RegisterClipboardFormat(x)
STDAPI_(void) IDLData_InitializeClipboardFormats(void)
{
if (g_cfBriefObj == 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_cfFileNameA = RCF(CFSTR_FILENAMEA); // "FileName"
g_cfFileNameW = RCF(CFSTR_FILENAMEW); // "FileNameW"
g_cfFileNameMapA = 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); // "Logical Performed DropEffect"
g_cfPasteSucceeded = RCF(CFSTR_PASTESUCCEEDED); // "Paste Succeeded"
g_cfShellURL = RCF(CFSTR_SHELLURL); // "Uniform Resource Locator"
g_cfInDragLoop = RCF(CFSTR_INDRAGLOOP); // "InShellDragLoop"
g_cfDragContext = RCF(CFSTR_DRAGCONTEXT); // "DragContext"
g_cfTargetCLSID = RCF(CFSTR_TARGETCLSID); // "TargetCLSID", who the drag drop went to
g_cfEmbeddedObject = RCF(TEXT("Embedded Object"));
g_cfObjectDescriptor = RCF(TEXT("Object Descriptor"));
g_cfNotRecyclable = RCF(TEXT("NotRecyclable")); // This object is not recyclable in the recycle bin.
g_cfBriefObj = RCF(CFSTR_BRIEFOBJECT);
g_cfText = CF_TEXT;
g_cfUnicodeText = CF_UNICODETEXT;
g_cfDropEffectFolderList = RCF(CFSTR_DROPEFFECTFOLDERLIST);
g_cfAutoPlayHIDA = RCF(CFSTR_AUTOPLAY_SHELLIDLISTS);
}
}
STDMETHODIMP CIDLDataObj::QueryInterface(REFIID riid, void **ppvObj)
{
static const QITAB qit[] = {
QITABENT(CIDLDataObj, IDataObject),
QITABENT(CIDLDataObj, IAsyncOperation),
{ 0 },
};
return QISearch(this, qit, riid, ppvObj);
}
STDMETHODIMP_(ULONG) CIDLDataObj::AddRef()
{
return InterlockedIncrement(&_cRef);
}
STDMETHODIMP_(ULONG) CIDLDataObj::Release()
{
if (InterlockedDecrement(&_cRef))
return _cRef;
delete this;
return 0;
}
STDMETHODIMP CIDLDataObj::GetData(FORMATETC *pformatetcIn, STGMEDIUM *pmedium)
{
HRESULT hr = E_INVALIDARG;
pmedium->hGlobal = NULL;
pmedium->pUnkForRelease = NULL;
for (int i = 0; i < MAX_FORMATS; i++)
{
if ((_fmte[i].cfFormat == pformatetcIn->cfFormat) &&
(_fmte[i].tymed & pformatetcIn->tymed) &&
(_fmte[i].dwAspect == pformatetcIn->dwAspect))
{
*pmedium = _medium[i];
if (pmedium->hGlobal)
{
// Indicate that the caller should not release hmem.
if (pmedium->tymed == TYMED_HGLOBAL)
{
InterlockedIncrement(&_cRef);
pmedium->pUnkForRelease = SAFECAST(this, IDataObject *);
return S_OK;
}
// if the type is stream then clone the stream.
if (pmedium->tymed == TYMED_ISTREAM)
{
hr = CreateStreamOnHGlobal(NULL, TRUE, &pmedium->pstm);
if (SUCCEEDED(hr))
{
STATSTG stat;
// Get the Current Stream size
hr = _medium[i].pstm->Stat(&stat, STATFLAG_NONAME);
if (SUCCEEDED(hr))
{
// Seek the source stream to the beginning.
_medium[i].pstm->Seek(g_li0, 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[i].pstm->CopyTo(pmedium->pstm, stat.cbSize, NULL,NULL );
// Before returning Set the destination seek pointer back at the beginning.
pmedium->pstm->Seek(g_li0, STREAM_SEEK_SET, NULL);
// If this medium has a punk for release, make sure to add ref that...
pmedium->pUnkForRelease = _medium[i].pUnkForRelease;
if (pmedium->pUnkForRelease)
pmedium->pUnkForRelease->AddRef();
//Hoooh its done.
return hr;
}
else
{
hr = E_OUTOFMEMORY;
}
}
}
}
}
}
if (hr == E_INVALIDARG && _pdtInner)
hr = _pdtInner->GetData(pformatetcIn, pmedium);
return hr;
}
STDMETHODIMP CIDLDataObj::GetDataHere(FORMATETC *pformatetc, STGMEDIUM *pmedium)
{
#ifdef DEBUG
if (pformatetc->cfFormat<CF_MAX)
{
TraceMsg(TF_IDLIST, "CIDLDataObj::GetDataHere called with %x,%x,%x",
pformatetc->cfFormat, pformatetc->tymed, pmedium->tymed);
}
else
{
TCHAR szName[256];
GetClipboardFormatName(pformatetc->cfFormat, szName, ARRAYSIZE(szName));
TraceMsg(TF_IDLIST, "CIDLDataObj::GetDataHere called with %s,%x,%x",
szName, pformatetc->tymed, pmedium->tymed);
}
#endif
return _pdtInner ? _pdtInner->GetDataHere(pformatetc, pmedium) : E_NOTIMPL;
}
STDMETHODIMP CIDLDataObj::QueryGetData(FORMATETC *pformatetcIn)
{
#ifdef DEBUG
if (pformatetcIn->cfFormat<CF_MAX)
{
TraceMsg(TF_IDLIST, "CIDLDataObj::QueryGetData called with %x,%x",
pformatetcIn->cfFormat, pformatetcIn->tymed);
}
else
{
TCHAR szName[256];
GetClipboardFormatName(pformatetcIn->cfFormat, szName, ARRAYSIZE(szName));
TraceMsg(TF_IDLIST, "CIDLDataObj::QueryGetData called with %s,%x",
szName, pformatetcIn->tymed);
}
#endif
for (int i = 0; i < MAX_FORMATS; i++)
{
if ((_fmte[i].cfFormat == pformatetcIn->cfFormat) &&
(_fmte[i].tymed & pformatetcIn->tymed) &&
(_fmte[i].dwAspect == pformatetcIn->dwAspect))
return S_OK;
}
HRESULT hr = S_FALSE;
if (_pdtInner)
hr = _pdtInner->QueryGetData(pformatetcIn);
return hr;
}
STDMETHODIMP CIDLDataObj::GetCanonicalFormatEtc(FORMATETC *pformatetc, FORMATETC *pformatetcOut)
{
// This is the simplest implemtation. It means we always return
// the data in the format requested.
return DATA_S_SAMEFORMATETC;
}
STDMETHODIMP CIDLDataObj::SetData(FORMATETC *pformatetc, STGMEDIUM *pmedium, BOOL fRelease)
{
HRESULT hr;
ASSERT(pformatetc->tymed == pmedium->tymed);
if (fRelease)
{
// first add it if that format is already present
// on a NULL medium (render on demand)
for (int i = 0; i < MAX_FORMATS; i++)
{
if ((_fmte[i].cfFormat == pformatetc->cfFormat) &&
(_fmte[i].tymed == pformatetc->tymed) &&
(_fmte[i].dwAspect == pformatetc->dwAspect))
{
//
// We are simply adding a format, ignore.
//
if (pmedium->hGlobal == NULL)
return S_OK;
// if we are set twice on the same object
if (_medium[i].hGlobal)
ReleaseStgMedium(&_medium[i]);
_medium[i] = *pmedium;
return S_OK;
}
}
//
// This is a new clipboard format. Give the inner a chance first.
// This is important for formats like "Performed DropEffect" and
// "TargetCLSID", which are used by us to communicate information
// into the data object.
//
if (_pdtInner == NULL ||
FAILED(hr = _pdtInner->SetData(pformatetc, pmedium, fRelease)))
{
// Inner object doesn't want it; let's keep it ourselves
// now look for a free slot
for (i = 0; i < MAX_FORMATS; i++)
{
if (_fmte[i].cfFormat == 0)
{
// found a free slot
_medium[i] = *pmedium;
_fmte[i] = *pformatetc;
return S_OK;
}
}
// fixed size table
hr = E_OUTOFMEMORY;
}
}
else
{
if (_pdtInner)
hr = _pdtInner->SetData(pformatetc, pmedium, fRelease);
else
hr = E_INVALIDARG;
}
return hr;
}
STDMETHODIMP CIDLDataObj::EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC **ppenumFormatEtc)
{
// If this is the first time, build the format list by calling
// QueryGetData with each clipboard format.
if (!_fEnumFormatCalled)
{
UINT ifmt;
FORMATETC fmte = { 0, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
STGMEDIUM medium = { TYMED_HGLOBAL, NULL, NULL };
for (ifmt = 0; ifmt < ICF_MAX; ifmt++)
{
fmte.cfFormat = g_acfIDLData[ifmt];
if (QueryGetData(&fmte) == S_OK)
{
SetData(&fmte, &medium, TRUE);
}
}
_fEnumFormatCalled = TRUE;
}
// Get the number of formatetc
for (UINT cfmt = 0; cfmt < MAX_FORMATS; cfmt++)
{
if (_fmte[cfmt].cfFormat == 0)
break;
}
return SHCreateStdEnumFmtEtcEx(cfmt, _fmte, _pdtInner, ppenumFormatEtc);
}
STDMETHODIMP CIDLDataObj::DAdvise(FORMATETC * pFormatetc, DWORD advf, LPADVISESINK pAdvSink, DWORD *pdwConnection)
{
return OLE_E_ADVISENOTSUPPORTED;
}
STDMETHODIMP CIDLDataObj::DUnadvise(DWORD dwConnection)
{
return OLE_E_ADVISENOTSUPPORTED;
}
STDMETHODIMP CIDLDataObj::EnumDAdvise(LPENUMSTATDATA *ppenumAdvise)
{
return OLE_E_ADVISENOTSUPPORTED;
}
// *** IAsyncOperation methods ***
HRESULT CIDLDataObj::SetAsyncMode(BOOL fDoOpAsync)
{
return E_NOTIMPL;
}
HRESULT CIDLDataObj::GetAsyncMode(BOOL *pfIsOpAsync)
{
if (_punkThread || IsMainShellProcess())
{
*pfIsOpAsync = TRUE;
}
else
{
*pfIsOpAsync = FALSE;
}
return S_OK;
}
HRESULT CIDLDataObj::StartOperation(IBindCtx * pbc)
{
_fDidAsynchStart = TRUE;
return S_OK;
}
HRESULT CIDLDataObj::InOperation(BOOL * pfInAsyncOp)
{
if (_fDidAsynchStart)
{
*pfInAsyncOp = TRUE;
}
else
{
*pfInAsyncOp = FALSE;
}
return S_OK;
}
HRESULT CIDLDataObj::EndOperation(HRESULT hResult, IBindCtx * pbc, DWORD dwEffects)
{
_fDidAsynchStart = FALSE;
return S_OK;
}
void CIDLDataObj::InitIDLData1(IDataObject *pdtInner)
{
_cRef = 1;
_pdtInner = pdtInner;
if (pdtInner)
pdtInner->AddRef();
SHGetThreadRef(&_punkThread);
}
void CIDLDataObj::InitIDLData2(LPCITEMIDLIST pidlFolder, UINT cidl, LPCITEMIDLIST apidl[])
{
if (apidl)
{
HIDA hida = HIDA_Create(pidlFolder, cidl, apidl);
if (hida)
{
#if 0 // not yet
// QueryGetData/SetData on HDROP before calling DataObj_SetGlobal with
// HIDA to insure that CF_HDROP will come first in the enumerator
FORMATETC fmte = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
STGMEDIUM medium = { TYMED_HGLOBAL, NULL, NULL };
if (QueryGetData(&fmte) == S_OK)
{
SetData(&fmte, &medium, TRUE);
}
#endif // 0
IDLData_InitializeClipboardFormats(); // init our registerd formats
DataObj_SetGlobal(this, g_cfHIDA, hida);
}
}
}
CIDLDataObj::CIDLDataObj(IDataObject *pdtInner)
{
InitIDLData1(pdtInner);
}
CIDLDataObj::CIDLDataObj(LPCITEMIDLIST pidlFolder, UINT cidl, LPCITEMIDLIST apidl[], IDataObject *pdtInner)
{
InitIDLData1(pdtInner);
InitIDLData2(pidlFolder, cidl, apidl);
}
CIDLDataObj::CIDLDataObj(LPCITEMIDLIST pidlFolder, UINT cidl, LPCITEMIDLIST apidl[])
{
InitIDLData1(NULL);
InitIDLData2(pidlFolder, cidl, apidl);
}
CIDLDataObj::~CIDLDataObj()
{
for (int i = 0; i < MAX_FORMATS; i++)
{
if (_medium[i].hGlobal)
ReleaseStgMedium(&_medium[i]);
}
if (_pdtInner)
_pdtInner->Release();
if (_punkThread)
_punkThread->Release();
}
//
// Create an instance of CIDLDataObj with default Vtable pointer.
//
STDAPI CIDLData_CreateFromIDArray(LPCITEMIDLIST pidlFolder, UINT cidl, LPCITEMIDLIST apidl[], IDataObject **ppdtobj)
{
return CIDLData_CreateInstance(pidlFolder, cidl, apidl, NULL, ppdtobj);
}
HRESULT CIDLData_CreateInstance(LPCITEMIDLIST pidlFolder, UINT cidl, LPCITEMIDLIST apidl[], IDataObject *pdtInner, IDataObject **ppdtobj)
{
*ppdtobj = new CIDLDataObj(pidlFolder, cidl, apidl, pdtInner);
return *ppdtobj ? S_OK : E_OUTOFMEMORY;
}