[VC]使用IDropTarget接口同时支持文本和文件拖放(1)(zt)

2008-09-22 23:58这是一篇好文章,我收藏在此。 我是引用于:http://blog.csdn.net/vcbear/archive/2002/01/25/5990.aspx 所有版权为原作者所有。 vcbear 关于 参考了 作为学习笔记,就有了这么一篇文字,以抛砖引玉: Windows的外壳扩展编程,拖放是比较简单的一种,在网上可以找到不少介绍这个技巧的文章。大部分是介绍使用MFC的COleDropTarget实现的,我觉得一般使用COleDropTarget已经很好了,但是我习惯在一些程序模块中,完全的不使用MFC,比如纯SDK编程,还有用在ATL的时候,MFC是相当累赘的。所以COleDropTarget在这个意义上讲不够完美。MSDN以及www.CodeProject.com的相关文章和代码(by Thomas Blenkers)之后,我发现拖放实际上主要使用了IDropTarget的接口方法,非常简单,不妨直接面对原始IDropTarget实现自己的拖放类。 IDropTarget 基本 是系统留给支持拖放的客户程序的一个纯虚接口,事先没有对接口的任何函数进行实现,而是让用户通过实现接口函数来接管拖放的结果。IDropTarget接口有以下成员函数:COM成员函数 QueryInterface AddRef Release 接管拖放事件的成员函数: DragEnter DragEnter DragOver DragLeave Drop 也就是说,要在客户程序里实现以上 系统在检测到拖放发生的时候,会在合适的时候依次调用客户程序里实现的 7个函数的实体。IDropTarget接口相应函数,检查用户在这些函数里返回的标志,决定鼠标外观表现和拖放结果。 --------------------------------------------------------------------------------   实现 class CDropTargetEx : public IDropTarget IDropTarget接口IDropTarget的类: IDropTarget接口在OLEIDL.h里定义,为纯虚接口。CDropTargetEx里依次声明接口所包含的7个函数,原形为: (为了实现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) { HRESULT s = ::RegisterDragDrop (hWnd,this); if(SUCCEEDED(s)) { m_hTargetWnd = hWnd; m_AcceptKeyState = AcceptKeyState; return true; } else { return false; } } if(!IsWindow(hWnd))return false; 在这个函数里调用 以下具体讨论 RegisterDragDrop,将this指针传入,表示本类实现了IDropTarget.,由本类接管拖放事件。另外顺便定义了一下拖放鼠标和键盘特性常数,对这个类来说,我希望默认的只接受鼠标左键的拖放,所以,默认的AcceptKeyState值是MK_LBUTTON。相关的键盘鼠标常数还有MK_SHIFT,MK_ALT,MK_RBOTTON,MK_MBUTTON,MK_BOTTON等几个,我想这个几个常数从字面上就可以理解它的意思了。这些常数可以用“位与”的操作组合。IDropTarget的拖放相关接口函数(4个),这里的拖放对象以文本和文件为主。 -------------------------------------------------------------------------------- 在 首先 当你用鼠标选中了某一个文件或一段文本,并且将鼠标移到某个可以接受拖放(已经调用过 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比较: 如果键盘和鼠标的状态和我期望的不一样,那么 然后 这里要介绍的是两个关键的结构体 FORMATETC 其定义为 是OLE数据交换的一个关键结构,对某种设备,数据,和相关媒体做了格式上的描述。 typedef struct tagFORMATETC { CLIPFORMAT cfFormat; DVTARGETDEVICE *ptd; DWORD dwAspect; LONG lindex; DWORD tymed; } FORMATETC, *LPFORMATETC; 在这里我们最感兴趣的是 一个典型的 cfFormat和tymed两个数据。cfFormat是标准的“粘帖板”数据类型比如CF_TEXT之类。tymed表示数据所依附的媒介,比如内存,磁盘文件,存储对象等等。其他的成员可以参见MSDN。FORMATETC结构变量定义如下: FORMATETC cFmt = {(CLIPFORMAT) CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; IDataObject提供了一个GetData接口来获取其实例里包含的数据,比如: GetData传入cFmt,以指出所感兴趣的数据,并将返回在stgMedium结构里。 的定义如下1 typedef struct tagSTGMEDIUM { DWORD tymed; [switch_type(DWORD), switch_is((DWORD) tymed)] union { [case(TYMED_GDI)] HBITMAP hBitmap; [case(TYMED_MFPICT)] HMETAFILEPICT hMetaFilePict; [case(TYMED_ENHMF)] HENHMETAFILE hEnhMetaFile; [case(TYMED_HGLOBAL)] HGLOBAL hGlobal; [case(TYMED_FILE)] LPWSTR lpszFileName; [case(TYMED_ISTREAM)] IStream *pstm; [case(TYMED_ISTORAGE)] IStorage *pstg; [default] ; }; [unique] IUnknown *pUnkForRelease; }STGMEDIUM; typedef STGMEDIUM *LPSTGMEDIUM; 看起来颇为复杂,其实主要是一系列句柄或数据对象接口的联合,根据数据具体的类型,使用其中之一即可。 得到了句柄或数据对象接口,也相当于得到了拖放的数据。 tymed和FORMATETC里一样,指出数据的载体类型(遗憾的是它不能指出具体的标准类型比如CF_TEXT或者其他)。至于pUnkForRelease,是源数据指定的一个接口,用来传递给ReleaseStgMedium函数,如果它不为NULL,则ReleaseStgMedium函数使用这个接口释放数据。如果为NULL,则ReleaseStgMedium函数使用默认的IUnknown接口。对于常规的拖放来说,这个对象指针应该为NULL. STGMEDIUM STGMEDIUM stgMedium; ret = pDataObject->GetData(&cFmt, &stgMedium); pdwEffect里返回DROPEFFECT_NONE表示不接受拖放。,判断拖放过来的IDataObject对象里有没有我感兴趣的数据。FORMATETC和STDMEDIUM 对于拖放对象来说,一般只要使用 if(grfKeyState!=m_AcceptKeyState ) { *pdwEffect = DROPEFFECT_NONE; return S_OK; } 在 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);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值