426 lines
12 KiB
C++
426 lines
12 KiB
C++
/**********************************************************************/
|
|
/** Microsoft Windows/NT **/
|
|
/** Copyright(c) Microsoft Corporation, 1997 - 1998 **/
|
|
/**********************************************************************/
|
|
|
|
/*
|
|
dataobj.cpp
|
|
Implementation for data objects in the MMC
|
|
|
|
FILE HISTORY:
|
|
|
|
*/
|
|
|
|
#include "stdafx.h"
|
|
#include "dataobj.h"
|
|
#include "extract.h"
|
|
|
|
#ifdef _DEBUG
|
|
#define new DEBUG_NEW
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Sample code to show how to Create DataObjects
|
|
// Minimal error checking for clarity
|
|
|
|
|
|
// Internal private format
|
|
const wchar_t* SNAPIN_INTERNAL = L"SNAPIN_INTERNAL";
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Snap-in NodeType in both GUID format and string format
|
|
// Note - Typically there is a node type for each different object, sample
|
|
// only uses one node type.
|
|
|
|
// MMC required clipboard formats
|
|
unsigned int CDataObject::m_cfNodeType = RegisterClipboardFormat(CCF_NODETYPE);
|
|
unsigned int CDataObject::m_cfNodeTypeString = RegisterClipboardFormat(CCF_SZNODETYPE);
|
|
unsigned int CDataObject::m_cfDisplayName = RegisterClipboardFormat(CCF_DISPLAY_NAME);
|
|
unsigned int CDataObject::m_cfCoClass = RegisterClipboardFormat(CCF_SNAPIN_CLASSID);
|
|
unsigned int CDataObject::m_cfMultiSel = RegisterClipboardFormat(CCF_OBJECT_TYPES_IN_MULTI_SELECT);
|
|
unsigned int CDataObject::m_cfMultiSelDobj = RegisterClipboardFormat(CCF_MMC_MULTISELECT_DATAOBJECT);
|
|
unsigned int CDataObject::m_cfDynamicExtension = RegisterClipboardFormat(CCF_MMC_DYNAMIC_EXTENSIONS);
|
|
unsigned int CDataObject::m_cfNodeId2 = RegisterClipboardFormat(CCF_NODEID2);
|
|
|
|
// snpain specific clipboard formats
|
|
unsigned int CDataObject::m_cfInternal = RegisterClipboardFormat(SNAPIN_INTERNAL);
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CDataObject implementations
|
|
DEBUG_DECLARE_INSTANCE_COUNTER(CDataObject);
|
|
|
|
IMPLEMENT_ADDREF_RELEASE(CDataObject)
|
|
|
|
STDMETHODIMP CDataObject::QueryInterface(REFIID riid, LPVOID *ppv)
|
|
{
|
|
AFX_MANAGE_STATE(AfxGetStaticModuleState());
|
|
|
|
// Is the pointer bad?
|
|
if (ppv == NULL)
|
|
return E_INVALIDARG;
|
|
|
|
// Place NULL in *ppv in case of failure
|
|
*ppv = NULL;
|
|
|
|
// This is the non-delegating IUnknown implementation
|
|
if (riid == IID_IUnknown)
|
|
*ppv = (LPVOID) this;
|
|
else if (riid == IID_IDataObject)
|
|
*ppv = (IDataObject *) this;
|
|
else if (m_spUnknownInner)
|
|
{
|
|
// blind aggregation, we don't know what we're aggregating
|
|
// with, so just pass it down.
|
|
return m_spUnknownInner->QueryInterface(riid, ppv);
|
|
}
|
|
|
|
// If we're going to return an interface, AddRef it first
|
|
if (*ppv)
|
|
{
|
|
((LPUNKNOWN) *ppv)->AddRef();
|
|
return hrOK;
|
|
}
|
|
else
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDataObject::GetDataHere(LPFORMATETC lpFormatetc, LPSTGMEDIUM lpMedium)
|
|
{
|
|
HRESULT hr = DV_E_CLIPFORMAT;
|
|
|
|
AFX_MANAGE_STATE(AfxGetStaticModuleState());
|
|
|
|
// Based on the CLIPFORMAT write data to the stream
|
|
const CLIPFORMAT cf = lpFormatetc->cfFormat;
|
|
|
|
if(cf == m_cfNodeType)
|
|
{
|
|
hr = CreateNodeTypeData(lpMedium);
|
|
}
|
|
else if (cf == m_cfCoClass)
|
|
{
|
|
hr = CreateCoClassID(lpMedium);
|
|
}
|
|
else if(cf == m_cfNodeTypeString)
|
|
{
|
|
hr = CreateNodeTypeStringData(lpMedium);
|
|
}
|
|
else if (cf == m_cfDisplayName)
|
|
{
|
|
hr = CreateDisplayName(lpMedium);
|
|
}
|
|
else if (cf == m_cfInternal)
|
|
{
|
|
hr = CreateInternal(lpMedium);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Call the derived class and see if it can handle
|
|
// this clipboard format
|
|
//
|
|
hr = GetMoreDataHere(lpFormatetc, lpMedium);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CDataObject::GetData(LPFORMATETC lpFormatetcIn, LPSTGMEDIUM lpMedium)
|
|
{
|
|
AFX_MANAGE_STATE(AfxGetStaticModuleState());
|
|
|
|
HRESULT hr = DV_E_CLIPFORMAT;
|
|
|
|
if (lpFormatetcIn->cfFormat == m_cfMultiSel)
|
|
{
|
|
ASSERT(m_internal.m_cookie == MMC_MULTI_SELECT_COOKIE);
|
|
if (m_internal.m_cookie != MMC_MULTI_SELECT_COOKIE)
|
|
return E_FAIL;
|
|
|
|
//return CreateMultiSelData(lpMedium);
|
|
|
|
ASSERT(m_pbMultiSelData != 0);
|
|
ASSERT(m_cbMultiSelData != 0);
|
|
|
|
lpMedium->tymed = TYMED_HGLOBAL;
|
|
lpMedium->hGlobal = ::GlobalAlloc(GMEM_SHARE|GMEM_MOVEABLE,
|
|
(m_cbMultiSelData + sizeof(DWORD)));
|
|
if (lpMedium->hGlobal == NULL)
|
|
return STG_E_MEDIUMFULL;
|
|
|
|
BYTE* pb = reinterpret_cast<BYTE*>(::GlobalLock(lpMedium->hGlobal));
|
|
*((DWORD*)pb) = m_cbMultiSelData / sizeof(GUID);
|
|
pb += sizeof(DWORD);
|
|
CopyMemory(pb, m_pbMultiSelData, m_cbMultiSelData);
|
|
|
|
::GlobalUnlock(lpMedium->hGlobal);
|
|
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
if (lpFormatetcIn->cfFormat == m_cfDynamicExtension)
|
|
{
|
|
if (m_pDynExt)
|
|
{
|
|
// get the data...
|
|
m_pDynExt->BuildMMCObjectTypes(&lpMedium->hGlobal);
|
|
|
|
if (lpMedium->hGlobal == NULL)
|
|
return STG_E_MEDIUMFULL;
|
|
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
else
|
|
if (lpFormatetcIn->cfFormat == m_cfNodeId2)
|
|
{
|
|
hr = CreateNodeId2(lpMedium);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CDataObject::QueryGetData(LPFORMATETC lpFormatEtc)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
|
|
if (lpFormatEtc == NULL)
|
|
return DV_E_FORMATETC;
|
|
|
|
if (lpFormatEtc->lindex != -1)
|
|
return DV_E_LINDEX;
|
|
|
|
if (lpFormatEtc->tymed != TYMED_HGLOBAL)
|
|
return DV_E_TYMED;
|
|
|
|
if (!(lpFormatEtc->dwAspect & DVASPECT_CONTENT))
|
|
return DV_E_DVASPECT;
|
|
|
|
// these are our supported clipboard formats. If it isn't one
|
|
// of these then return invalid.
|
|
|
|
if ( (lpFormatEtc->cfFormat == m_cfNodeType) ||
|
|
(lpFormatEtc->cfFormat == m_cfNodeTypeString) ||
|
|
(lpFormatEtc->cfFormat == m_cfDisplayName) ||
|
|
(lpFormatEtc->cfFormat == m_cfCoClass) ||
|
|
(lpFormatEtc->cfFormat == m_cfInternal) ||
|
|
(lpFormatEtc->cfFormat == m_cfNodeId2) ||
|
|
(lpFormatEtc->cfFormat == m_cfDynamicExtension) )
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
else if ((lpFormatEtc->cfFormat == m_cfMultiSel) ||
|
|
(lpFormatEtc->cfFormat == m_cfMultiSelDobj))
|
|
{
|
|
// Support multi-selection format only if this
|
|
// is a multi-select data object
|
|
if (m_bMultiSelDobj)
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
hr = QueryGetMoreData(lpFormatEtc);
|
|
|
|
#ifdef DEBUG
|
|
TCHAR buf[2000];
|
|
|
|
::GetClipboardFormatName(lpFormatEtc->cfFormat, buf, sizeof(buf));
|
|
|
|
Trace2("CDataObject::QueryGetData - query format %s returning %lx\n", buf, hr);
|
|
#endif
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
// Note - Sample does not implement these
|
|
STDMETHODIMP CDataObject::EnumFormatEtc(DWORD dwDirection, LPENUMFORMATETC* ppEnumFormatEtc)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
STDMETHODIMP CDataObject::GetCanonicalFormatEtc(LPFORMATETC lpFormatEtcIn, LPFORMATETC lpFormatEtcOut)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
STDMETHODIMP CDataObject::SetData(LPFORMATETC lpFormatEtc, LPSTGMEDIUM lpMedium, BOOL bRelease)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
STDMETHODIMP CDataObject::DAdvise(LPFORMATETC lpFormatEc, DWORD advf,
|
|
LPADVISESINK pAdvSink, LPDWORD pdwConn)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
STDMETHODIMP CDataObject::DUnadvise(DWORD dwConnection)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
STDMETHODIMP CDataObject::EnumDAdvise(LPENUMSTATDATA *ppEnumAdvise)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CDataObject creation members
|
|
|
|
HRESULT CDataObject::Create(const void* pBuffer, int len, LPSTGMEDIUM lpMedium)
|
|
{
|
|
HRESULT hr = DV_E_TYMED;
|
|
|
|
AFX_MANAGE_STATE(AfxGetStaticModuleState());
|
|
|
|
// Do some simple validation
|
|
if (pBuffer == NULL || lpMedium == NULL)
|
|
return E_POINTER;
|
|
|
|
// Make sure the type medium is HGLOBAL
|
|
if (lpMedium->tymed == TYMED_HGLOBAL)
|
|
{
|
|
// Create the stream on the hGlobal passed in
|
|
LPSTREAM lpStream;
|
|
hr = CreateStreamOnHGlobal(lpMedium->hGlobal, FALSE, &lpStream);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Write to the stream the number of bytes
|
|
unsigned long written;
|
|
hr = lpStream->Write(pBuffer, len, &written);
|
|
|
|
// Because we told CreateStreamOnHGlobal with 'FALSE',
|
|
// only the stream is released here.
|
|
// Note - the caller (i.e. snap-in, object) will free the HGLOBAL
|
|
// at the correct time. This is according to the IDataObject specification.
|
|
lpStream->Release();
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CDataObject::CreateNodeTypeData(LPSTGMEDIUM lpMedium)
|
|
{
|
|
AFX_MANAGE_STATE(AfxGetStaticModuleState());
|
|
|
|
// Create the node type object in GUID format
|
|
SPITFSNode spNode;
|
|
spNode = GetDataFromComponentData();
|
|
const GUID* pNodeType = spNode->GetNodeType();
|
|
return Create(reinterpret_cast<const void*>(pNodeType), sizeof(GUID), lpMedium);
|
|
}
|
|
|
|
HRESULT CDataObject::CreateNodeTypeStringData(LPSTGMEDIUM lpMedium)
|
|
{
|
|
// Create the node type object in GUID string format
|
|
OLECHAR szNodeType[128];
|
|
SPITFSNode spNode;
|
|
spNode = GetDataFromComponentData();
|
|
const GUID* pNodeType = spNode->GetNodeType();
|
|
::StringFromGUID2(*pNodeType,szNodeType,128);
|
|
return Create(szNodeType, ((wcslen(szNodeType)+1) * sizeof(wchar_t)), lpMedium);
|
|
}
|
|
|
|
HRESULT CDataObject::CreateDisplayName(LPSTGMEDIUM lpMedium)
|
|
{
|
|
// This is the display named used in the scope pane and snap-in manager
|
|
CString szDispName;
|
|
SPITFSNode spNode;
|
|
spNode = GetDataFromComponentData();
|
|
szDispName = spNode->GetString(-1);
|
|
return Create(szDispName, ((szDispName.GetLength()+1) * sizeof(wchar_t)), lpMedium);
|
|
}
|
|
|
|
HRESULT CDataObject::CreateInternal(LPSTGMEDIUM lpMedium)
|
|
{
|
|
return Create(&m_internal, sizeof(INTERNAL), lpMedium);
|
|
}
|
|
|
|
HRESULT CDataObject::CreateCoClassID(LPSTGMEDIUM lpMedium)
|
|
{
|
|
// Create the CoClass information
|
|
return Create(reinterpret_cast<const void*>(&m_internal.m_clsid), sizeof(CLSID), lpMedium);
|
|
}
|
|
|
|
HRESULT CDataObject::CreateMultiSelData(LPSTGMEDIUM lpMedium)
|
|
{
|
|
Assert(m_internal.m_cookie == MMC_MULTI_SELECT_COOKIE);
|
|
|
|
Assert(m_pbMultiSelData != 0);
|
|
Assert(m_cbMultiSelData != 0);
|
|
|
|
return Create(reinterpret_cast<const void*>(m_pbMultiSelData),
|
|
m_cbMultiSelData, lpMedium);
|
|
}
|
|
|
|
HRESULT CDataObject::CreateNodeId2(LPSTGMEDIUM lpMedium)
|
|
{
|
|
AFX_MANAGE_STATE(AfxGetStaticModuleState());
|
|
|
|
HRESULT hr = hrOK;
|
|
|
|
// Create the node type object in GUID format
|
|
SPITFSNode spNode;
|
|
spNode = GetDataFromComponentData();
|
|
|
|
SPITFSNodeHandler spHandler;
|
|
|
|
spNode->GetHandler(&spHandler);
|
|
|
|
if (spHandler)
|
|
{
|
|
CComBSTR bstrId;
|
|
DWORD dwFlags = 0;
|
|
|
|
hr = spHandler->CreateNodeId2(spNode, &bstrId, &dwFlags);
|
|
if (SUCCEEDED(hr) && hr != S_FALSE && bstrId.Length() > 0)
|
|
{
|
|
int nSize = sizeof(SNodeID2) + (bstrId.Length() * sizeof(TCHAR));
|
|
|
|
lpMedium->tymed = TYMED_HGLOBAL;
|
|
lpMedium->hGlobal = ::GlobalAlloc(GMEM_SHARE|GMEM_MOVEABLE, nSize);
|
|
if (lpMedium->hGlobal == NULL)
|
|
{
|
|
hr = STG_E_MEDIUMFULL;
|
|
}
|
|
else
|
|
{
|
|
SNodeID2 * pNodeId = reinterpret_cast<SNodeID2*>(::GlobalLock(lpMedium->hGlobal));
|
|
|
|
::ZeroMemory(pNodeId, nSize);
|
|
|
|
pNodeId->cBytes = bstrId.Length() * sizeof(TCHAR);
|
|
pNodeId->dwFlags = dwFlags;
|
|
_tcscpy((LPTSTR) pNodeId->id, bstrId);
|
|
|
|
::GlobalUnlock(lpMedium->hGlobal);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_UNEXPECTED;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
ITFSNode* CDataObject::GetDataFromComponentData()
|
|
{
|
|
SPITFSNodeMgr spNodeMgr;
|
|
SPITFSNode spNode;
|
|
|
|
Assert(m_spTFSComponentData);
|
|
m_spTFSComponentData->GetNodeMgr(&spNodeMgr);
|
|
spNodeMgr->FindNode(m_internal.m_cookie, &spNode);
|
|
|
|
return spNode.Transfer();
|
|
}
|
|
|