• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            Dict.CN 在線詞典, 英語(yǔ)學(xué)習(xí), 在線翻譯

            學(xué)海苦作舟,書(shū)山勤為徑

            留下點(diǎn)回憶

            常用鏈接

            統(tǒng)計(jì)

            積分與排名

            Denoise

            English study

            Web技術(shù)

            數(shù)據(jù)壓縮

            一些連接

            最新評(píng)論

            第三部分:實(shí)現(xiàn)IDataObject(OLE drag&drop之旅)

            上一張我們著重介紹了怎么樣使用OLEIDataObject來(lái)訪問(wèn)windows粘貼板。本章主要實(shí)現(xiàn)一個(gè)IDataObject接口,然后使用我們完成的數(shù)據(jù)對(duì)象來(lái)存儲(chǔ)文本“Hello World”到粘貼板中。

            創(chuàng)建一個(gè)COM接口-IDataObject

            為了創(chuàng)建一個(gè)COM對(duì)象,我們需要定義一個(gè)實(shí)現(xiàn)所有這些函數(shù)的C++類,并且讓COM的虛函數(shù)表為我們自動(dòng)包含,我們使用C++類繼承:

            class CDataObject : public IDataObject
            {
            Public:
                // IUnknown members
                HRESULT __stdcall QueryInterface (REFIID iid, void ** ppvObject);
                ULONG   __stdcall AddRef (void);
                ULONG   __stdcall Release (void);
                    
                // IDataObject members
                HRESULT __stdcall GetData (FORMATETC *pFormatEtc, STGMEDIUM *pmedium);
                HRESULT __stdcall GetDataHere (FORMATETC *pFormatEtc, STGMEDIUM *pmedium);
                HRESULT __stdcall QueryGetData (FORMATETC *pFormatEtc);
                HRESULT __stdcall GetCanonicalFormatEtc (FORMATETC *pFormatEct, FORMATETC *pFormatEtcOut);
                HRESULT __stdcall SetData (FORMATETC *pFormatEtc, STGMEDIUM *pMedium,  BOOL fRelease);
                HRESULT __stdcall EnumFormatEtc (DWORD dwDirection, IEnumFORMATETC **ppEnumFormatEtc);
                HRESULT __stdcall DAdvise (FORMATETC *pFormatEtc, DWORD advf, IAdviseSink *, DWORD *);
                HRESULT __stdcall DUnadvise (DWORD      dwConnection);
                HRESULT __stdcall EnumDAdvise (IEnumSTATDATA **ppEnumAdvise);
                    stgmed
                // Constructor / Destructor
                CDataObject (FORMATETC *fmtetc, STGMEDIUM *, int count);
                ~CDataObject ()
            private:
            LONG m_lRefCount;
            int LookupFormatEtc(FORMATETC *pFormatEtc);
            };

            上面列出了所有IDataObject成員,包括IUnknown接口成員,這是因?yàn)槲覀儸F(xiàn)在需要實(shí)現(xiàn)整個(gè)COM對(duì)象,因此每個(gè)成員必須正確的包含。

            由于IUnknown函數(shù)我們?cè)谇懊嬉呀?jīng)介紹了,我們繼續(xù)介紹IDataObject函數(shù)。有些好的消息,同時(shí)也有些壞的消息;好的消息是,不是所有餓函數(shù)都需要實(shí)現(xiàn),在IDataObject9個(gè)函數(shù)中,我們僅僅需要實(shí)現(xiàn)3個(gè)來(lái)支持OLE的拖放操作,因此顯著節(jié)省了我們的工作量。

            壞的消息是:一般我們已經(jīng)實(shí)現(xiàn)了IDataObject方法,我們需要實(shí)現(xiàn)完全獨(dú)立的COM接口-IEnumFORMATETC接口。然而到這步還有很大的距離,因此讓我們以一個(gè)簡(jiǎn)單分配新IDataObject的實(shí)例作為一個(gè)開(kāi)始。

            構(gòu)造IDataObject

            IDataObject的主要任務(wù)是允許一個(gè)消費(fèi)者查詢數(shù)據(jù),這些查詢從QueryDataEnumFormatEtc調(diào)用來(lái)發(fā)起的,因此,IDataObject需要知道存儲(chǔ)什么樣的數(shù)據(jù)格式,并且在消費(fèi)者需要數(shù)據(jù)的時(shí)候,它能夠提供。

            我們因此需要找到一些辦法來(lái)以FORMATETC結(jié)構(gòu)的形式用真正的數(shù)據(jù)片來(lái)組裝IDataObject且說(shuō)明數(shù)據(jù)是什么。

            IDataObjectC++類構(gòu)造函數(shù)的時(shí)候組裝,為了更彈性,可能需要添加內(nèi)部幫助程序來(lái)執(zhí)行這個(gè)任務(wù),但對(duì)于我們簡(jiǎn)單實(shí)現(xiàn)僅在構(gòu)造函數(shù)中使用。

            CDataObject::CDataObject (FORMATETC *fmtetc, STGMEDIUM *stgmed, int count)
            {
                // reference count must ALWAYS start at 1
                m_lRefCount    = 1;
                m_nNumFormats  = count;
             
                m_pFormatEtc   = new FORMATETC[count];
                m_pStgMedium   = new STGMEDIUM[count];
             
                for(int i = 0; i < count; i++)
                {
                    m_pFormatEtc[i] = fmtetc[i];
                    m_pStgMedium[i] = stgmed[i];
                }
            }

            構(gòu)造函數(shù)執(zhí)行兩個(gè)重要的任務(wù),首先是初始化COM對(duì)象引用記數(shù)為1。我看到過(guò)許多不正確的COM代碼,他們初始化記數(shù)為0COM規(guī)約明確地聲明,一個(gè)COM對(duì)象必須以“1”作為生命周期的開(kāi)始,如果你記得,一個(gè)記數(shù)為0COM對(duì)象應(yīng)該被刪除,因此它應(yīng)該從不應(yīng)該被初始化為這個(gè)值。

            第二個(gè)任務(wù)是在類構(gòu)造函數(shù)中做一個(gè)私有的FORMATETCSTGMEDIUM的副本。數(shù)據(jù)對(duì)象不是每個(gè)STGMEDIUM結(jié)構(gòu)體內(nèi)部的所有者,它純粹是引用并且在請(qǐng)求調(diào)用GetData的時(shí)候復(fù)制數(shù)據(jù)。

            創(chuàng)建IDataObject對(duì)象

            現(xiàn)在我們有一個(gè)定義良好的IDataObject構(gòu)造函數(shù),我可以寫(xiě)一個(gè)包裝函數(shù)來(lái)隱藏類的細(xì)節(jié):

            HRESULT CreateDataObject (FORMATETC *fmtetc, STGMEDIUM *stgmeds, UINT count, IDataObject **ppDataObject)
            {
                if(ppDataObject == 0)
                    return E_INVALIDARG;
             
                *ppDataObject = new CDataObject (fmtetc, stgmeds, count);
             
                return (*ppDataObject) ? S_OK: E_OUTOFMEMORY;
            }

            現(xiàn)在創(chuàng)建一個(gè)IDataObject變的非常簡(jiǎn)單:

            FORMATETC fmtetc = {CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
            STGMEDIUM stgmed = {TYMED_HGLOBAL, {0}, 0};
             
            stgmed.hGlobal = StringToHandle ("Hello, World!");
             
            IDataObject *pDataObject;
             
            CreateDataObject (&fmtetc, &stgmed, 1, &pDataObject);

            許多IDataObject的實(shí)現(xiàn)包含許多接口內(nèi)部執(zhí)行內(nèi)存分配的程序指定編碼;在這個(gè)實(shí)現(xiàn)后面的思想是可以提供一個(gè)用于各種程序的通用IDataObject。好了,在創(chuàng)建數(shù)據(jù)對(duì)象之前有點(diǎn)工作需要做就是創(chuàng)建FORMATETCSTGMEDIUM結(jié)構(gòu),但這很容易被隔離,并且不會(huì)污染接口編碼。

            IDataObject::QueryGetData

            該成員函數(shù)在某程序想檢查IDataObject看是否包含指定類型的數(shù)據(jù)時(shí)候調(diào)用。一個(gè)指向FORMATETC結(jié)構(gòu)的指針作為一個(gè)參數(shù),且IDataObject::QueryGetData來(lái)檢查這個(gè)結(jié)構(gòu)且返回一個(gè)值來(lái)指示請(qǐng)求的數(shù)據(jù)是否可用。

            HRESULT __stdcall IDataObject::QueryGetData(FORMATETC *pFormatEtc)
            {
                return (LookupFormatEtc(pFormat) == -1) ? DV_E_FORMATETC : S_OK;
            }

            這個(gè)例子中的QueryGetData函數(shù)非常簡(jiǎn)單,我們放棄私有協(xié)助函數(shù)-LookupFormatEtc的所有工作:

            int CDataObject::LookupFormatEtc(FORMATETC *pFormatEtc)
            {
                // 輪流檢查格式看是否能找到匹配的格式
                for(int i = 0; i < m_nNumFormats; i++)
                {
                    if((m_pFormatEtc[i].tymed    &  pFormatEtc->tymed)   &&
                        m_pFormatEtc[i].cfFormat == pFormatEtc->cfFormat &&
                        m_pFormatEtc[i].dwAspect == pFormatEtc->dwAspect)
                    {
                        // return index of stored format
                        return i;
                    }
                }
             
                // error, format not found
                return -1;
            }

            上面的函數(shù)盡量在我們數(shù)據(jù)對(duì)象的可用結(jié)構(gòu)中查找一個(gè)與指定FORMATETC結(jié)構(gòu)匹配的對(duì)象,如果找到一個(gè)匹配的,就簡(jiǎn)單的返回相應(yīng)m_pFormatEtc數(shù)組的索引,如果找不到,返回-1表示一個(gè)錯(cuò)誤。

            注意,在if從句中的位與操作符:

            if( m_pFormatEtc[i].tymed & pFormatEtc->tymed ) 

            AND操作符用在這里是因?yàn)?/SPAN>FORMATETC::tymed成員實(shí)際上是一個(gè)位標(biāo)志,它能夠包含不止一個(gè)值;例如:QueryGetData的調(diào)用者可以完全指定一個(gè)FORMATETC::tymed值(TYMED_HGLOBAL|TYMED_ISTREAM)就意味著你支持HGLOBALIStream嗎?

            IDataObject::GetData

            GetData函數(shù)和QueryGetData有許多相似之處,除了如果支持請(qǐng)求的數(shù)據(jù)格式,它必須返回指定的存儲(chǔ)類型。

            HRESULT __stdcall CDataObject::GetData (FORMATETC *pFormatEtc, STGMEDIUM *pStgMedium)
            {
                int idx;
                // try to match the specified FORMATETC with one of our supported formats
                if((idx = LookupFormatEtc(pFormatEtc)) == -1)
                    return DV_E_FORMATETC;
                // found a match - transfer data into supplied storage medium
                pMedium->tymed           = m_pFormatEtc[idx].tymed;
                pMedium->pUnkForRelease  = 0;
                // copy the data into the caller's storage medium
                switch(m_pFormatEtc[idx].tymed)
                {
                case TYMED_HGLOBAL:
                    pMedium->hGlobal     = DupGlobalMem(m_pStgMedium[idx].hGlobal);
                    break;
                default:
                    return DV_E_FORMATETC;
                }
                return S_OK;
            }

            同樣要調(diào)用內(nèi)部協(xié)助函數(shù)LookupFormatEtc來(lái)檢查是否支持請(qǐng)求的數(shù)據(jù)格式,如果支持,相應(yīng)的STGMEDIUM數(shù)據(jù)被復(fù)制到調(diào)用者提供的結(jié)構(gòu)。

            注意,現(xiàn)在調(diào)用DupGlobalMem程序,這是一個(gè)協(xié)助函數(shù),它返回指定HGLOBAL內(nèi)存的HANDLE的副本,并且必須返回部分,因?yàn)槊總€(gè)GetData調(diào)用都要求一個(gè)新的數(shù)據(jù)副本。

            HGLOBAL DupGlobalMemMem (HGLOBAL hMem)
            {
                DWORD   len    = GlobalSize (hMem);
                PVOID   source = GlobalLock (hMem);
                PVOID   dest   = GlobalAlloc (GMEM_FIXED, len);
                memcpy (dest, source, len);
                GlobalUnlock (hMem);
                return dest;
            }

            我們需要同樣的程序來(lái)支持TYMED_xxx存儲(chǔ)類型,但現(xiàn)在我們?cè)O(shè)想實(shí)現(xiàn)的支持格式是IStream。

            IDataObject::EnumFormatEtc

            這是最后需要自己動(dòng)手的成員,不幸的是這個(gè)成員函數(shù)實(shí)現(xiàn)如此簡(jiǎn)單,但也要求我們寫(xiě)IEnumFORMATETC對(duì)象。

            HRESULT __stdcall CDataObject::EnumFormatEtc (DWORD dwDirection, IEnumFORMATETC **ppEnumFormatEtc)
            {
                // OLE僅僅支持得到方向成員
                if(dwDirection == DATADIR_GET)
                {
                    // WIN2K下,你可以調(diào)用AIP函數(shù)SHCreateStdEnumFmtEtc來(lái)完成,但為了支持//所有的window平臺(tái),我們需要實(shí)現(xiàn)IEnumFormatEtc。
                    return CreateEnumFormatEtc(m_NumFormats, m_FormatEtc, ppEnumFormatEtc);
                }
                else
                {
                    // the direction specified is not supported for drag+drop
                    return E_NOTIMPL;
                }
            }

            看到上面的代碼,你會(huì)提到SHCreateStdEnumFmtEtc這個(gè)API調(diào)用,它能夠代表我們創(chuàng)建IEnumFORMATETC接口,不幸的是,這個(gè)API僅僅在WIN2K上可用,因此,我們需要提供其他創(chuàng)建IEnumFORMATETC對(duì)象。

            因此下面的旅程中,我們將提供一個(gè)CreateEnumFormatEtc的完整實(shí)現(xiàn),來(lái)代替Shell API調(diào)用。

            不支持的IDataObject函數(shù)

            仍然有一些IDataObject函數(shù)需要實(shí)現(xiàn),而同時(shí)每個(gè)函數(shù)必須是一個(gè)有效的程序,有個(gè)簡(jiǎn)單的辦法可以指定給OLE,我們不支持這些拖放操作以外的函數(shù)。

            IDataObject::DAdvise、IDataObject::EnumDAdviseIDataObject::DUnadivise函數(shù)簡(jiǎn)單的返回OLE_E_ADVISENOTSUPPORTED。

            HRESULT CDataObject::DAdvise (FORMATETC *pFormatEtc, DWORD advf, IAdviseSink *pAdvSink, DWORD *pdwConnection)
            {
                return OLE_E_ADVISENOTSUPPORTED;
            }
             
            HRESULT CDataObject::DUnadvise (DWORD dwConnection)
            {
                return OLE_E_ADVISENOTSUPPORTED;
            }
             
            HRESULT CDataObject::EnumDAdvise (IEnumSTATDATA **ppEnumAdvise)
            {
                return OLE_E_ADVISENOTSUPPORTED;
            }

            GetDataHere只需要實(shí)現(xiàn)IStreamIStorage接口來(lái)支持?jǐn)?shù)據(jù)對(duì)象,在我們的例子中,我們只支持HGLOBAL數(shù)據(jù),因此返回DATA_E_FORMATETC是一個(gè)明智的選擇。

             

            HRESULT CDataObject::GetDataHere (FORMATETC *pFormatEtc, STGMEDIUM *pMedium)
            {
            return DATA_E_FORMATETC;
            }

            SetDataGetCanonicalFormatEtc也只要簡(jiǎn)單的實(shí)現(xiàn),本例中可以返回E_NOTIMPL值,即使我們返回錯(cuò)誤的值,一個(gè)GetCanonicalFormatEtc記名票據(jù),輸出的FORMATETC結(jié)構(gòu)ptd成員應(yīng)該是0。

            HRESULT CDataObject::GetCanonicalFormatEtc (FORMATETC *pFormatEct, FORMATETC *pFormatEtcOut)
            {
                // Apparently we have to set this field to NULL even though we don't do anything else
                pFormatEtcOut->ptd = NULL;
                return E_NOTIMPL;
            }
             
            HRESULT CDataObject::SetData (FORMATETC *pFormatEtc, STGMEDIUM *pMedium,  BOOL fRelease)
            {
                return E_NOTIMPL;
            }

            添加數(shù)據(jù)到粘貼板

            好了,這里有一個(gè)簡(jiǎn)單那的程序用來(lái)通過(guò)OLE和數(shù)據(jù)對(duì)象來(lái)添加“Hello World”到Windows的粘貼板。

            #include <windows.h>
            int main(void)
            {
                OleInitialize (0);
                IDataObject *pDataObject;
                FORMATETC fmtetc = {CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
                STGMEDIUM stgmed = {TYMED_HGLOBAL, {0}, 0};
                stgmed.hGlobal = StringToHandle ("Hello, World!”, -1);
                // create the data object
                if (CreateDataObject(&fmtetc, &stgmed, 1, &pDataObject) == S_OK)
                {
                    // add data to the clipboard
                    OleSetClipboard (pDataObject);
                    OleFlushClipboard ();
                    pDataObject->Release();
                }
                // cleanup
                ReleaseStgMedium (&stgmed);
                OleUninitialize ();
                return 0;
            }
            不幸的是這個(gè)程序不能工作,因?yàn)槲覀冞€沒(méi)有實(shí)現(xiàn)IEnumFORMATETCCreateEnumFormatEtc函數(shù)。

            posted on 2006-03-03 10:08 笨笨 閱讀(7783) 評(píng)論(3)  編輯 收藏 引用 所屬分類: OLE Drag&Drop

            評(píng)論

            # re: 第三部分:實(shí)現(xiàn)IDataObject(OLE drag&drop之旅) 2006-11-09 10:47 heng

            對(duì)這個(gè)有異議:
            構(gòu)造函數(shù)執(zhí)行兩個(gè)重要的任務(wù),首先是初始化COM對(duì)象引用記數(shù)為1。我看到過(guò)許多不正確的COM代碼,他們初始化記數(shù)為0。

            證據(jù):
            來(lái)自 atlcom.h
            class CComObjectRootBase
            {
            public:
            CComObjectRootBase()
            {
            m_dwRef = 0L;
            }
            ....
            }

              回復(fù)  更多評(píng)論   

            # re: 第三部分:實(shí)現(xiàn)IDataObject(OLE drag&drop之旅) 2007-08-29 17:45 wu

            COM 對(duì)象構(gòu)造時(shí) m_cRef = 0;
            在QueryInterface 內(nèi)部 會(huì) m_cRef++;  回復(fù)  更多評(píng)論   

            # re: 第三部分:實(shí)現(xiàn)IDataObject(OLE drag&drop之旅) 2013-04-11 09:52 張瑞強(qiáng)

            沒(méi)有析構(gòu)函數(shù)申請(qǐng)了大量?jī)?nèi)存  回復(fù)  更多評(píng)論   

            久久婷婷五月综合97色直播 | 波多野结衣久久| 国产伊人久久| 久久亚洲中文字幕精品一区四 | 无码专区久久综合久中文字幕| 亚洲人成精品久久久久| avtt天堂网久久精品| 久久91这里精品国产2020| 无码乱码观看精品久久| 91精品国产高清91久久久久久| 久久综合综合久久97色| 99精品国产免费久久久久久下载| 亚洲人成网亚洲欧洲无码久久| 狠狠色噜噜狠狠狠狠狠色综合久久| 欧美久久久久久午夜精品| 日韩精品久久久肉伦网站| 99久久这里只精品国产免费| 久久久久人妻精品一区| 伊人久久大香线蕉亚洲| 91久久精品无码一区二区毛片| 丰满少妇高潮惨叫久久久| 久久毛片免费看一区二区三区| 人人狠狠综合久久88成人| 午夜精品久久久久成人| 青青青国产精品国产精品久久久久 | 精品久久久久香蕉网| 欧美亚洲国产精品久久| 久久精品成人欧美大片| 久久久网中文字幕| 久久综合综合久久97色| 久久精品国产秦先生| 无码精品久久久久久人妻中字| 日韩精品久久久久久久电影| 久久精品夜色噜噜亚洲A∨| 亚洲成人精品久久| 99久久这里只有精品| 日韩人妻无码精品久久久不卡| 久久精品国产色蜜蜜麻豆| 久久天堂AV综合合色蜜桃网| 中文字幕热久久久久久久| 久久99精品国产麻豆宅宅|