青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

Dict.CN 在線詞典, 英語學習, 在線翻譯

學海苦作舟,書山勤為徑

留下點回憶

常用鏈接

統計

積分與排名

Denoise

English study

Web技術

數據壓縮

一些連接

最新評論

第三部分:實現IDataObject(OLE drag&drop之旅)

上一張我們著重介紹了怎么樣使用OLEIDataObject來訪問windows粘貼板。本章主要實現一個IDataObject接口,然后使用我們完成的數據對象來存儲文本“Hello World”到粘貼板中。

創建一個COM接口-IDataObject

為了創建一個COM對象,我們需要定義一個實現所有這些函數的C++類,并且讓COM的虛函數表為我們自動包含,我們使用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接口成員,這是因為我們現在需要實現整個COM對象,因此每個成員必須正確的包含。

由于IUnknown函數我們在前面已經介紹了,我們繼續介紹IDataObject函數。有些好的消息,同時也有些壞的消息;好的消息是,不是所有餓函數都需要實現,在IDataObject9個函數中,我們僅僅需要實現3個來支持OLE的拖放操作,因此顯著節省了我們的工作量。

壞的消息是:一般我們已經實現了IDataObject方法,我們需要實現完全獨立的COM接口-IEnumFORMATETC接口。然而到這步還有很大的距離,因此讓我們以一個簡單分配新IDataObject的實例作為一個開始。

構造IDataObject

IDataObject的主要任務是允許一個消費者查詢數據,這些查詢從QueryDataEnumFormatEtc調用來發起的,因此,IDataObject需要知道存儲什么樣的數據格式,并且在消費者需要數據的時候,它能夠提供。

我們因此需要找到一些辦法來以FORMATETC結構的形式用真正的數據片來組裝IDataObject且說明數據是什么。

IDataObjectC++類構造函數的時候組裝,為了更彈性,可能需要添加內部幫助程序來執行這個任務,但對于我們簡單實現僅在構造函數中使用。

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];
    }
}

構造函數執行兩個重要的任務,首先是初始化COM對象引用記數為1。我看到過許多不正確的COM代碼,他們初始化記數為0COM規約明確地聲明,一個COM對象必須以“1”作為生命周期的開始,如果你記得,一個記數為0COM對象應該被刪除,因此它應該從不應該被初始化為這個值。

第二個任務是在類構造函數中做一個私有的FORMATETCSTGMEDIUM的副本。數據對象不是每個STGMEDIUM結構體內部的所有者,它純粹是引用并且在請求調用GetData的時候復制數據。

創建IDataObject對象

現在我們有一個定義良好的IDataObject構造函數,我可以寫一個包裝函數來隱藏類的細節:

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;
}

現在創建一個IDataObject變的非常簡單:

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的實現包含許多接口內部執行內存分配的程序指定編碼;在這個實現后面的思想是可以提供一個用于各種程序的通用IDataObject。好了,在創建數據對象之前有點工作需要做就是創建FORMATETCSTGMEDIUM結構,但這很容易被隔離,并且不會污染接口編碼。

IDataObject::QueryGetData

該成員函數在某程序想檢查IDataObject看是否包含指定類型的數據時候調用。一個指向FORMATETC結構的指針作為一個參數,且IDataObject::QueryGetData來檢查這個結構且返回一個值來指示請求的數據是否可用。

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

這個例子中的QueryGetData函數非常簡單,我們放棄私有協助函數-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;
}

上面的函數盡量在我們數據對象的可用結構中查找一個與指定FORMATETC結構匹配的對象,如果找到一個匹配的,就簡單的返回相應m_pFormatEtc數組的索引,如果找不到,返回-1表示一個錯誤。

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

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

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

IDataObject::GetData

GetData函數和QueryGetData有許多相似之處,除了如果支持請求的數據格式,它必須返回指定的存儲類型。

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;
}

同樣要調用內部協助函數LookupFormatEtc來檢查是否支持請求的數據格式,如果支持,相應的STGMEDIUM數據被復制到調用者提供的結構。

注意,現在調用DupGlobalMem程序,這是一個協助函數,它返回指定HGLOBAL內存的HANDLE的副本,并且必須返回部分,因為每個GetData調用都要求一個新的數據副本。

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;
}

