OLE Drag&Drop 介绍
一、基本概念
拖放方式有两种,一种是OLE拖放(这个比较复杂)和文件管理器拖放。它们利用了不同的机制。
二、文件管理器拖放
只能处理文件名,比如,你可以在资源管理器中选择一个或多个文件,拖到应用程序上面(这个程序的dwExStyle要加上WS_EX_ACCEPTFILES),那么这个窗体就会收到一个WM_DROPFILES消息。它主要用到下面几个API函数:DragQueryFile、DragQueryPoint、DragFinish。函数原型如下:
a)UINT DragQueryFile(HDROP hDrop, UINT iFile, LPTSTR lpszFile, UINT cch)
b)BOOL DragQueryPoint(HDROP hDrop, LPPOINT lppt)
c)void DragFinish(HDROP hDrop)
一般用法如下:
void CListCtrlEx::OnDropFiles(HDROP hDrop)
{
char szFilePathName[_MAX_PATH+1] = {0};
UINT nNumOfFiles = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0); //得到文件个数
for (UINT nIndex=0 ; nIndex< nFileCount; ++nIndex)
{
DragQueryFile(hDrop, nIndex, szFilePathName, _MAX_PATH); //得到文件名
}
DragFinish(hDrop);
}
由于这里不重点讲这种方式的拖放,所以具体的用法讲参考MSDN,上面都有详细说明。
三、OLE拖放原理
拖放是用来描述用鼠标装数据从一个地方传输到另一个地方,OLE拖放比文件管理拖放更加通用得多,能在不同的应用程序中交互任务类型的数据(先要向系统注册该类型)。每一个拖放操作都须包含三个元素,当然,这些都是COM对象,需要我们去实现这些接口。
1)IDropSource,表示拖放操作的源,它包含了拖放目标要使用的数据(IDataObject),它能在拖放的过程生成一些可视化的反馈,如设置鼠标样式等。
2)IDropTarget,表示拖放操作的目标,它决定拖放的效果、接收任何合法数据、给拖放源一些反馈等。
3)IDataObject,表示拖放源与目标之间传输的数据。
注意了,一个应用程序不需要支持所有的COM接口。如果你的程序想做为一个拖放目标,那么你就实现IDropTarget接口,同样的,如果你需要支持作为数据源的程序,那么就应该实现IDropSource和IDataObject接口。当然,程序也可以同时实现这三个接口,从而可以在同一程序中支持拖放操作。
上面的图描述了拖放操作中所需要的关键组件。左边的表示拖放操作的源,它已经包含了两个对象,一个是IDropSource,另一个是IDataObject,最终是通过API函数DoDragDrop来发现拖放操作。
右边描述了拖放操作的目标,它需要实现IDropTarget接口。这个目标中,它能接收IDataObject对象。当鼠标拖动到目标窗体时,OLE传递一个IDataObject接口到目标对象上面,这是源传给目标的数据,它不能以任何方式得到一个副本。这目标这边它可以根据IDataObject得到该数据格式,从而判断这种格式是不是目标能识别的,如果不能识别,就不接收。它本质上需要调用RegisterDragDrop来把当前目标窗体注册成一个拖放目标。
注意,上面的图所示,源和目标可以是同一进程,也可以是不同进程。在使用之前,需要调用COM和OLE的初始化,使用完后,再调用其反初始化。
WINOLEAPI OleInitialize(LPVOID pvReserved);
WINOLEAPI OleUninitialize();
四、开始拖放
上面也提到了,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
);
在调用这个方法时,就要把你实现的源(IDragSource)和数据(IDataObject)传给他,至于如何创建源和数据的对象,在后面介绍。
当调用DoDragDrop时,它会进入一个模态的消息循环,用来监视鼠标和简单的消息,它是会阻塞住的。
五、注册目标
要想一个窗体成为一个拖放的接收方,它必须调用RegisterDragDrop函数来注册。其原型如下:
WINOLEAPI RegisterDragDrop(
HWND hwnd, // Handle to a window that can accept drops
IDropTarget * pDropTarget // Pointer to object that is to be target of drop
);
它的第一个参数就是目标窗体的句柄。
与它作用相反的函数是RevokeDragDrop函数,释放注册时用到的IDropTarget接口对象。这个函数在窗体销毁时应该调用。
WINOLEAPI RevokeDragDrop(