OLE文件拖放

本文详细介绍了如何使用IDropTarget接口在Windows下实现文件和文本的拖放功能,包括DragEnter、DragOver、DragLeave、Drop等接口的实现,以及在MFC中扩展CView类支持拖放的原理和方法。通过RTTI技术,可以将拖放处理函数封装到派生窗口类中,实现自定义的拖放操作。
摘要由CSDN通过智能技术生成

继续MFC OLE文件拖放2009-03-20 10:31最近在关于这方面的功能模块,所以在网上搜索了一些资料来填充一下自己吧。

使用IDropTarget接口同时支持文本和文件拖放

关于Windows的外壳扩展编程,拖放是比较简单的一种,在网上可以找到不少介绍这个技巧的文章。大部分是介绍使用MFC的COleDropTarget实现的,
我觉得一般使用COleDropTarget已经很好了,但是我习惯在一些程序模块中,完全的不使用MFC,比如纯SDK编程,还有用在ATL的时候,MFC是相当累
赘的。所以COleDropTarget在这个意义上讲不够完美。


IDropTarget是系统留给支持拖放的客户程序的一个纯虚接口,事先没有对接口的任何函数进行实现,而是让用户通过实现接口函数来接管拖放的
结果。IDropTarget接口有以下成员函数:
基本COM成员函数

QueryInterface
AddRef
Release
接管拖放事件的成员函数:

DragEnter
DragOver
DragLeave
Drop
也就是说,要在客户程序里实现以上7个函数的实体。

系统在检测到拖放发生的时候,会在合适的时候依次调用客户程序里实现的IDropTarget接口相应函数,检查用户在这些函数里返回的标志,
决定鼠标外观表现和拖放结果。
--------------------------------------------------------------------------------
实现IDropTarget接口
为此建立一个基类为IDropTarget的类:

class CDropTargetEx : public IDropTarget

IDropTarget接口在OLEIDL.h里定义,为纯虚接口。

在CDropTargetEx里依次声明接口所包含的7个函数,原形为:

HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void ** ppvObject);

ULONG STDMETHODCALLTYPE AddRef(void);

ULONG STDMETHODCALLTYPE Release(void);

HRESULT STDMETHODCALLTYPE DragOver(DWORD grfKeyState,

POINTL pt,

DWORD *pdwEffect);

HRESULT STDMETHODCALLTYPE DragEnter(IDataObject * pDataObject,

DWORD grfKeyState, POINTL pt,

DWORD * pdwEffect);

HRESULT STDMETHODCALLTYPE DragLeave(void);

HRESULT STDMETHODCALLTYPE Drop(IDataObject *pDataObj,

DWORD grfKeyState,

POINTL pt,

DWORD __RPC_FAR *pdwEffect);

(为了实现Addref计数,还有一个ULONG tb_RefCount成员变量是必须的。QueryInterface,AddRef,Release这3个函数的实现是COM知识中最基本的)

在讲解IDropTarget其他函数的具体实现之前,有必要介绍一下一个你可能永远不会直接调用但是确实存在的函数:DoDragDrop函数.此函数在某
数据源的数据被拖动的时候就被调用,它负责检测目标窗口是否支持拖放,发现目标窗口的IDropTarget接口随时跟踪鼠标和键盘的状态,根据
状态决定调用其DrageEnter,DragMove,Drop或DragLeave接口,从这些接口获取客户程序的返回值,根据这些值和用户界面以及数据源进行交互。
可以说DoDragDrop控制拖放的整个过程,我们要做的,只是将这个过程里发生的事件,接管下来并得到相应的信息,和DoDragDrop进行交互而已。
了解了这一点有助于我们理解为什么通过区区一个接口4个函数就可以实现了拖放的效果,因为系统为我们已经做了很多。

另一个非常重要的API是RegisterDragDrop,这个函数的原形是这样的:
WINOLEAPI RegisterDragDrop(HWND hwnd, IDropTarget * pDropTarget);
不用被WINOLEAPI吓到,这是一个宏:#define STDAPI EXTERN_C HRESULT STDAPICALLTYPE
也就是表示一个标准的WIN API函数,返回一个HRESULT的值。
函数RegisterDragDrop的作用是告诉系统:某个窗口(hwnd参数指定)可以接受拖放,接管拖放的接口是pDropTarget。
记住在调用RegisterDragDrop之前,一定要先调用OleInitialize初始化OLE环境。
在类CDropTargetEx里设计了一个函数
BOOL CDropTargetEx::DragDropRegister(HWND hWnd,
     DWORD AcceptKeyState=|MK_LBUTTON)

{

if(!IsWindow(hWnd))return false;
HRESULT s = ::RegisterDragDrop (hWnd,this);
if(SUCCEEDED(s))
{
   m_hTargetWnd = hWnd;
   m_AcceptKeyState = AcceptKeyState;
   return true;
}
else
{ return false; }

}

