IDataObject详解

第二部分我们介绍了OLE数据传输的相关知识,这一节主要讲怎么实现一个IDataObject接口。然后再给出一个例子。

首先我们要明白,IDataObject是一个COM接口,我们就必须得创建一个类,实现这个接口的每一个方法,包括它的基类的方法。

1. SdkDataObject.h 头文件: 

[cpp]  view plain copy
  1. #ifdef __cplusplus  
  2. #ifndef _SDKDATAOBJECT_H_  
  3. #define _SDKDATAOBJECT_H_  
  4.   
  5. #include "SdkCommon.h"  
  6. #include "SdkDropSource.h"  
  7.   
  8.   
  9. typedef struct _DATASTORAGE  
  10. {  
  11.     FORMATETC *m_formatEtc;  
  12.     STGMEDIUM *m_stgMedium;  
  13.   
  14. } DATASTORAGE_t, *LPDATASTORAGE_t;  
  15.   
  16. class CLASS_DECLSPEC SdkDataObject : public IDataObject  
  17. {  
  18. public:  
  19.   
  20.     SdkDataObject(SdkDropSource *pDropSource = NULL);  
  21.     BOOL IsDataAvailable(CLIPFORMAT cfFormat);  
  22.     BOOL GetGlobalData(CLIPFORMAT cfFormat, void **ppData);  
  23.     BOOL GetGlobalDataArray(CLIPFORMAT cfFormat,   
  24.         HGLOBAL *pDataArray, DWORD dwCount);  
  25.     BOOL SetGlobalData(CLIPFORMAT cfFormat, void *pData, BOOL fRelease = TRUE);  
  26.     BOOL SetGlobalDataArray(CLIPFORMAT cfFormat,   
  27.         HGLOBAL *pDataArray, DWORD dwCount, BOOL fRelease = TRUE);  
  28.     BOOL SetDropTip(DROPIMAGETYPE type, PCWSTR pszMsg, PCWSTR pszInsert);  
  29.    
  30.     // The com interface.  
  31.     IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv);  
  32.     IFACEMETHODIMP_(ULONG) AddRef();  
  33.     IFACEMETHODIMP_(ULONG) Release();  
  34.     IFACEMETHODIMP GetData(FORMATETC *pformatetcIn, STGMEDIUM *pmedium);  
  35.     IFACEMETHODIMP SetData(FORMATETC *pformatetc,  
  36.         STGMEDIUM *pmedium, BOOL fRelease);  
  37.     IFACEMETHODIMP GetDataHere(FORMATETC *pformatetc , STGMEDIUM *pmedium );  
  38.     IFACEMETHODIMP QueryGetData(FORMATETC *pformatetc);  
  39.     IFACEMETHODIMP GetCanonicalFormatEtc(FORMATETC *pformatetcIn,    
  40.         FORMATETC *pformatetcOut);  
  41.     IFACEMETHODIMP EnumFormatEtc(DWORD dwDirection,   
  42.         IEnumFORMATETC **ppenumFormatEtc);  
  43.     IFACEMETHODIMP DAdvise(FORMATETC *pformatetc , DWORD advf ,   
  44.         IAdviseSink *pAdvSnk , DWORD *pdwConnection);  
  45.     IFACEMETHODIMP DUnadvise(DWORD dwConnection);  
  46.     IFACEMETHODIMP EnumDAdvise(IEnumSTATDATA **ppenumAdvise);  
  47.   
  48. private:  
  49.   
  50.     ~SdkDataObject(void);  
  51.     SdkDataObject(const SdkDataObject&);  
  52.     SdkDataObject& operator = (const SdkDataObject&);  
  53.     HRESULT CopyMedium(STGMEDIUM* pMedDest,   
  54.        STGMEDIUM* pMedSrc, FORMATETC* pFmtSrc);  
  55.     HRESULT SetBlob(CLIPFORMAT cf, const void *pvBlob, UINT cbBlob);  
  56.   
  57. private:  
  58.   
  59.     //!< The reference of count  
  60.     volatile LONG           m_lRefCount;        
  61.     //!< The pointer to CDropSource object    
  62.     SdkDropSource          *m_pDropSource;    
  63.     //!< The collection of DATASTORAGE_t structure      
  64.     vector<DATASTORAGE_t>   m_dataStorageCL;      
  65. };  
  66.   
  67. #endif // _SDKDATAOBJECT_H_  
  68. #endif // __cplusplus  

