• <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 在線詞典, 英語學(xué)習(xí), 在線翻譯

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

            留下點回憶

            常用鏈接

            統(tǒng)計

            積分與排名

            Denoise

            English study

            Web技術(shù)

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

            一些連接

            最新評論

            第四部分:枚舉FORMATETC(OLE drag&drop之旅)

            本章注重于實現(xiàn)一個暴露IEnumFORMATETC接口的COM對象,這里有兩部分代碼可以下載。第一包含一個完整的通用的IEnumFORMATETC實現(xiàn),你可以將它用到你的程序中。另一部分代碼是一個叫做IDataObject Viewer的所有代碼。這是PlatformSDK同名程序的替代品,它是一個怎么樣使用IEnumFORMATETC接口的基本介紹,而不是寫這個接口。更重要的是,它在調(diào)式OLE拖放代碼是非常有用,你可以拖動任何格式的IDataObject到它上面,它會顯示顯示數(shù)據(jù)包含的可用格式。

            IEnumFORMATETC接口在開始拖放時經(jīng)常不會注意到,在許多情況下它是不必要的,但為了你的IDataObject可以在所有條件下保證100%工作,提供該接口的完整實現(xiàn)是必要的。

            IEnumFORMATETC 方法

            描述

            Next

            返回枚舉中的下一個FORMATETC結(jié)構(gòu)體

            Skip

            跳過指定數(shù)量的FORMATETC structures (例如,不返回他們).

            Reset

            返回枚舉的開始狀態(tài)

            Clone

            返回與當(dāng)前結(jié)構(gòu)相同的IEnumFORMATETC 接口, 并且有相同的低層狀態(tài)

            下圖應(yīng)該可以能夠幫助你描述IEnumFORMATETC接口:

            dragdrop07.gif


            枚舉包含3項,枚舉索引初始化在第一項(索引是0)。

            1.         Next方法在索引0時返回第一個FORMATETC結(jié)構(gòu),并且枚舉指針指向索引1

            2.         Skip方法以參數(shù)2來調(diào)用,跳過兩個位置,到達(dá)枚舉的尾部(索引3)。

            3.         Reset方法返回到索引的開始(索引0)。

            IEnumFORMATETC實際上非常簡單,僅僅需要實現(xiàn)四個方法:

            class CEnumFormatEtc : public IEnumFORMATETC
            {
            public:
                //
                // IUnknown members
                //
                HRESULT __stdcall  QueryInterface (REFIID iid, void ** ppvObject);
                ULONG   __stdcall  AddRef (void);
                ULONG   __stdcall  Release (void);
                //
                // IEnumFormatEtc members
                //
                HRESULT __stdcall  Next  (ULONG celt, FORMATETC * rgelt, ULONG * pceltFetched);
                HRESULT __stdcall  Skip  (ULONG celt); 
                HRESULT __stdcall  Reset (void);
                HRESULT __stdcall  Clone (IEnumFORMATETC ** ppEnumFormatEtc);
             
                //
                // Construction / Destruction
                //
                CEnumFormatEtc(FORMATETC *pFormatEtc, int nNumFormats);
                ~CEnumFormatEtc();
             
            private:
             
                LONG        m_lRefCount;        // Reference count for this COM interface
                ULONG       m_nIndex;           // current enumerator index
                ULONG       m_nNumFormats;      // number of FORMATETC members
                FORMATETC * m_pFormatEtc;       // array of FORMATETC objects
            };

            構(gòu)造一個IEnumFORMATETC對象

            IEnumFORMATETC最復(fù)雜的事情是創(chuàng)建對象,在這時候?qū)崿F(xiàn)COM方法真的非常簡單,好了,創(chuàng)建一個對象是非茶館內(nèi)容易的,因為我所需要的就是使用C++操作符new來做這件事情:

            IEnumFORMATETC *pEnumFormatEtc = new CEnumFormatEtc (fmtetc, numfmts);
            CEnumFormatEtc::CFormatEtc (FORMATETC *pFormatEtc, int nNumFormats)
            {
                m_lRefCount   = 1;
                m_nIndex      = 0;
                m_nNumFormats = nNumFormats;
                m_pFormatEtc  = new FORMATETC[nNumFormats];
                // make a new copy of each FORMATETC structure
                for(int i = 0; i < nNumFormats; i++)
                {
                    DeepCopyFormatEtc (&m_pFormatEtc[i], &pFormatEtc[i]);
                }
            }

            我們來看以下這個C++構(gòu)造函數(shù)做了什么,它有兩個參數(shù):一個指向FORMATETC結(jié)構(gòu)的數(shù)組,另外一個是表示數(shù)組中有多少元素的整數(shù)。

            第一行初始化對象引用記數(shù),這是所有COM對象的標(biāo)準(zhǔn),我們應(yīng)該非常熟悉它,因此我這里不在做更多的介紹。

            下一步就是初始化枚舉狀態(tài),成員變量m_nIndex表示枚舉中的當(dāng)前位置,因此它以0開始是很自然的,同樣,m_nNumFormats變量用來表示枚舉的結(jié)尾,有了這兩個變量,我們可以跟蹤枚舉當(dāng)前的位置和結(jié)束位置。

            最重要的一步是分配參數(shù)中的FORMATETC結(jié)構(gòu)體的一個新數(shù)組副本。一個數(shù)據(jù)被分配(m_pFormatEtc)其保存所有要被枚舉的結(jié)構(gòu)體,每個枚舉需要有自己的私有FORMATETC結(jié)構(gòu)的緩存,關(guān)鍵細(xì)節(jié)是復(fù)制FORMATETC結(jié)構(gòu)的方法,這里,我們引入一個叫DeepCopyFormatEtc新的函數(shù)。

            void DeepCopyFormatEtc(FORMATETC *dest, FORMATETC *source)
            {
                // copy the source FORMATETC into dest
                *dest = *source;
                    
                if(source->ptd)
                {
                    // allocate memory for the DVTARGETDEVICE if necessary
                    dest->ptd = (DVTARGETDEVICE*)CoTaskMemAlloc(sizeof(DVTARGETDEVICE));
             
                    // copy the contents of the source DVTARGETDEVICE into dest->ptd
                    *(dest->ptd) = *(source->ptd);
                }
            }

            函數(shù)的第一行非常簡單:

            *dest = *source;

            這個實際上是一個標(biāo)準(zhǔn)的C函數(shù)memcpy。實際上,這幾乎在所有的情況都是需要的,因為他能正確的執(zhí)行一個二進(jìn)制的結(jié)構(gòu)體到結(jié)構(gòu)體的復(fù)制,問題是當(dāng)源FORMATETC::ptd成員的已經(jīng)被初始化為指向一個DVTAGETDEVIDE結(jié)構(gòu),就不能正確復(fù)制了。

            僅僅在FORMATETC上執(zhí)行memcpy是不夠的,因為兩個FORMATETC結(jié)構(gòu)體都指向原來的DVTARGETDEVICE;因此我們私有的結(jié)構(gòu)體復(fù)制函數(shù)是需要的。

            IEnumFORMATETC::Next文檔聲明調(diào)用這使用CoTaskMemFree這個API來釋放DVTARGETDEVICE結(jié)構(gòu)體,邏輯上意味著這個結(jié)構(gòu)必須首先已經(jīng)使用CoTaskMemAlloc來分配了棵,因此這就是深度復(fù)制所做的,使用CoTaskMemAlloc來分配一個新的DVTARGETDEVICE結(jié)構(gòu)體,并且設(shè)置dest->ptd指向原來的,那么source->DVTARGETDEVICE結(jié)構(gòu)體就被復(fù)制到新的指針上了。

            清理一個IEnumFORMATETC對象

            CEnumFormatEtc類的C++析構(gòu)函數(shù)必須清理所有在構(gòu)造函數(shù)分配的內(nèi)存:

            CEnumFormatEtc::~CEnumFormatEtc()
            {
                // first free any DVTARGETDEVICE structures
                for(ULONG i = 0; i < m_nNumFormats; i++)
                {
                    if(m_pFormatEtc[i].ptd)
                        CoTaskMemFree(m_pFormatEtc[i].ptd);
                }
                // now free the main array
                delete[] m_pFormatEtc;
            }

            這是一個簡單的任務(wù),調(diào)用CoTaskMemFree來釋放所有在構(gòu)造函數(shù)中分配的DVTAGETDEVICE結(jié)構(gòu)體,一旦這些已經(jīng)釋放完了,m_pFormatEtc數(shù)組也應(yīng)該被釋放。

            取代SHCreateStdEnumFmtEtc

            你可能會問,在該指南中,我們?yōu)槭裁磿恢边@么煩心來,因為SHCreateStdEnumFmtEtc API調(diào)用可以用來創(chuàng)建IEnumFORMATETC接口,但不幸的是,它只能在WINDOS2000以上的版本使用,看原型:

            HRESULT SHCreateStdEnumFmtEtc(UINT cfmt, const FORMATETC afmt[], IEnumFORMATETC **ppenumFormatEtc);

            因此如果你不準(zhǔn)備向下兼容老的版本Windows的拖放操作,否則我們總是需要實現(xiàn)IEnumFORMATETC。我們需要做的就是寫一個SHCreateStdEnumFmtEtcdrop-in替代版本,我們可以在僅僅支持windows2000的時候很容易切換,我們的版本是這樣的:

            HRESULT CreateEnumFormatEtc (UINT cfmt, FORMATETC *afmt, IEnumFORMATETC **ppEnumFormatEtc)
            {
                if (cfmt == 0 || afmt == 0 || ppEnumFormatEtc == 0)
                    return E_INVALIDARG;
                *ppEnumFormatEtc = new CEnumFormatEtc (afmt, cfmt);
                return (*ppEnumFormatEtc) ? S_OK: E_OUTOFMEMORY;
            }

            函數(shù)非常簡單,因為所有的工作都在CEnumFormatEtc構(gòu)造函數(shù)中調(diào)用,我們需要做的就是創(chuàng)建一個類的實例,然后以最后一個參數(shù)返回;其余的代碼是錯誤檢查。

            使用API是也很簡單:

            FORMATETC fmtetc = {CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
            IEnumFORMATETC *pEnumFormatEtc; 
            CreateEnumFormatEtc (1, &fmtetc, &pEnumFormatEtc);

            這似乎是枚舉一些簡單FORMATETC結(jié)構(gòu)的許多工作,但這是值得的,因為我們的COM枚舉器現(xiàn)在真正的獨立了,剩下的接口就非常簡單了。

            IEnumFORMATETC::Reset

            這個成員非常簡單,設(shè)置枚舉到開始的位置:

            HRESULT CEnumFormatEtc::Reset (void)
            {
                m_nIndex = 0;
                return S_OK;
            }

            上面的實現(xiàn)可以自解釋。

            IEnumFORMATETC::Skip

            該實現(xiàn)直接向前移動,簡直不需要解釋:

            HRESULT IEnumFORMATETC::Skip (ULONG celt)
            {
                m_nIndex += celt;
                return (m_nIndex <= m_nNumFormats) ? S_OK : S_FALSE;
            }

            該函數(shù)僅僅向前移動枚舉指定單元。注意,盡管這里沒有保證索引在枚舉范圍內(nèi),但一個返回值用來指示是否前進(jìn)的太多。

            IEnumFORMATETC::Clone

            Clone函數(shù)起先看起來優(yōu)點神秘;盡管我很少看到這個函數(shù)調(diào)用,它實際上很容易實現(xiàn):

            HRESULT IEnumFORMATETC::Clone(IEnumFORMATETC **ppEnumFormatEtc)
            {
                HRESULT hResult;
                // make a duplicate enumerator
                hResult = CreateEnumFormatEtc(m_nNumFormats, m_pFormatEtc, ppEnumFormatEtc);
                if(hResult == S_OK)
                {
                    // manually set the index state
                    ((CEnumFormatEtc *)*ppEnumFormatEtc)->m_nIndex = m_nIndex;
                }
                return hResult;
            }

            上面代碼很簡單地創(chuàng)建了一個IEnumFORMATETC接口的實例,使用我們前面寫的CreateEnumFormatEtc函數(shù);使用當(dāng)前的枚舉內(nèi)部狀態(tài),因此結(jié)果就是復(fù)制接口的當(dāng)前內(nèi)部狀態(tài)。

            if從句中的轉(zhuǎn)型看起來有點復(fù)雜,其用來保留枚舉的索引位置,轉(zhuǎn)型是必須的,因為IEnumFORMATETC接口并可以訪問內(nèi)部變量,然而,我們知道ppEnumFormatEtc實際上就是一個CEnumFormatEtc,所以這個轉(zhuǎn)換能安全的執(zhí)行。轉(zhuǎn)換操作看起來復(fù)雜的原因是我們必須引用ppEnumFormatEtc參數(shù)來訪問指向IEnumFORMATETC的指針。

            IEnumFORMATETC::Next

            Next成員函數(shù)比其他的稍微棘手一點:

            HRESULT CEnumFormatEtc::Next(ULONG celt, FORMATETC *pFormatEtc, ULONG *pceltFetched)
            {
                ULONG copied = 0;
                // copy the FORMATETC structures into the caller's buffer
                while (m_nIndex < m_nNumFormats && copied < celt) 
                {
                    DeepCopyFormatEtc (&pFormatEtc [copied], &m_pFormatEtc [m_nIndex]);
                    copied++;
                    m_nIndex++;
                }
                // store result
                if(pceltFetched != 0) 
                    *pceltFetched = copied;
                // did we copy all that was requested?
                return (copied == celt) ? S_OK : S_FALSE;
            }

            這個函數(shù)看起來有點復(fù)雜,但可以被分解成三個重要的操作;主要的部分是while循環(huán)部分,它負(fù)責(zé)復(fù)制FORMATETC結(jié)構(gòu)(使用深度復(fù)制程序),循環(huán)僅僅復(fù)制范圍內(nèi)的元素到提供的緩沖區(qū)中。

            第二部分是返回實際復(fù)制的相數(shù),且返回一個錯誤值來指示是否所有需要復(fù)制的元素都被復(fù)制了。

            最后一部分就是錯誤代碼來指示復(fù)制指定數(shù)量的項數(shù)的操作成功和失敗。

            posted on 2006-03-03 10:12 笨笨 閱讀(4670) 評論(4)  編輯 收藏 引用 所屬分類: OLE Drag&Drop

            評論

            # re: 第四部分:枚舉FORMATETC(OLE drag&drop之旅) 2006-03-04 13:04

            very good
            樓主的文章很有功力  回復(fù)  更多評論   

            # re: 第四部分:枚舉FORMATETC(OLE drag&drop之旅) 2006-03-04 22:50 笨笨

            謝謝,但我必須說清楚,這是翻譯的文章  回復(fù)  更多評論   

            # re: 第四部分:枚舉FORMATETC(OLE drag&drop之旅) 2006-06-09 16:25 QQ8080761

            太厲害了,找了半天的資料,看英文都看疼了,總算找到一篇好的,多謝多謝  回復(fù)  更多評論   

            # re: 第四部分:枚舉FORMATETC(OLE drag&drop之旅) 2008-04-29 16:51 紅馬天下

            不錯!  回復(fù)  更多評論   

            久久精品国产精品亚洲下载 | 国内精品久久久久影院网站| 久久66热人妻偷产精品9| 久久亚洲国产午夜精品理论片| 国内精品久久久久久中文字幕| 日本欧美国产精品第一页久久| 久久伊人五月丁香狠狠色| 国产亚洲精久久久久久无码| 国产成人久久777777| 日韩精品久久无码中文字幕| 久久久噜噜噜久久| 精品无码久久久久国产| 国产精品乱码久久久久久软件 | 亚洲AV无码久久| 蜜桃麻豆www久久国产精品| 亚洲精品乱码久久久久久按摩 | 伊人久久成人成综合网222| 99麻豆久久久国产精品免费| 精品伊人久久久| 久久免费视频6| 狠狠人妻久久久久久综合| 久久婷婷久久一区二区三区| 亚洲AV无一区二区三区久久| 久久婷婷色香五月综合激情| 久久线看观看精品香蕉国产| 精品国产乱码久久久久久1区2区| 久久久这里有精品| 欧美精品丝袜久久久中文字幕 | 精品无码久久久久久久久久 | 久久精品国产91久久综合麻豆自制 | 国产精品熟女福利久久AV| 99999久久久久久亚洲| 国产亚洲精品久久久久秋霞| 久久人人爽人人人人片av| 亚洲国产成人久久综合一区77| 欧美性大战久久久久久 | 人妻丰满AV无码久久不卡| 亚洲国产另类久久久精品黑人| 久久久www免费人成精品| 性欧美大战久久久久久久久| 婷婷伊人久久大香线蕉AV|