OLE Drap/Drop(1)
转自:http://www.handytech.cn/main/ArticleShow.asp?ArtID=244
该指南的目的在于读者能够在他们自己的程序实现完整的拖拽功能。自Window95以来,Drag和Drop已经成为Window程序的一个标准功能,随着COM和OLE成为主流技术,程序能和Window Shell甚至其他Window程序无缝交互。这个弹性是以高昂的代价为基础的,说的婉转点,写一个COM或OLE支持的程序完全是一个噩梦。
本指南目的在于帮助你轻松克服写一个OLE接口支持的拖拽程序的困难。通常,我们使用纯WIN32 API基础。然而,我会使用C++而不是C,因为C++是写COM接口程序的首选语言;我也会解释怎么样以简单的方式转换成C语言。
我有意以几个部分来写这个指南,主要的原因是太多的信息。另外,Drag-and-drop组件也使他们有各自不同的主题,因此我采用了这种方法。指南的第一部分(实际上就是该部分)简单介绍OLE 拖拽,后面的指南着重于拖拽;第2和3部分介绍OLE数据传输IDataObject接口。第4部分看一下IEnumFORMATETC接口,第5和6部分介绍drag源和drop目标。
推荐阅读
我强烈推荐你研究一下下面的信息,因为我是从那里学习COM、OLE拖拽的。
1. msdn.microsoft.com
每个win32相关的起始之处。
2. Inside OLE 2nd edition
该书中有许多有用的信息,被作为OLD的圣经。它有点老了,但包含每个你需要知道的东西。MSDN中包含了该书的一个软COPY,也许一直再那里;因特耐特上也有许多PDF和CHM的版本。
3. ftp://ftp.microsoft.com/softlib/msfiles
微软的FTP服务器包含几百个以前的资源,到目前为止我发现的最有用的东西是两个小文件:drgdrps.exe和drgdrpt.exe。他们是自解压的ZIP文件,包含了简单的drop源和drop目标程序的完整代码,为了可以简单的访问这些文件,你仅仅需要输入下面的命令:
ftp ftp.microsoft.com
username "ftp"
password "ftp"
1. cd softlib/mslfiles
bin
get drgdrps.exe
get drgdrpt.exe
bye
4. 微软技术论文-OLE for Idiots系列,What OLE is Really about等,这些论文虽然很老了,但他们在今天依然有用。在GOOGLE中可以轻松查询到。
OLE Drag和Drop
拖放是用来描述使用鼠标将数据从一个地方传输到另一个地方的短语。
每个拖放操作包含三个元素,当然这些元素是COM对象,需要支持拖放功能的程序都必须实现这三个元素。
1. IDropSource接口表示拖放操作的源。IDropSource包含产生可视化的方法,取消或完成拖放操作的方法。
2. IDropTarget接口用来表示拖放操作的目标对象。
3. IDataObject接口用来表示拖放操作过程中传输的数据。
注意,一个程序不需要支持所有的COM接口;如果你想定义一个drop目标,那么仅仅实现IDropTarget接口,同样,如果一个需要支持作为数据源的程序应该支持IDropSource和IDataObject接口。当然,程序也可以实现三个接口,从而在同一个程序中支持拖放操作。
上面的图描述了拖放操作中需要支持的关键组件;花点时间来理解一下图的内容。左边的方块是拖放操作的出发点,它已经创建了两个COM对象,每个暴露一个接口(IDataObject和IDropSource),OLE通过他们来执行拖放操作。
右边的方块表示拖放操作的目标,其创建一个COM对象(IDropTarget接口)。当鼠标被拖动过目标窗口时,OLE传递一个IDataObject接口到目标对象,这是源暴露给目标的数据对象。对象不能以任何方式得到一个副本,仅仅COM接口变为可用。当目标从数据对象提取数据时,OLE/COM运行时负责函数调用已经通过进程边界的数据传输。
上面的例子中,源和目标可以是同一个进程,也可以是不同的进程。在那里实现并不重要,因为OLE运行时(实际上是COM)负责数据对象在目标进程中激活。
开始拖放
任何程序想要使用OLE函数时首先需要做的是在启动的时候调用OleInitialize并且在结束的时候调用OleUninitialize。这么说不是很准确,最好说想要使用OLE的线程必须调用这些函数,因为COM和OLE必须在每个线程中被初始化和释放。
WINOLEAPI OleInitialize (LPVOID pvReserved);
WINOLEAPI OleUninitialize ();
非常核心的OLE拖放是一个API调用DoDragDrop,函数原型如下:
WINOLEAPI DoDragDrop(
IDataObject * pDataObject, // Pointer to the data object
IDropSource * pDropSource, // Pointer to the source
DWORD dwOKEffect, // Effects allowed by the source
DWORD * pdwEffect // Pointer to effects on the source
);
一个程序想初始化拖放操作,他必须首先调用这个函数,但在调用DoDragDrop之前,两个重要的步骤必须完成。
在调用DoDragDrop ,IDataObject和IDropSource对象被拖放操作的发起者创建。创建这两个对象并不是琐碎的事情,因此我们在下一部分介绍。另外我们这里一直没有提到创建任何GUI相关的对象(例如窗口),实际上一个drop源是独立于任何窗口的单独实体,即使拖放操作在窗口程序处理WM_MOUSEMOVE消息的时候初始化的。
当DoDragDrop被调用时,进入一个摸态的消息循环,用来监视鼠标和简单的消息。
接收Drag和Drop数据
一个程序想作为拖放操作的接收方,它必须调用RegisterDragDrop函数,当然,这个程序也必须调用与源程序一样调用OleInitialize/OleUninitialize函数。
WINOLEAPI RegisterDragDrop(
HWND hwnd, // Handle to a window that can accept drops
IDropTarget * pDropTarget // Pointer to object that is to be target of drop
);
看一下上面函数原型显示了最后一个拖放操作组件-IDropTarget COM接口。RegisterDragDrop同时要求一个窗口的句柄。该窗口被OLE运行时注册,因此,当鼠标拖过该窗口时,OLE能调用IDropTarget接口的方法来通知拥有该窗口的程序正在进行一个拖放操作。
当这个窗口被销毁时,应该调用RevokeDragDrop API:
WINOLEAPI RevokeDragDrop(
HWND hwnd // Handle to a window that can accept drops
);
这个API用OLE来反初始化指定的窗口,并且释放注册时使用的IDropTarget接口和进程中的DropTarget对象。