上面SdkDataObject.h定义了类SdkDataObject类,它实现了IDataObject接口,包括IUnknown接口。

几点说明如下:

1、SdkDataObject类里面也声明了拷贝构造、赋值操作符等,而且是私有的,就是不想让这个对象可以复制

2、IsDataAvailable:判断指定的格式是否支持。

3、GetGlobalData:得到全局的数据。

4、SetGlobalData:设置全局的数据。

5、CopyMedium:复制媒体数据。

6、上面列出的这些函数,几乎都是辅助函数,我设计时是根据我自己的业务需求来设计的,不同的需求可能不同,但最本质的都是实现一些COM接口。同时,我还定义了一个结构体DATASTORAGE_t,用来保存数据格式对,把设置的数据格式对存在一个vector中。

7、成员变量volatile LONG m_lRefCount,表示当前类的引用计数,构造函数一定要初始化为1,不能是0,其中volatile的意思就是说,告诉编译器不要其优化,每次要用访问这个值时,都是到它的地址上去取,而不是从寄存器中读取。


2. SdkDataObject.cpp的实现

2.1 构造函数

很简单,就是进行一些初始化操作,注意引用计数一定要是1,而不是0。

[cpp]  view plain copy
  1. SdkDataObject::SdkDataObject(SdkDropSource *pDropSource)  
  2. {  
  3.     m_pDropSource = pDropSource;  
  4.     m_lRefCount = 1;  
  5. }  

2.2 析构函数

负责释放内存,这个函数是私有的,调用者只能调用Release来释放它。 

[cpp]  view plain copy
  1. SdkDataObject::~SdkDataObject(void)  
  2. {  
  3.     m_lRefCount = 0;  
  4.   
  5.     int nSize = (int)m_dataStorageCL.size();  
  6.     for (int i = 0; i < nSize; ++i)  
  7.     {  
  8.         DATASTORAGE_t dataEntry = m_dataStorageCL.at(i);  
  9.         ReleaseStgMedium(dataEntry.m_stgMedium);  
  10.         SAFE_DELETE(dataEntry.m_stgMedium);  
  11.         SAFE_DELETE(dataEntry.m_formatEtc);  
  12.     }  
  13. }  

2.3 QueryInterface

内部实现最终也是调用API来实现:

[cpp]  view plain copy
  1. STDMETHODIMP SdkDataObject::QueryInterface(REFIID riid, void **ppv)  
  2. {  
  3.     static const QITAB qit[] =  
  4.     {  
  5.         QITABENT(SdkDataObject, IDataObject),  
  6.         { 0 },  
  7.     };  
  8.     return QISearch(this, qit, riid, ppv);  
  9. }  

2.4 AddRef和Release

方法就相对简单了,几乎所有的COM接口实现都一样。

[cpp]  view plain copy
  1. STDMETHODIMP_(ULONG) SdkDataObject::AddRef()  
  2. {  
  3.     return InterlockedIncrement(&m_lRefCount);  
  4. }  
  5.   
  6. STDMETHODIMP_(ULONG) SdkDataObject::Release()  
  7. {  
  8.     ULONG lRef = InterlockedDecrement(&m_lRefCount);  
  9.     if (0 == lRef)  
  10.     {  
  11.         delete this;  
  12.     }  
  13.     return m_lRefCount;  
  14. }  

2.5 GetData和SetData

相当重要的方法:就是向你写的Data Object要数据和传数据。内部必须把这些数据存起来。同时,这两个方法还依赖CopyMedium函数,这个用来复制数据。这个方法的实现后面会说明。GetDataHere这里没有实现,直接返回E_NOTIMPL。 

