Dict.CN 在线词典, 英语学习, 在线翻译

学海苦作舟,书山勤为径

留下点回忆

常用链接

统计

积分与排名

Denoise

English study

Web技术

数据压缩

一些连接

最新评论

第二部分:OLE数据传输(OLE Drag和Drop随笔)

欢迎来到OLE拖放指南第二部分;本部分的目的在于解释在OLE环境中,程序之间怎么样表示和传输数据。

OLE数据传输的核心是IDataObject COM接口,一个IDataObject提供从一个程序到另一个程序传输和访问数据的方法。最通用的OLE数据传输是窗口粘贴板,当然也有拖放。IDataObject是一到多个数据的有效的COM包装。

在我们调查IDataObject任何细节之前,两个重要的数据结构你必须熟悉:FORMATETCSTGMEDIUM接口,他们用来描述和存储OLE数据。

描述OLE数据

FORMATETC接口(发音“format et cetera”)用来表示IDataObject提供(或接收)的数据类型,是标准window粘贴板格式(CF_TEXT等)的扩展,因此除了基本的粘贴板格式之外,还包含了数据怎么样rendered和存储。

typedef struct
{
    CLIPFORMAT      cfFormat;   // 粘贴板格式 
    DVTARGETDEVICE *ptd;        // (NULL)  rendering的目标设备
    DWORD           dwAspect;    // (DV_CONTENT) rendering的详细程度
    LONG            lindex;      // (-1)  在数据通过页面边界分割的时候使用
    DWORD           tymed;       // 用于数据传输的存储媒体(HGLOBAL,IStream
} FORMATETC;

FORMATETC结构的成员如下描述:

cfFormat:粘贴板格式,用来表示FORMATETC结构。可以是内建的格式(例如:CF_TEXTCF_BITMAP)或者用RegisterClipboardFormat注册的自定义格式。

Ptd:指向DVTARGETDEVICE结构,提供已经rendered数据的设备信息。正常的粘贴板操作和拖放操作都是NULL

dwAspect:描述用户怎么样render数据的大量细节。通常这个是DVASPECT_CONTECT,表示全内容,但也可以描述较少的信息,例如:图标。

Lindex:仅仅在当数据通过页面边界被分割的时候使用,它不用于简单的OLE传输,因此该值几乎总是-1

Typemed:这是一个有趣的成员;因为其描述了用于存储数据的存储媒体类型。该成员名字自词组“Type of Medium”;该值在window.h中定义的TYMED_XXX等值。

因此有了这个数据结构,OLE已经提供了一个描述消费者什么样的数据已经怎么样render这个数据。

存储OLE数据

结构体STGMEDIUM(STORAGE MEDIUM的缩写)提供一个用来存储数据的容器,因此叫存储媒体:

typedef struct 
{
    DWORD tymed;
    union
    {
        HBITMAP        hBitmap;
        HMETAFILEPICT  hMetaFilePict;
        HENHMETAFILE   hEnhMetaFile;
        HGLOBAL        hGlobal;
        LPWSTR         lpszFileName;
        IStream        *pstm;
        IStorage       *pstg;
    }; 
    IUnknown *pUnkForRelease;
} STGMEDIUM;

这个结构定义看起来比较复杂,但是有用的仅仅三个成员,因为未命名联合合并了所有内容作为一个实体共享同样的存储空间。

1.         tymed:这个成员必须和FORMATETC结构相同,这个成员指定已经存储的媒体类型,例如,全局数据(TYMED_HGLOBAL),IStreamTYPED_ISTREAM)等等。相应的联合中的元素是数据的句柄。

2.         hBitmap/hGlobal等:实际的数据,仅仅他们中的一个是有效的,这依赖于tymed的值。

3.         pUnkForRelease:一个可选的指针,指向IUnknown接口,数据的接收方应该调用其Release方法。当这个字段是NULL时,接收方有责任释放内存句柄。ReleaseStgMedium API调用在这里非常有用,它负责释放STGMEDIUMS的数据内容,因此实际上我们不需要做什么。

STGMEDIUM结构是传统的windows HGLOBAL内存句柄的扩展,同时支持HGLOBAL(且一直是最常用的),同时支持许多其他的类型,最有用的是IStreamIStorage通用COM接口。

总之,结构体FORMATETCSTGMEDIUM一起用来描述和存储OLE数据实体的。FORMATETC通常用来从IDataObject请求指定类型的数据,同时STGMEDIUM结构用来接收和保存请求的数据。

传输OLE数据