在这个函数里调用RegisterDragDrop,将this指针传入,表示本类实现了IDropTarget.,由本类接管拖放事件。另外顺便定义了一下拖放鼠标和键盘
特性常数,对这个类来说,我希望默认的只接受鼠标左键的拖放,所以,默认的AcceptKeyState值是MK_LBUTTON。相关的键盘鼠标常数
还有MK_SHIFT,MK_ALT,MK_RBOTTON,MK_MBUTTON,MK_BOTTON等几个,我想这个几个常数从字面上就可以理解它的意思了。这些常数可以用“位与”的
操作组合。

以下具体讨论IDropTarget的拖放相关接口函数(4个),这里的拖放对象以文本和文件为主。
--------------------------------------------------------------------------------


DragEnter

当你用鼠标选中了某一个文件或一段文本,并且将鼠标移到某个可以接受拖放(已经调用过RegisterDragDrop)的窗口里,DragEnter将第一时间
被调用。再看一下其原形:

HRESULT DragEnter( IDataObject * pDataObject,

     DWORD grfKeyState,

     POINTL pt,

      DWORD * pdwEffect   )

pDataobject 是从拖放的原数据中传递过来的一个IDataObject接口实例,包含数据对象的一些相关方法,可以通过此接口获得数据。

grfKeyState 为DragEnter被调用时当前的键盘和鼠标的状态,包含上面介绍过的键盘鼠标状态常数。

pt 表示鼠标所在的点。是以整个屏幕为参考坐标的。

pdwEffect 是DoDragDrop提供的一个DWORD指针,客户程序通过这个指针给DoDragDrop返回特定的状态。有效的状态包括:

DROPEFFECT_NONE=0 表示此窗口不能接受拖放。

DROPEFFECT_MOVE=1 表示拖放的结果将使源对象被删除

DROPEFFECT_COPY=2 表示拖放将引起源对象的复制。

DROPEFFECT_LINK =4 表示拖放源对象创建了一个对自己的连接

DROPEFFECT_SCROLL=0x80000000表示拖放目标窗口正在或将要进行卷滚。此标志可以和其他几个合用

对于拖放对象来说,一般只要使用DROPEFFECT_NONE和DROPEFFECT_COPY即可。

在DragEnter里要做什么呢?主要是告知拖放已经进入窗口区域,并判断是否支持某具体类型的拖放。

首先,要判断键盘的状态。在调用DragDropRegister时我传入了一个AcceptKeyState并将其保存在m_AcceptKeyState成员变量里,现在可以拿它
跟这里得到的grfKeyState比较:

if(grfKeyState!=m_AcceptKeyState )

{

可以使用C#中的`DragDrop`事件和Windows API中的`RegisterDragDrop`函数来实现窗口接收文件拖放功能。 以下是一个示例代码,可以让窗口接收文件拖放,并在控制台输出文件路径: ```csharp using System; using System.Runtime.InteropServices; using System.Windows.Forms; namespace WindowDragDrop { public partial class Form1 : Form { public Form1() { InitializeComponent(); RegisterDragDrop(this.Handle); } private void Form1_DragDrop(object sender, DragEventArgs e) { string[] files = (string[])e.Data.GetData(DataFormats.FileDrop); foreach (string file in files) { Console.WriteLine(file); } } private void Form1_DragEnter(object sender, DragEventArgs e) { if (e.Data.GetDataPresent(DataFormats.FileDrop)) e.Effect = DragDropEffects.Copy; else e.Effect = DragDropEffects.None; } [DllImport("ole32.dll")] private static extern int RegisterDragDrop(IntPtr hwnd, IDropTarget target); private void RegisterDragDrop(IntPtr handle) { IDropTarget target = new FileDropTarget(this); RegisterDragDrop(handle, target); } private class FileDropTarget : IDropTarget { private readonly Form1 _form; public FileDropTarget(Form1 form) { _form = form; } public void OnDragEnter(DragEventArgs e) { _form.OnDragEnter(e); } public void OnDragOver(DragEventArgs e) { _form.OnDragOver(e); } public void OnDragLeave() { _form.OnDragLeave(); } public void OnDrop(DragEventArgs e) { _form.OnDrop(e); } } } [ComImport] [Guid("00000122-0000-0000-C000-000000000046")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal interface IDropTarget { void OnDragEnter(DragEventArgs e); void OnDragOver(DragEventArgs e); void OnDragLeave(); void OnDrop(DragEventArgs e); } } ``` 在窗口的构造函数中,调用了`RegisterDragDrop`函数,将窗口句柄和窗口的`IDropTarget`对象传递给了`RegisterDragDrop`函数,从而实现了窗口的文件拖放功能。同时,窗口中还需要实现`DragEnter`和`DragDrop`事件的处理函数,用来处理文件拖放的具体操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值