/***************************************************************************** * * (C) COPYRIGHT MICROSOFT CORPORATION, 2000 * * TITLE: comutils.inl * * VERSION: 1.0 * * AUTHOR: LazarI * * DATE: 23-Dec-2000 * * DESCRIPTION: COM templates & utilities (Impl.) * *****************************************************************************/ //////////////////////////////////////////////// // template class CDataObj // // implementation for an IDataObject which // supports SetData to different formats. // // construction/destruction template CDataObj::CDataObj() : m_cRefs(1) { memset(m_fmte, 0, sizeof(m_fmte)); memset(m_medium, 0, sizeof(m_medium)); } template CDataObj::~CDataObj() { // release the data we keep for( int i = 0; i < MAX_FORMATS; i++ ) { if( m_medium[i].hGlobal ) { ReleaseStgMedium(&m_medium[i]); } } } /////////////////////////////// // IUnknown impl. - standard // template STDMETHODIMP CDataObj::QueryInterface(REFIID riid, void **ppv) { // standard implementation if( !ppv ) { return E_INVALIDARG; } *ppv = NULL; if( IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IDataObject) ) { *ppv = static_cast(this); } else { return E_NOINTERFACE; } reinterpret_cast(*ppv)->AddRef(); return S_OK; } template STDMETHODIMP_(ULONG) CDataObj::AddRef() { // standard implementation return InterlockedIncrement(&m_cRefs); } template STDMETHODIMP_(ULONG) CDataObj::Release() { // standard implementation ULONG cRefs = InterlockedDecrement(&m_cRefs); if( 0 == cRefs ) { delete this; } return cRefs; } ////////////////// // IDataObject // template /* [local] */ HRESULT STDMETHODCALLTYPE CDataObj::GetData( /* [unique][in] */ FORMATETC *pformatetcIn, /* [out] */ STGMEDIUM *pmedium) { HRESULT hr = E_INVALIDARG; pmedium->hGlobal = NULL; pmedium->pUnkForRelease = NULL; for( int i = 0; i < MAX_FORMATS; i++ ) { if( (m_fmte[i].cfFormat == pformatetcIn->cfFormat) && (m_fmte[i].tymed & pformatetcIn->tymed) && (m_fmte[i].dwAspect == pformatetcIn->dwAspect) ) { *pmedium = m_medium[i]; if( pmedium->hGlobal ) { // indicate that the caller should not release hmem. if( pmedium->tymed == TYMED_HGLOBAL ) { pmedium->pUnkForRelease = static_cast(this); AddRef(); 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 = m_medium[i].pstm->Stat(&stat, STATFLAG_NONAME); if( SUCCEEDED(hr) ) { const LARGE_INTEGER g_li0 = {0}; // Seek the source stream to the beginning. m_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 = m_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 = m_medium[i].pUnkForRelease; if( pmedium->pUnkForRelease ) { pmedium->pUnkForRelease->AddRef(); } } else { hr = E_OUTOFMEMORY; } } } } } } return hr; } template /* [local] */ HRESULT STDMETHODCALLTYPE CDataObj::GetDataHere( /* [unique][in] */ FORMATETC *pformatetc, /* [out][in] */ STGMEDIUM *pmedium) { // we don't implement this. return E_NOTIMPL; } template HRESULT STDMETHODCALLTYPE CDataObj::QueryGetData( /* [unique][in] */ FORMATETC *pformatetc) { HRESULT hr = E_UNEXPECTED; for( int i = 0; i < MAX_FORMATS; i++ ) { if( (m_fmte[i].cfFormat == pformatetc->cfFormat) && (m_fmte[i].tymed & pformatetc->tymed) && (m_fmte[i].dwAspect == pformatetc->dwAspect) ) { hr = S_OK; } } return hr; } template HRESULT STDMETHODCALLTYPE CDataObj::GetCanonicalFormatEtc( /* [unique][in] */ FORMATETC *pformatectIn, /* [out] */ FORMATETC *pformatetcOut) { // always return the data in the same format return DATA_S_SAMEFORMATETC; } template /* [local] */ HRESULT STDMETHODCALLTYPE CDataObj::SetData( /* [unique][in] */ FORMATETC *pformatetc, /* [unique][in] */ STGMEDIUM *pmedium, /* [in] */ BOOL fRelease) { HRESULT hr = E_INVALIDARG; ASSERT(pformatetc->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 < MAX_FORMATS; i++ ) { if( (m_fmte[i].cfFormat == pformatetc->cfFormat) && (m_fmte[i].tymed == pformatetc->tymed) && (m_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( m_medium[i].hGlobal ) { ReleaseStgMedium(&m_medium[i]); } m_medium[i] = *pmedium; return S_OK; } } // this is a new clipboard format. look for a free slot. for( i = 0; i < MAX_FORMATS; i++ ) { if( m_fmte[i].cfFormat == 0 ) { // found a free slot m_medium[i] = *pmedium; m_fmte[i] = *pformatetc; return S_OK; } } // overflow of our fixed size table hr = E_OUTOFMEMORY; } return hr; } template HRESULT STDMETHODCALLTYPE CDataObj::EnumFormatEtc( /* [in] */ DWORD dwDirection, /* [out] */ IEnumFORMATETC **ppenumFormatEtc) { // we don't implement this. return E_NOTIMPL; } template HRESULT STDMETHODCALLTYPE CDataObj::DAdvise( /* [in] */ FORMATETC *pformatetc, /* [in] */ DWORD advf, /* [unique][in] */ IAdviseSink *pAdvSink, /* [out] */ DWORD *pdwConnection) { // we don't implement this. return OLE_E_ADVISENOTSUPPORTED; } template HRESULT STDMETHODCALLTYPE CDataObj::DUnadvise( /* [in] */ DWORD dwConnection) { // we don't implement this. return OLE_E_ADVISENOTSUPPORTED; } template HRESULT STDMETHODCALLTYPE CDataObj::EnumDAdvise( /* [out] */ IEnumSTATDATA **ppenumAdvise) { // we don't implement this. return OLE_E_ADVISENOTSUPPORTED; } //////////////////////////////////////////////// // template class CSimpleDataObjImpl // // simple implementation for an IDataObject // and IDropSource which lives in memory. // // construction/destruction template CSimpleDataObjImpl::CSimpleDataObjImpl(const T &data, CLIPFORMAT cfDataType, IDataObject *pDataObj) : m_cRefs(1), m_cfDataType(cfDataType) { m_data = data; m_spDataObj.CopyFrom(pDataObj); } template CSimpleDataObjImpl::~CSimpleDataObjImpl() { // nothing special } /////////////////////////////// // IUnknown impl. - standard // template STDMETHODIMP CSimpleDataObjImpl::QueryInterface(REFIID riid, void **ppv) { // standard implementation if( !ppv ) { return E_INVALIDARG; } *ppv = NULL; if( IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IDataObject) ) { *ppv = static_cast(this); } else if( IsEqualIID(riid, IID_IDropSource) ) { *ppv = static_cast(this); } else { return E_NOINTERFACE; } reinterpret_cast(*ppv)->AddRef(); return S_OK; } template STDMETHODIMP_(ULONG) CSimpleDataObjImpl::AddRef() { // standard implementation return InterlockedIncrement(&m_cRefs); } template STDMETHODIMP_(ULONG) CSimpleDataObjImpl::Release() { // standard implementation ULONG cRefs = InterlockedDecrement(&m_cRefs); if( 0 == cRefs ) { delete this; } return cRefs; } ////////////////// // IDataObject // template /* [local] */ HRESULT STDMETHODCALLTYPE CSimpleDataObjImpl::GetData( /* [unique][in] */ FORMATETC *pformatetcIn, /* [out] */ STGMEDIUM *pmedium) { HRESULT hr = E_INVALIDARG; // try our data obejct first if( m_spDataObj ) { hr = m_spDataObj->GetData(pformatetcIn, pmedium); } if( FAILED(hr) ) { pmedium->hGlobal = NULL; pmedium->pUnkForRelease = NULL; pmedium->tymed = TYMED_HGLOBAL; hr = QueryGetData(pformatetcIn); if( SUCCEEDED(hr) && FAILED(m_spDataObj->QueryGetData(pformatetcIn)) ) { pmedium->hGlobal = GlobalAlloc(GPTR, sizeof(T)); if( pmedium->hGlobal ) { *((T *)pmedium->hGlobal) = m_data; hr = S_OK; // success } else { hr = E_OUTOFMEMORY; } } } return hr; } template /* [local] */ HRESULT STDMETHODCALLTYPE CSimpleDataObjImpl::GetDataHere( /* [unique][in] */ FORMATETC *pformatetc, /* [out][in] */ STGMEDIUM *pmedium) { // we don't implement this. return E_NOTIMPL; } template HRESULT STDMETHODCALLTYPE CSimpleDataObjImpl::QueryGetData( /* [unique][in] */ FORMATETC *pformatetc) { HRESULT hr = E_UNEXPECTED; // try our data obejct first if( m_spDataObj ) { hr = m_spDataObj->QueryGetData(pformatetc); } if( FAILED(hr) ) { if( m_cfDataType == pformatetc->cfFormat ) { if( TYMED_HGLOBAL & pformatetc->tymed ) { // success hr = S_OK; } else { // invalid tymed hr = DV_E_TYMED; } } else { // invalid clipboard format hr = DV_E_CLIPFORMAT; } } return hr; } template HRESULT STDMETHODCALLTYPE CSimpleDataObjImpl::GetCanonicalFormatEtc( /* [unique][in] */ FORMATETC *pformatectIn, /* [out] */ FORMATETC *pformatetcOut) { // always return the data in the same format return DATA_S_SAMEFORMATETC; } template /* [local] */ HRESULT STDMETHODCALLTYPE CSimpleDataObjImpl::SetData( /* [unique][in] */ FORMATETC *pformatetc, /* [unique][in] */ STGMEDIUM *pmedium, /* [in] */ BOOL fRelease) { HRESULT hr = E_INVALIDARG; // try our data obejct first if( m_spDataObj ) { hr = m_spDataObj->SetData(pformatetc, pmedium, fRelease); } if( FAILED(hr) ) { hr = QueryGetData(pformatetc); if( SUCCEEDED(hr) && FAILED(m_spDataObj->QueryGetData(pformatetc)) ) { if( pmedium->hGlobal ) { m_data = *((T *)pmedium->hGlobal); hr = S_OK; // success } else { hr = E_INVALIDARG; } } } return hr; } template HRESULT STDMETHODCALLTYPE CSimpleDataObjImpl::EnumFormatEtc( /* [in] */ DWORD dwDirection, /* [out] */ IEnumFORMATETC **ppenumFormatEtc) { // we don't implement this. return E_NOTIMPL; } template HRESULT STDMETHODCALLTYPE CSimpleDataObjImpl::DAdvise( /* [in] */ FORMATETC *pformatetc, /* [in] */ DWORD advf, /* [unique][in] */ IAdviseSink *pAdvSink, /* [out] */ DWORD *pdwConnection) { // we don't implement this. return OLE_E_ADVISENOTSUPPORTED; } template HRESULT STDMETHODCALLTYPE CSimpleDataObjImpl::DUnadvise( /* [in] */ DWORD dwConnection) { // we don't implement this. return OLE_E_ADVISENOTSUPPORTED; } template HRESULT STDMETHODCALLTYPE CSimpleDataObjImpl::EnumDAdvise( /* [out] */ IEnumSTATDATA **ppenumAdvise) { // we don't implement this. return OLE_E_ADVISENOTSUPPORTED; } ////////////////// // IDropSource // template HRESULT STDMETHODCALLTYPE CSimpleDataObjImpl::QueryContinueDrag( /* [in] */ BOOL fEscapePressed, /* [in] */ DWORD grfKeyState) { // standard implementation HRESULT hr = S_OK; if( fEscapePressed ) { hr = DRAGDROP_S_CANCEL; } if( !(grfKeyState & (MK_LBUTTON | MK_RBUTTON | MK_MBUTTON)) ) { hr = DRAGDROP_S_DROP; } return hr; } template HRESULT STDMETHODCALLTYPE CSimpleDataObjImpl::GiveFeedback( /* [in] */ DWORD dwEffect) { // standard implementation return DRAGDROP_S_USEDEFAULTCURSORS; } // this namespace is a placeholder to put COM related helpers here namespace comhelpers { inline BOOL AreObjectsIdentical(IUnknown *punk1, IUnknown *punk2) { BOOL bRet = FALSE; if (NULL == punk1 && NULL == punk2) { // if both are NULL then we assume they are identical bRet = TRUE; } else { // one of them isn't NULL - we compare using the COM identity rules if (punk1 && punk2) { CRefPtrCOM spUnk1, spUnk2; if (SUCCEEDED(punk1->QueryInterface(IID_IUnknown, (void**)&spUnk1)) && SUCCEEDED(punk2->QueryInterface(IID_IUnknown, (void**)&spUnk2))) { bRet = (spUnk1 == spUnk2); } } } return bRet; } }