IDataObject接口提供了从一个程序到另一个程序传递数据的方法,IDataObject在两个情况下非常有用:粘贴板和拖放。如果设计精细,可以用一个COM对象来同时实现粘贴板和拖放操作。

下面的表列出了IDataObject成员函数,按照他们在接口虚表中出现的顺序。为了简化的原因,IUnknown方法(AddRefReleaseQueryInterface)没有列出。

IDataObject方法

描述

GetData

RenderFORMATETC结构体中描述的数据,并通过STGMEDIUM结构体来传递数据

GetDataHere

RenderFORMATETC结构体中的数据,并通过调用者分配的STGMEDIUM结构体传输数据。

QueryGetData

判断数据对象是否可以renderFORMATETC结构中描述的数据

GetCanonicalFormatEtc

提供一个潜在不同的但逻辑上相同的FORMATETC结构体。

SetData

提供一个通过FORMATECT结构和STGMEDIUM结构描述的源数据对象。

EnumFormatEtc

创建并返回一个IEnumFORMATETC接口的指针来枚举数据对象支持的FORMATETC对象。

DAdvise

创建一个在数据对象和通知接收器之间的连接,因此通知接收器能接收到数据对象中通知的改变。

DUnadvise

销毁一个前面使用DAdvise方法安装的通知

EnumDAdvise

创建和返回一个指向枚举当前通知连接的接口指针。

这个表看起来很漂亮,我们也看到EnumFormatEtc方法并发现我们不得不同时实现IEnumFORMATETC接口,它有13个成员函数,不包括IUnknown方法,到这里我们还没有考虑IDropSourceIDropTarget

庆幸的是,为了简化OLE拖放,我们仅仅需要实现GetDataQueryGetDataEnumFormatEtc,因此这节省了我们许多工作。

使用IDataObject来访问粘贴板

为了使在我们的OLE旅程中放松一下,下面我们来看一个简单的通过OLE来访问粘贴板的例子:

WINOLEAPI OleGetClipboard(IDataObject ** ppDataObj);

这个简单的Windows API调用用来返回一个IDataObject,它提供用来一个干净地访问WINDOWS粘贴板内容的好接口。注意,我们在本例中不需要实现IDataObject接口,我们仅仅需要知道接口怎么样工作的,一个简单的访问粘贴板内容的程序如下:

#include <windows.h>
 
int main(void)
{
    IDataObject *pDataObject;
    // Initialize COM and OLE
    if(OleInitialize(0) != S_OK)
        return 0;
    // Access the data on the clipboard
    if(OleGetClipboard(&pDataObject) == S_OK)
    {
        // access the IDataObject using a separate function
        DisplayDataObject (pDataObject);
        pDataObject->Release();
    }
    // Cleanup
    OleUninitialize();
    return 0;
}

OLE API调用非常简单,且它是直接来访问IDataObject对象:

void DisplayDataObject(IDataObject *pDataObject)
{
    FORMATETC fmtetc = { CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
    STGMEDIUM stgmed;
 
    // ask the IDataObject for some CF_TEXT data, stored as a HGLOBAL
    if(pDataObject->GetData(&fmtetc, &stgmed) == S_OK)
    {
        // 我们必须锁定HGLOBAL句柄,因为我们不能确信这是否是一个GEM_FIXED数据
        char *data = GlobalLock(stgmed.hGlobal);
        printf("%s\n", data);
        // cleanup
        GlobalUnlock(stgmed.hGlobal);
        ReleaseStgMedium(&stgmed);
    }
}

上面的代码演示了最常用的访问IDataObject的方法,数据通过调用IDataObject::GetData来请求,我们构造一个FORMATETC对象,它指定了我们想要访问的数据的类型,在这个例子中,标准的CF_TEXT数据缓冲区以HGLOBAL内存对象来存储。

数据返回到我们提供的STGMEDIUM结构体中,一旦我们锁定并显示数据,清理和调用标准的ReleaseStgMedium API来释放存储在STGMEDIUM结构中的数据就简单了。

注意,代码中仅仅当文本被选择到粘贴板的时候才工作,也就是说,如果没有CF_TEXT被存储到粘贴板,粘贴板的IDataObject::GetData程序调用会失败,我们什么也不打印。

posted on 2006-03-01 15:47 笨笨 阅读(4416) 评论(2)  编辑 收藏 引用 所属分类: OLE Drag&Drop

评论

# re: 第二部分:OLE数据传输(OLE Drag和Drop随笔) 2012-05-02 14:32 飞飞龙

学习了  回复  更多评论   

# re: 第二部分:OLE数据传输(OLE Drag和Drop随笔) 2012-09-11 10:46 fgg

很好的东西  回复  更多评论   


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理