我們需要同樣的程序來支持TYMED_xxx存儲類型,但現在我們設想實現的支持格式是IStream

IDataObject::EnumFormatEtc

這是最后需要自己動手的成員,不幸的是這個成員函數實現如此簡單,但也要求我們寫IEnumFORMATETC對象。

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

看到上面的代碼,你會提到SHCreateStdEnumFmtEtc這個API調用,它能夠代表我們創建IEnumFORMATETC接口,不幸的是,這個API僅僅在WIN2K上可用,因此,我們需要提供其他創建IEnumFORMATETC對象。

因此下面的旅程中,我們將提供一個CreateEnumFormatEtc的完整實現,來代替Shell API調用。

不支持的IDataObject函數

仍然有一些IDataObject函數需要實現,而同時每個函數必須是一個有效的程序,有個簡單的辦法可以指定給OLE,我們不支持這些拖放操作以外的函數。

IDataObject::DAdviseIDataObject::EnumDAdviseIDataObject::DUnadivise函數簡單的返回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只需要實現IStreamIStorage接口來支持數據對象,在我們的例子中,我們只支持HGLOBAL數據,因此返回DATA_E_FORMATETC是一個明智的選擇。

 

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

SetDataGetCanonicalFormatEtc也只要簡單的實現,本例中可以返回E_NOTIMPL值,即使我們返回錯誤的值,一個GetCanonicalFormatEtc記名票據,輸出的FORMATETC結構ptd成員應該是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;
}

添加數據到粘貼板

好了,這里有一個簡單那的程序用來通過OLE和數據對象來添加“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;
}
不幸的是這個程序不能工作,因為我們還沒有實現IEnumFORMATETCCreateEnumFormatEtc函數。

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

評論

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

對這個有異議:
構造函數執行兩個重要的任務,首先是初始化COM對象引用記數為1。我看到過許多不正確的COM代碼,他們初始化記數為0。

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

  回復  更多評論   

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

COM 對象構造時 m_cRef = 0;
在QueryInterface 內部 會 m_cRef++;  回復  更多評論   

# re: 第三部分:實現IDataObject(OLE drag&drop之旅) 2013-04-11 09:52 張瑞強