[cpp]  view plain copy
  1. STDMETHODIMP SdkDataObject::GetData(FORMATETC *pformatetcIn,   
  2.     STGMEDIUM *pmedium)  
  3. {  
  4.     if ( (NULL == pformatetcIn) || (NULL == pmedium) )  
  5.     {  
  6.         return E_INVALIDARG;  
  7.     }  
  8.   
  9.     pmedium->hGlobal = NULL;  
  10.   
  11.     int nSize = (int)m_dataStorageCL.size();  
  12.     for (int i = 0; i < nSize; ++i)  
  13.     {  
  14.         DATASTORAGE_t dataEntry = m_dataStorageCL.at(i);  
  15.         if( (pformatetcIn->tymed & dataEntry.m_formatEtc->tymed) &&  
  16.             (pformatetcIn->dwAspect == dataEntry.m_formatEtc->dwAspect) &&  
  17.             (pformatetcIn->cfFormat == dataEntry.m_formatEtc->cfFormat) )  
  18.         {  
  19.             return CopyMedium(pmedium,   
  20.                 dataEntry.m_stgMedium, dataEntry.m_formatEtc);  
  21.         }  
  22.     }  
  23.   
  24.     return DV_E_FORMATETC;  
  25. }  
  26.   
  27. STDMETHODIMP SdkDataObject::SetData(FORMATETC *pformatetc,   
  28.     STGMEDIUM *pmedium, BOOL fRelease)  
  29. {  
  30.     if ( (NULL == pformatetc) || (NULL == pmedium) )  
  31.     {  
  32.         return E_INVALIDARG;  
  33.     }  
  34.   
  35.     if ( pformatetc->tymed != pmedium->tymed )  
  36.     {  
  37.         return E_FAIL;  
  38.     }  
  39.   
  40.     FORMATETC* fetc = new FORMATETC;  
  41.     STGMEDIUM* pStgMed = new STGMEDIUM;  
  42.     ZeroMemory(fetc, sizeof(FORMATETC));  
  43.     ZeroMemory(pStgMed, sizeof(STGMEDIUM));  
  44.   
  45.     *fetc = *pformatetc;  
  46.   
  47.     if ( TRUE == fRelease )  
  48.     {  
  49.         *pStgMed = *pmedium;  
  50.     }  
  51.     else  
  52.     {  
  53.         CopyMedium(pStgMed, pmedium, pformatetc);  
  54.     }  
  55.   
  56.     DATASTORAGE_t dataEntry = { fetc, pStgMed };  
  57.     m_dataStorageCL.push_back(dataEntry);  
  58.   
  59.     return S_OK;  
  60. }  
  61.   
  62. STDMETHODIMP SdkDataObject::GetDataHere(  
  63.     FORMATETC *pformatetc , STGMEDIUM *pmedium)  
  64. {  
  65.     UNREFERENCED_PARAMETER(pformatetc);  
  66.     UNREFERENCED_PARAMETER(pmedium);  
  67.     return E_NOTIMPL;  
  68. }  

2.6 QueryGetData函数

用来查询指定的数据是否支持,这个方法跟自己提供的IsDataAvailable功能相似,只是接口复杂一点,IsDataAvailable内部也是调用QueryGetData来实现的。

[cpp]  view plain copy
  1. STDMETHODIMP SdkDataObject::QueryGetData(FORMATETC *pformatetc)  
  2. {  
  3.     if ( NULL == pformatetc )  
  4.     {  
  5.         return E_INVALIDARG;  
  6.     }  
  7.     if ( !(DVASPECT_CONTENT & pformatetc->dwAspect) )  
  8.     {  
  9.         return DV_E_DVASPECT;  
  10.     }  
  11.     HRESULT hr = DV_E_TYMED;  
  12.     int nSize = m_dataStorageCL.size();  
  13.     for (int i = 0; i < nSize; ++i)  
  14.     {  
  15.         DATASTORAGE_t dataEnrty = m_dataStorageCL.at(i);  
  16.         if ( dataEnrty.m_formatEtc->tymed & pformatetc->tymed )  
  17.         {  
  18.             if ( dataEnrty.m_formatEtc->cfFormat == pformatetc->cfFormat )  
  19.             {  
  20.                 return S_OK;  
  21.             }  
  22.             else  
  23.             {  
  24.                 hr = DV_E_CLIPFORMAT;  
  25.             }  
  26.         }  
  27.         else  
  28.         {  
  29.             hr = DV_E_TYMED;  
  30.         }  
  31.     }  
  32.     return hr;  
  33. }  

