373 lines
10 KiB
C++
373 lines
10 KiB
C++
|
#include "pch.h"
|
||
|
#include "stddef.h"
|
||
|
#pragma hdrstop
|
||
|
|
||
|
|
||
|
// free a DSA
|
||
|
|
||
|
|
||
|
int _DestroyCB(LPVOID pItem, LPVOID pData)
|
||
|
{
|
||
|
DATAOBJECTITEM *pdoi = (DATAOBJECTITEM*)pItem;
|
||
|
LocalFreeStringW(&pdoi->pszPath);
|
||
|
LocalFreeStringW(&pdoi->pszObjectClass);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
STDAPI_(void) FreeDataObjectDSA(HDSA hdsaObjects)
|
||
|
{
|
||
|
DSA_DestroyCallback(hdsaObjects, _DestroyCB, NULL);
|
||
|
}
|
||
|
|
||
|
|
||
|
// IDataObject stuff
|
||
|
|
||
|
CLIPFORMAT g_cfDsObjectNames = 0;
|
||
|
|
||
|
typedef struct
|
||
|
{
|
||
|
UINT cfFormat;
|
||
|
STGMEDIUM medium;
|
||
|
} OTHERFMT;
|
||
|
|
||
|
class CDataObject : public IDataObject
|
||
|
{
|
||
|
public:
|
||
|
CDataObject(HDSA hdsaObjects, BOOL fAdmin);
|
||
|
~CDataObject();
|
||
|
|
||
|
// IUnknown
|
||
|
STDMETHOD(QueryInterface)(REFIID riid, LPVOID* ppvObject);
|
||
|
STDMETHOD_(ULONG, AddRef)();
|
||
|
STDMETHOD_(ULONG, Release)();
|
||
|
|
||
|
// IDataObject
|
||
|
STDMETHODIMP GetData(FORMATETC *pformatetcIn, STGMEDIUM *pmedium);
|
||
|
STDMETHODIMP GetDataHere(FORMATETC *pformatetc, STGMEDIUM *pmedium)
|
||
|
{ return E_NOTIMPL; }
|
||
|
STDMETHODIMP QueryGetData(FORMATETC *pformatetc);
|
||
|
STDMETHODIMP GetCanonicalFormatEtc(FORMATETC *pformatectIn, FORMATETC *pformatetcOut)
|
||
|
{ return DATA_S_SAMEFORMATETC; }
|
||
|
STDMETHODIMP SetData(FORMATETC *pformatetc, STGMEDIUM *pmedium, BOOL fRelease);
|
||
|
STDMETHODIMP EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC **ppenumFormatEtc)
|
||
|
{ return E_NOTIMPL; }
|
||
|
STDMETHODIMP DAdvise(FORMATETC *pformatetc, DWORD advf, IAdviseSink *pAdvSink, DWORD *pdwConnection)
|
||
|
{ return OLE_E_ADVISENOTSUPPORTED; }
|
||
|
STDMETHODIMP DUnadvise(DWORD dwConnection)
|
||
|
{ return OLE_E_ADVISENOTSUPPORTED; }
|
||
|
STDMETHODIMP EnumDAdvise(IEnumSTATDATA **ppenumAdvise)
|
||
|
{ return OLE_E_ADVISENOTSUPPORTED; }
|
||
|
|
||
|
private:
|
||
|
LONG _cRef;
|
||
|
|
||
|
BOOL _fAdmin;
|
||
|
HDSA _hdsaObjects; // array of the objects
|
||
|
HDSA _hdsaOtherFmt;
|
||
|
|
||
|
static INT s_OtherFmtDestroyCB(LPVOID pVoid, LPVOID pData);
|
||
|
|
||
|
void _RegisterClipFormats(void);
|
||
|
HRESULT _GetDsObjectNames(FORMATETC* pFmt, STGMEDIUM* pMedium);
|
||
|
};
|
||
|
|
||
|
|
||
|
STDAPI CDataObject_CreateInstance(HDSA dsaObjects, BOOL fAdmin, REFIID riid, void **ppv)
|
||
|
{
|
||
|
CDataObject *pdo = new CDataObject(dsaObjects, fAdmin);
|
||
|
if (!pdo)
|
||
|
return E_OUTOFMEMORY;
|
||
|
|
||
|
HRESULT hr = pdo->QueryInterface(riid, ppv);
|
||
|
pdo->Release();
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
// IDataObject implementation
|
||
|
|
||
|
void CDataObject::_RegisterClipFormats(void)
|
||
|
{
|
||
|
if (!g_cfDsObjectNames)
|
||
|
g_cfDsObjectNames = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_DSOBJECTNAMES);
|
||
|
}
|
||
|
|
||
|
CDataObject::CDataObject(HDSA dsaObjects, BOOL fAdmin) :
|
||
|
_hdsaObjects(dsaObjects),
|
||
|
_fAdmin(fAdmin),
|
||
|
_cRef(1)
|
||
|
{
|
||
|
DllAddRef();
|
||
|
_RegisterClipFormats(); // ensure our private formats are registered
|
||
|
}
|
||
|
|
||
|
|
||
|
// destruction
|
||
|
|
||
|
INT CDataObject::s_OtherFmtDestroyCB(LPVOID pVoid, LPVOID pData)
|
||
|
{
|
||
|
OTHERFMT *pOtherFmt = (OTHERFMT*)pVoid;
|
||
|
ReleaseStgMedium(&pOtherFmt->medium);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
CDataObject::~CDataObject()
|
||
|
{
|
||
|
FreeDataObjectDSA(_hdsaObjects);
|
||
|
|
||
|
if ( _hdsaOtherFmt )
|
||
|
DSA_DestroyCallback(_hdsaOtherFmt, s_OtherFmtDestroyCB, NULL);
|
||
|
|
||
|
DllRelease();
|
||
|
}
|
||
|
|
||
|
|
||
|
// QI handling
|
||
|
|
||
|
ULONG CDataObject::AddRef()
|
||
|
{
|
||
|
return InterlockedIncrement(&_cRef);
|
||
|
}
|
||
|
|
||
|
ULONG CDataObject::Release()
|
||
|
{
|
||
|
if (InterlockedDecrement(&_cRef))
|
||
|
return _cRef;
|
||
|
|
||
|
delete this;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
HRESULT CDataObject::QueryInterface(REFIID riid, void **ppv)
|
||
|
{
|
||
|
static const QITAB qit[] =
|
||
|
{
|
||
|
QITABENT(CDataObject, IDataObject), // IID_IDataObject
|
||
|
{0, 0 },
|
||
|
};
|
||
|
return QISearch(this, qit, riid, ppv);
|
||
|
}
|
||
|
|
||
|
|
||
|
// fetch the object names from the IDataObject
|
||
|
|
||
|
HRESULT CDataObject::_GetDsObjectNames(FORMATETC* pFmt, STGMEDIUM* pMedium)
|
||
|
{
|
||
|
IDsDisplaySpecifier *pdds;
|
||
|
HRESULT hr = CoCreateInstance(CLSID_DsDisplaySpecifier, NULL, CLSCTX_INPROC_SERVER, IID_IDsDisplaySpecifier, (void **)&pdds);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
int count = DSA_GetItemCount(_hdsaObjects);
|
||
|
int i;
|
||
|
|
||
|
// lets walk the array of items trying to determine which items
|
||
|
// are to be returned to the caller.
|
||
|
|
||
|
DWORD cbStruct = SIZEOF(DSOBJECTNAMES);
|
||
|
DWORD offset = SIZEOF(DSOBJECTNAMES);
|
||
|
|
||
|
for (i = 0 ; i < count; i++)
|
||
|
{
|
||
|
DATAOBJECTITEM* pdoi = (DATAOBJECTITEM*)DSA_GetItemPtr(_hdsaObjects, i);
|
||
|
|
||
|
// string offset is offset by the number of structures
|
||
|
offset += SIZEOF(DSOBJECT);
|
||
|
|
||
|
// adjust the size of the total structure
|
||
|
cbStruct += SIZEOF(DSOBJECT);
|
||
|
cbStruct += StringByteSizeW(pdoi->pszPath);
|
||
|
cbStruct += StringByteSizeW(pdoi->pszObjectClass);
|
||
|
}
|
||
|
|
||
|
// we have walked the structure, we know the size so lets return
|
||
|
// the structure to the caller.
|
||
|
|
||
|
DSOBJECTNAMES *pDsObjectNames;
|
||
|
hr = AllocStorageMedium(pFmt, pMedium, cbStruct, (LPVOID*)&pDsObjectNames);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
pDsObjectNames->clsidNamespace = CLSID_MicrosoftDS;
|
||
|
pDsObjectNames->cItems = count;
|
||
|
|
||
|
for (i = 0 ; i < count; i++)
|
||
|
{
|
||
|
DATAOBJECTITEM* pdoi = (DATAOBJECTITEM*)DSA_GetItemPtr(_hdsaObjects, i);
|
||
|
|
||
|
// is this class a conatiner, if so then lets return that to the caller
|
||
|
|
||
|
if (pdoi->fIsContainer)
|
||
|
pDsObjectNames->aObjects[i].dwFlags |= DSOBJECT_ISCONTAINER;
|
||
|
|
||
|
if (_fAdmin)
|
||
|
pDsObjectNames->aObjects[i].dwProviderFlags = DSPROVIDER_ADVANCED;
|
||
|
|
||
|
// copy the strings to the buffer
|
||
|
|
||
|
pDsObjectNames->aObjects[i].offsetName = offset;
|
||
|
StringByteCopyW(pDsObjectNames, offset, pdoi->pszPath);
|
||
|
offset += StringByteSizeW(pdoi->pszPath);
|
||
|
|
||
|
pDsObjectNames->aObjects[i].offsetClass = offset;
|
||
|
StringByteCopyW(pDsObjectNames, offset, pdoi->pszObjectClass);
|
||
|
offset += StringByteSizeW(pdoi->pszObjectClass);
|
||
|
}
|
||
|
|
||
|
if ( FAILED(hr) )
|
||
|
ReleaseStgMedium(pMedium);
|
||
|
}
|
||
|
|
||
|
pdds->Release();
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
// IDataObject methods
|
||
|
|
||
|
STDMETHODIMP CDataObject::GetData(FORMATETC* pFmt, STGMEDIUM* pMedium)
|
||
|
{
|
||
|
int i;
|
||
|
HRESULT hr = S_OK;
|
||
|
|
||
|
TraceEnter(TRACE_DATAOBJ, "CDataObject::GetData");
|
||
|
|
||
|
if ( !pFmt || !pMedium )
|
||
|
ExitGracefully(hr, E_INVALIDARG, "Bad arguments to GetData");
|
||
|
|
||
|
if ( pFmt->cfFormat == g_cfDsObjectNames )
|
||
|
{
|
||
|
hr = _GetDsObjectNames(pFmt, pMedium);
|
||
|
FailGracefully(hr, "Failed when build CF_DSOBJECTNAMES");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hr = DV_E_FORMATETC; // failed
|
||
|
|
||
|
for ( i = 0 ; _hdsaOtherFmt && (i < DSA_GetItemCount(_hdsaOtherFmt)); i++ )
|
||
|
{
|
||
|
OTHERFMT *pOtherFmt = (OTHERFMT*)DSA_GetItemPtr(_hdsaOtherFmt, i);
|
||
|
TraceAssert(pOtherFmt);
|
||
|
|
||
|
if ( pOtherFmt->cfFormat == pFmt->cfFormat )
|
||
|
{
|
||
|
hr = CopyStorageMedium(pFmt, pMedium, &pOtherFmt->medium);
|
||
|
FailGracefully(hr, "Failed to copy the storage medium");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
exit_gracefully:
|
||
|
|
||
|
TraceLeaveResult(hr);
|
||
|
}
|
||
|
|
||
|
|
||
|
STDMETHODIMP CDataObject::QueryGetData(FORMATETC* pFmt)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
INT i;
|
||
|
BOOL fSupported = FALSE;
|
||
|
|
||
|
TraceEnter(TRACE_DATAOBJ, "CDataObject::QueryGetData");
|
||
|
|
||
|
// check the valid clipboard formats either the static list, or the
|
||
|
// DSA which contains the ones we have been set with.
|
||
|
|
||
|
if (pFmt->cfFormat == g_cfDsObjectNames)
|
||
|
{
|
||
|
fSupported = TRUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
for ( i = 0 ; !fSupported && _hdsaOtherFmt && (i < DSA_GetItemCount(_hdsaOtherFmt)) ; i++ )
|
||
|
{
|
||
|
OTHERFMT *pOtherFmt = (OTHERFMT*)DSA_GetItemPtr(_hdsaOtherFmt, i);
|
||
|
TraceAssert(pOtherFmt);
|
||
|
|
||
|
if ( pOtherFmt->cfFormat == pFmt->cfFormat )
|
||
|
{
|
||
|
TraceMsg("Format is supported (set via ::SetData");
|
||
|
fSupported = TRUE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( !fSupported )
|
||
|
ExitGracefully(hr, DV_E_FORMATETC, "Bad format passed to QueryGetData");
|
||
|
|
||
|
// format looks good, lets check the other parameters
|
||
|
|
||
|
if ( !( pFmt->tymed & TYMED_HGLOBAL ) )
|
||
|
ExitGracefully(hr, E_INVALIDARG, "Non HGLOBAL StgMedium requested");
|
||
|
|
||
|
if ( ( pFmt->ptd ) || !( pFmt->dwAspect & DVASPECT_CONTENT) || !( pFmt->lindex == -1 ) )
|
||
|
ExitGracefully(hr, E_INVALIDARG, "Bad format requested");
|
||
|
|
||
|
hr = S_OK; // successs
|
||
|
|
||
|
exit_gracefully:
|
||
|
|
||
|
TraceLeaveResult(hr);
|
||
|
}
|
||
|
|
||
|
|
||
|
STDMETHODIMP CDataObject::SetData(FORMATETC* pFmt, STGMEDIUM* pMedium, BOOL fRelease)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
INT i;
|
||
|
OTHERFMT otherfmt = { 0 };
|
||
|
USES_CONVERSION;
|
||
|
|
||
|
TraceEnter(TRACE_DATAOBJ, "CDataObject::SetData");
|
||
|
|
||
|
// All the user to store data with our DataObject, however we are
|
||
|
// only interested in allowing them to this with particular clipboard formats
|
||
|
|
||
|
if ( fRelease && !( pFmt->tymed & TYMED_HGLOBAL ) )
|
||
|
ExitGracefully(hr, E_INVALIDARG, "fRelease == TRUE, but not a HGLOBAL allocation");
|
||
|
|
||
|
if ( !_hdsaOtherFmt )
|
||
|
{
|
||
|
_hdsaOtherFmt = DSA_Create(SIZEOF(OTHERFMT), 4);
|
||
|
TraceAssert(_hdsaOtherFmt);
|
||
|
|
||
|
if ( !_hdsaOtherFmt )
|
||
|
ExitGracefully(hr, E_OUTOFMEMORY, "Failed to allocate the DSA for items");
|
||
|
}
|
||
|
|
||
|
// if there is another copy of this data in the IDataObject then lets discard it.
|
||
|
|
||
|
for ( i = 0 ; i < DSA_GetItemCount(_hdsaOtherFmt) ; i++ )
|
||
|
{
|
||
|
OTHERFMT *pOtherFmt = (OTHERFMT*)DSA_GetItemPtr(_hdsaOtherFmt, i);
|
||
|
TraceAssert(pOtherFmt);
|
||
|
|
||
|
if ( pOtherFmt->cfFormat == pFmt->cfFormat )
|
||
|
{
|
||
|
Trace(TEXT("Discarding previous entry for this format at index %d"), i);
|
||
|
ReleaseStgMedium(&pOtherFmt->medium);
|
||
|
DSA_DeleteItem(_hdsaOtherFmt, i);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// now put a copy of the data passed to ::SetData into the DSA.
|
||
|
|
||
|
otherfmt.cfFormat = pFmt->cfFormat;
|
||
|
|
||
|
hr = CopyStorageMedium(pFmt, &otherfmt.medium, pMedium);
|
||
|
FailGracefully(hr, "Failed to copy the STORAGEMEIDUM");
|
||
|
|
||
|
if ( -1 == DSA_AppendItem(_hdsaOtherFmt, &otherfmt) )
|
||
|
{
|
||
|
ReleaseStgMedium(&otherfmt.medium);
|
||
|
ExitGracefully(hr, E_OUTOFMEMORY, "Failed to add the data to the DSA");
|
||
|
}
|
||
|
|
||
|
hr = S_OK; // success
|
||
|
|
||
|
exit_gracefully:
|
||
|
|
||
|
TraceLeaveResult(hr);
|
||
|
}
|