沒有析構函數申請了大量內存  回復  更多評論   

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            久久精品夜夜夜夜久久| 亚洲免费av电影| 国产欧美视频一区二区三区| 亚洲女性喷水在线观看一区| 久久久精彩视频| 精品二区视频| 欧美精品尤物在线| 亚洲综合另类| 欧美高清日韩| 亚洲欧美国产精品va在线观看| 国产欧美精品在线播放| 美女国内精品自产拍在线播放| 在线天堂一区av电影| 国产精品午夜春色av| 快射av在线播放一区| 宅男噜噜噜66国产日韩在线观看| 久久久精品动漫| 亚洲人成亚洲人成在线观看| 国产精品私人影院| 欧美成人蜜桃| 久久精品国产99国产精品澳门| 亚洲区在线播放| 欧美一区二区| 亚洲日本欧美日韩高观看| 国产精品一区免费视频| 欧美成人一品| 欧美有码在线视频| 妖精视频成人观看www| 欧美1级日本1级| 欧美在线短视频| 一区二区三区黄色| 在线日韩一区二区| 国产日本精品| 欧美小视频在线观看| 免费人成精品欧美精品| 久久国产精品久久w女人spa| 中文欧美字幕免费| 亚洲激情第一页| 免费高清在线一区| 久久岛国电影| 亚洲欧美日本伦理| 中文国产成人精品| 亚洲美女尤物影院| 亚洲国产精品999| 国产视频在线一区二区| 欧美性jizz18性欧美| 欧美日韩国产成人高清视频| 另类av一区二区| 久久久精品网| 久久精品在线观看| 久久国产精品久久国产精品| 午夜精品视频在线观看一区二区| 在线亚洲美日韩| 日韩手机在线导航| 亚洲精品美女久久7777777| 欧美激情一区二区三区成人 | 一区二区日本视频| 亚洲片在线观看| 最新中文字幕亚洲| 亚洲国产日韩美| 亚洲国内高清视频| 亚洲国产另类久久精品| 亚洲高清av| 亚洲国产精品黑人久久久| 欧美理论视频| 一区二区三区鲁丝不卡| 99视频国产精品免费观看| 日韩视频在线永久播放| 一区二区三区鲁丝不卡| 亚洲在线中文字幕| 亚洲欧美日韩人成在线播放| 午夜精品国产更新| 久久se精品一区二区| 久久精品盗摄| 久久一区二区三区超碰国产精品| 久久免费黄色| 欧美91大片| 欧美日韩精品| 国产精品入口麻豆原神| 国产午夜精品美女毛片视频| 国语精品中文字幕| 亚洲电影免费| 一本大道久久a久久精品综合| 这里只有精品电影| 欧美中文字幕精品| 免费亚洲视频| 亚洲精品视频一区| 亚洲欧美日韩另类精品一区二区三区 | 亚洲免费在线电影| 欧美在线视频一区二区| 久久久爽爽爽美女图片| 欧美成人亚洲成人| 亚洲乱码国产乱码精品精| 亚洲调教视频在线观看| 欧美综合国产精品久久丁香| 久热re这里精品视频在线6| 欧美精品18+| 国产精品一区二区a| 好吊色欧美一区二区三区视频| 亚洲欧洲一区| 亚洲欧美中文另类| 你懂的国产精品| av72成人在线| 久久成人国产| 欧美激情综合五月色丁香小说| 国产精品久久久爽爽爽麻豆色哟哟| 国产亚洲精品aa| 亚洲免费观看在线观看| 久久福利毛片| 亚洲人成人一区二区在线观看| 亚洲一区二区三区影院| 久久亚洲影音av资源网| 欧美日韩在线播放三区四区| 狠狠v欧美v日韩v亚洲ⅴ| 亚洲精品免费网站| 欧美在线视频一区二区| 亚洲日本成人网| 欧美在线一级va免费观看| 欧美激情精品久久久久久久变态 | 欧美另类人妖| 国产亚洲综合性久久久影院| 亚洲人永久免费| 久久精品一区二区三区不卡| 亚洲精品九九| 久久亚洲影音av资源网| 国产伦理一区| 一区二区三区精密机械公司| 裸体素人女欧美日韩| 亚洲一区二区久久| 欧美激情综合亚洲一二区| 韩国三级在线一区| 欧美一区二区三区日韩| 亚洲精品综合| 免费日韩精品中文字幕视频在线| 国产视频亚洲精品| 亚洲欧美春色| 妖精视频成人观看www| 欧美国产91| 在线日韩视频| 久久综合九色综合网站| 亚洲欧美国产高清| 欧美视频在线观看一区| 妖精成人www高清在线观看| 欧美成人免费网| 久久久久久久久久久一区| 国产欧美短视频| 午夜精品一区二区三区电影天堂 | 亚洲欧美中文另类| 欧美日韩美女在线| 亚洲精品社区| 欧美激情精品久久久| 久久国产精品亚洲va麻豆| 国产精品无人区| 午夜欧美理论片| 一区二区三区福利| 欧美日韩一二三四五区| 亚洲乱码久久| 亚洲风情在线资源站| 久久综合伊人77777麻豆| 在线免费观看视频一区| 另类亚洲自拍| 久久午夜精品| 亚洲高清资源| 欧美国产乱视频| 美女在线一区二区| 亚洲国产视频直播| 亚洲国产精品va在看黑人| 欧美v日韩v国产v| 亚洲精品一二三| 亚洲人成绝费网站色www| 欧美日韩不卡在线| 一区二区三区回区在观看免费视频| 亚洲另类在线一区| 国产精品久久久久久久久免费樱桃 | 影音先锋亚洲电影| 免费亚洲一区二区| 久久综合色播五月| 亚洲激情综合| 亚洲欧洲视频在线| 欧美日韩精品是欧美日韩精品| 亚洲视频精选在线| 中国成人亚色综合网站| 国产精品国产自产拍高清av王其| 亚洲欧美一级二级三级| 性久久久久久久久| 影音先锋久久| 亚洲精品乱码| 国产美女精品在线| 老妇喷水一区二区三区| 免费欧美日韩| 亚洲综合首页| 久久精品30| 日韩一区二区免费高清| 亚洲视频1区2区| 国语自产精品视频在线看抢先版结局| 欧美成人日韩| 欧美午夜宅男影院| 麻豆精品传媒视频| 欧美连裤袜在线视频| 久久精品国产亚洲a|