2.7 EnumFormatEtc函数

一般我是调用Shell API来实现,这个方法很重要,用来说明当前这个Data Object支持什么格式。目前这里面只支持CF_UNICODETEXT。

[cpp]  view plain copy
  1. STDMETHODIMP SdkDataObject::EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC **ppenumFormatEtc)  
  2. {  
  3.     if ( NULL == ppenumFormatEtc )  
  4.     {  
  5.         return E_INVALIDARG;  
  6.     }  
  7.     *ppenumFormatEtc = NULL;  
  8.     HRESULT hr = E_NOTIMPL;  
  9.     if ( DATADIR_GET == dwDirection )  
  10.     {  
  11.         FORMATETC rgfmtetc[] =  
  12.         {  
  13.             { CF_UNICODETEXT, NULL, DVASPECT_CONTENT, 0, TYMED_HGLOBAL },  
  14.         };  
  15.         hr = SHCreateStdEnumFmtEtc(ARRAYSIZE(rgfmtetc), rgfmtetc, ppenumFormatEtc);  
  16.     }  
  17.     return hr;  
  18. }  

IDataObject::DAdvise、IDataObject::EnumDAdvise和IDataObject::DUnadivise函数简单的返回OLE_E_ADVISENOTSUPPORTED。

[cpp]  view plain copy
  1. STDMETHODIMP SdkDataObject::DAdvise(FORMATETC *pformatetc , DWORD advf , IAdviseSink *pAdvSnk , DWORD *pdwConnection)  
  2. {  
  3.     UNREFERENCED_PARAMETER(pformatetc);  
  4.     UNREFERENCED_PARAMETER(advf);  
  5.     UNREFERENCED_PARAMETER(pAdvSnk);  
  6.     UNREFERENCED_PARAMETER(pdwConnection);  
  7.     return E_NOTIMPL;  
  8. }  
  9.   
  10. STDMETHODIMP SdkDataObject::DUnadvise(DWORD dwConnection)  
  11. {  
  12.     UNREFERENCED_PARAMETER(dwConnection);  
  13.     return E_NOTIMPL;  
  14. }  
  15.    
  16. STDMETHODIMP SdkDataObject::EnumDAdvise(IEnumSTATDATA **ppenumAdvise)  
  17. {  
  18.     UNREFERENCED_PARAMETER(ppenumAdvise);  
  19.     return E_NOTIMPL;  
  20. }  

2.8 CopyMedium实现

[cpp]  view plain copy
  1. HRESULT SdkDataObject::CopyMedium(STGMEDIUM* pMedDest, STGMEDIUM* pMedSrc, FORMATETC* pFmtSrc)  
  2. {  
  3.     if ( (NULL == pMedDest) || (NULL ==pMedSrc) || (NULL == pFmtSrc) )  
  4.     {  
  5.         return E_INVALIDARG;  
  6.     }  
  7.     switch(pMedSrc->tymed)  
  8.     {  
  9.     case TYMED_HGLOBAL:  
  10.         pMedDest->hGlobal = (HGLOBAL)OleDuplicateData(pMedSrc->hGlobal, pFmtSrc->cfFormat, NULL);  
  11.         break;  
  12.     case TYMED_GDI:  
  13.         pMedDest->hBitmap = (HBITMAP)OleDuplicateData(pMedSrc->hBitmap, pFmtSrc->cfFormat, NULL);  
  14.         break;  
  15.     case TYMED_MFPICT:  
  16.         pMedDest->hMetaFilePict = (HMETAFILEPICT)OleDuplicateData(pMedSrc->hMetaFilePict, pFmtSrc->cfFormat, NULL);  
  17.         break;  
  18.     case TYMED_ENHMF:  
  19.         pMedDest->hEnhMetaFile = (HENHMETAFILE)OleDuplicateData(pMedSrc->hEnhMetaFile, pFmtSrc->cfFormat, NULL);  
  20.         break;  
  21.     case TYMED_FILE:  
  22.         pMedSrc->lpszFileName = (LPOLESTR)OleDuplicateData(pMedSrc->lpszFileName, pFmtSrc->cfFormat, NULL);  
  23.         break;  
  24.     case TYMED_ISTREAM:  
  25.         pMedDest->pstm = pMedSrc->pstm;  
  26.         pMedSrc->pstm->AddRef();  
  27.         break;  
  28.     case TYMED_ISTORAGE:  
  29.         pMedDest->pstg = pMedSrc->pstg;  
  30.         pMedSrc->pstg->AddRef();  
  31.         break;  
  32.     case TYMED_NULL:  
  33.     default:  
  34.         break;  
  35.     }  
  36.     pMedDest->tymed = pMedSrc->tymed;  
  37.     pMedDest->pUnkForRelease = NULL;  
  38.     if(pMedSrc->pUnkForRelease != NULL)  
  39.     {  
  40.         pMedDest->pUnkForRelease = pMedSrc->pUnkForRelease;  
  41.         pMedSrc->pUnkForRelease->AddRef();  
  42.     }  
  43.     return S_OK;  
  44. }  
  45. HRESULT SdkDataObject::SetBlob(CLIPFORMAT cf, const void *pvBlob, UINT cbBlob)  
  46. {  
  47.     void *pv = GlobalAlloc(GPTR, cbBlob);  
  48.     HRESULT hr = pv ? S_OK : E_OUTOFMEMORY;  
  49.     if ( SUCCEEDED(hr) )  
  50.     {  
  51.         CopyMemory(pv, pvBlob, cbBlob);  
  52.         FORMATETC fmte = {cf, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};  
  53.         // The STGMEDIUM structure is used to define how to handle a global memory transfer.  
  54.         // This structure includes a flag, tymed, which indicates the medium  
  55.         // to be used, and a union comprising pointers and a handle for getting whichever  
  56.         // medium is specified in tymed.  
  57.         STGMEDIUM medium = {};  
  58.         medium.tymed = TYMED_HGLOBAL;  
  59.         medium.hGlobal = pv;  
  60.         hr = this->SetData(&fmte, &medium, TRUE);  
  61.         if (FAILED(hr))  
  62.         {  
  63.             GlobalFree(pv);  
  64.         }  
  65.     }  
  66.     return hr;  
  67. }  

下面给出了利用这个Data object 住剪切板复制一些数据。

这里调用了SetGlobalData函数来设置数据,上面没有给出这个实现,现在记住就行了,它内部是调用SetData来实现。设置的数据格式是CF_UNICODETEXT,因为目前这个DataObject只支持CF_UNICODETEXT格式,这个从EnumFormatEtc函数的实现就可以看出。你可以写一个控制台程序,加如下面两个方法,运行后,在记事本里按Ctrl + V,看看是不是把Hello World.粘贴了。

[cpp]  view plain copy
  1. HGLOBAL CreateGlobalHandle(IN void* ptr, int size)  
  2. {  
  3.     HGLOBAL hGlobal = NULL;  
  4.     hGlobal = GlobalAlloc(GMEM_FIXED, size);  
  5.     if (NULL != hGlobal)  
  6.     {  
  7.         LPVOID pdest = GlobalLock(hGlobal);  
  8.         if (NULL != pdest)  
  9.         {  
  10.             memcpy_s(pdest, size, ptr, size);  
  11.         }  
  12.         GlobalUnlock(hGlobal);  
  13.     }  
  14.     return hGlobal;  
  15. }  
  16.    
  17. void TestSdkDataObject()  
  18. {  
  19.     OleInitialize(NULL);  
  20.     SdkDataObject *pDataObject = new SdkDataObject(NULL);  
  21.     WCHAR *pText = L"Hello world.";  
  22.     HGLOBAL hGlobal = CreateGlobalHandle((void*)pText, sizeof(WCHAR) * (wcslen(pText) + 1));  
  23.     pDataObject->SetGlobalData(CF_UNICODETEXT, hGlobal, FALSE);  
  24.     HRESULT hr = OleSetClipboard(pDataObject);  
  25.     hr = OleFlushClipboard();  
  26.     SAFE_RELEASE(pDataObject);  
  27.     OleUninitialize();  
  28. }  

这一节,我们给出了SdkDataObject的实现,有些实现还是很简单,关键是要明白它的本质。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页
评论 1

打赏作者

小人物2014

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值