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

学海苦作舟,书山勤为径

留下点回忆

常用链接

统计

积分与排名

Denoise

English study

Web技术

数据压缩

一些连接

最新评论

第六部分:实现Drop目标对象(OLE drag&drop 之旅)

欢迎你到OLE拖放操作的第六章!这里将着重于一个实现了drop-target的小程序,这就意味着我们的程序能够接收拖到它上面的对象(文件、图片、文本)了。

我们实现一个IDropTargetCOM接口允许OLE程序拖动数据到我们的程序上;这里仅仅是一个简单的EDIT控件,所以他将CF_TEXT数据作为目标。

成为一个“Drop Target

为了时窗口可以接收拖放操作的数据,窗口必须注册为drop目标;有一个OLEAPI调用RegisterDragDrop来完成这个事情,函数的原型是:

WINOLEAPI RegisterDragDrop (HWND hwnd, IDropTarget * pDropTarget);

第一个参数是窗口的HANDLE,这个窗口是拖动的目标窗口;第二个参数是一个指向IDropTarget COM对象的指针,COM/OLE运行时将在拖放操作的过程中调用这个方法。

同样有一个OLE API调用来将window从拖放操作中删除:

WINOLEAPI RevokeDragDrop(HWND hwnd);

我们所要做的就是在窗口创建的时候调用RegisterDragDrop,在窗口销毁的时候调用RevokeDragDrop。在我们调用RegisterDragDrop之前,我们需要构造一个COM对象来支持IDropTarget接口。

IDropTarget接口

IDropTarget接口相对比较简单,有四个函数需要实现,当然,也要实现IUnknown接口,不过我们前面已经介绍了。

IDropTarget 方法

描述

DragEnter

判断是否可以接受一个拖操作,以及接受之后的效果

DragOver

提供通过DoDragDrop函数执行的目标反馈

DragLeave

导致一个drop目标挂起它的返回行为

Drop

数据放进目标窗口

这些函数都由COM/OLE运行时在一个对象被拖到我们注册窗口的时候来调用。就象上表显示的一样,每个函数都有不同的任务,我们需要做的就是实现这些函数。

实现IDropTarget

以我的经验,IDropTarget接口非常难以写为不涉及特定程序的代码,例如:写成可以在所有程序都使用的通用IDropTarget COM对象是很难的。

这是因为IDropTarget要求在一个对象拖过你的目标窗口时显示图形效果,且也只有特定程序代码才可以访问这些数据对象内容。

在我们的拖放接口之外,IDropTarget是最容易被集成到你窗口类的对象。例如:假定你已经用C++类实现了一个自定义的窗口,为这个窗口添加一个多drop目标支持的最好方法就是从IDropTarget直接继承,而不需要单独定义一个CDropTarget类;这意味着你的drop-target代码能够访问所有你的窗口状态。

然而,我们这里提供完整的CDropTarget类:

class CDropTarget : public IDropTarget
{
public:
    // IUnknown implementation
    HRESULT __stdcall QueryInterface (REFIID iid, void ** ppvObject);
    ULONG   __stdcall AddRef (void);
ULONG   __stdcall Release (void);
 
    // IDropTarget implementation
    HRESULT __stdcall DragEnter(IDataObject * pDataObject, DWORD grfKeyState, POINTL pt, DWORD * pdwEffect);
    HRESULT __stdcall DragOver(DWORD grfKeyState, POINTL pt, DWORD * pdwEffect);
    HRESULT __stdcall DragLeave(void);
    HRESULT __stdcall Drop(IDataObject * pDataObject, DWORD grfKeyState, POINTL pt, DWORD * pdwEffect);
    // Constructor
    CDropTarget(HWND hwnd);
    ~CDropTarget();
private:
    // internal helper function
    DWORD DropEffect (DWORD grfKeyState, POINTL pt, DWORD dwAllowed);
    bool QueryDataObject(IDataObject *pDataObject);
    // Private member variables
    long   m_lRefCount;
    HWND   m_hWnd;
    bool   m_fAllowDrop;
    // Other internal window members
};

除引用记数器外,我们需要存储另外两个变量:m_hWnd变量是drop-target窗口的HANDLE,这个在提供可见效果的时候需要;m_fAllowDrop用来指示被拖动的数据对象是否包含我们需要的有用数据。因此我们没有连续查询数据对象,这是一个最优的办法。

IDropTarget::DragEnter方法

让我们首先看一下IDropTarget函数,因为这是在一个对象被拖过我们窗口时最先被COM调用的函数:

 

HRESULT DragEnter (
   IDataObject * pDataObject,//指向源数据对象的接口指针
   DWORD         grfKeyState, // 当前键盘修饰符的状态
   POINTL        pt,             // 当前鼠标的坐标
   DWORD *       pdwEffect       // 指向拖放操作的效果指针
   );

仔细看一下上面函数的原型,因为这对于理解每个参数怎么样使用很重要:

l         IDataObject-第一个参数是拖放操作的源对象通过COM传递来的数据对象指针。IDataObject是拖放操作带来数据的传输媒体,我们在DragEnter的时候查看数据对象来看是否有我们想要的任何数据。

l         grfKeyState-保留键盘修饰符的状态,例如:ControlAlt、和Shift以及鼠标按键的状态。是有一到多个MK_CONTROLMK_SHIFTMK_ALTMK_BUTTONMK_LBUTTON等组成的简单DWORD变量

l         pt-一个POINTL结构体,包含了鼠标进入我们窗口的坐标;在许多程序中,这个参数用来检查鼠标是否放置在允许的drop区域上,或者用来简单的放置某些插入光标来指示drop数据放在那里。

l         pdwEffect-一个DWORD的指针,指出drop源允许的drop效果。这个值和DoDragDropdwOKEffect值相同。

我们的DragEnter实现需要做几个通常的工作,另外画一个图形的反馈:

1.       检查提供的数据对象,然后判断它是否包含任何有用的数据

2.       检查存储在grfKeyState的键盘状态,并且计算应该是什么样的drop效果,例如:如果Control键按下,drop效果应该是复制,如果Shift被按下,drop效果应该是移动。

3.       验证这些效果是否与drop源的效果相兼容

4.       存储最终的drop效果到pdwEffectDWORD指针。

不要如此复杂吧!DragEnter的目的就是简单的对拖放操作说“yes还是NO”,指定采用什么drop效果以便于OLE更新鼠标光标。

HRESULT __stdcall CDropTarget::DragEnter(IDataObject *pDataObject, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
{
    // does the dataobject contain data we want?
    m_fAllowDrop = QueryDataObject (grfKeyState, pdwEffect, pDataObject);
    if(m_fAllowDrop)
    {
        // get the dropeffect based on keyboard state
        *pdwEffect = DropEffect (grfKeyState, pt, *pdwEffect);
        SetFocus (m_hWnd);
        PositionCursor (m_hWnd, pt);
    }
    else
    {
        *pdwEffect = DROPEFFECT_NONE;
    }
    return S_OK;
}

除了设置光标下的窗口和设置EDIT位置外,DragEnter的功能已经由两个内部协助函数代理而简化了:

bool CDropTarget::QueryDataObject(IDataObject *pDataObject)
{
    FORMATETC fmtetc = { CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
    // does the data object support CF_TEXT using a HGLOBAL?
    return pDataObject->QueryGetData(&fmtetc) == S_OK ? true : false;
}

QueryDataObject是一个私有函数,纯粹用来检查提供的数据,然后决定它是否包含对我们的drop目标有用的数据。在我们的例子中,我们仅仅接受CF_TEXT数据存储为HGLOBAL,因此这是我们请求的类型。一个私有成员变量m_fAllowDrop用来记住这个决定。

DWORD CDropTarget::DropEffect (DWORD grfKeyState, POINTL pt, DWORD dwAllowed)
{
        DWORD dwEffect = 0;
        // 1. 检查pt来看是否允许drop操作在某个位置
        // 2. 计算出基于grfKeyStatedrop效果
        if(grfKeyState & MK_CONTROL)
        {
               dwEffect = dwAllowed & DROPEFFECT_COPY;
        }
        else if(grfKeyState & MK_SHIFT)
        {
               dwEffect = dwAllowed & DROPEFFECT_MOVE;
        }
        // 3. 非键盘修饰符指定(或drop效果不允许),因此基于drop源的效果
        if(dwEffect == 0)
        {
               if(dwAllowed & DROPEFFECT_COPY) dwEffect = DROPEFFECT_COPY;
               if(dwAllowed & DROPEFFECT_MOVE) dwEffect = DROPEFFECT_MOVE;
        }
        return dwEffect;
}

DropEffect协助函数用来计算基于键盘状态的drop效果,并且这个效果是达到源允许的。

首先grfKeyState变量用来检查看是否使用了ControlShift键;这些键的标准的OLE行为是Control应该是复制数据,shift应该是移动数据。如果两个都按下,数据 应该是连接(例如:源应该建立一个到目标的快捷方式),但我们不支持这个功能。

主要的事情是使用位与操作符来对dwEffectdrop效果值的时候:

dwEffect = dwAllowed & DROPEFFECT_COPY;

这个分配的结构很简单-dwEffect将拥有DROPEFFECT_COPY,但只有在dwAllowed变量中仅仅包含这个值的时候起作用;这种逻辑用法防止我们强制执行一个源不允许的drop效果。

下面是看一下在没有键盘修饰符的时候怎么做,例如:ControlShift没有使用。在这种情况我,我们检查拖放的源对象允许的drop效果,以及选择使用哪个效果;在我们的实现中,我们是移动数据而不是复制。

IDropTarget::DragOver方法

这个函数在拖放操作的整个生命周期中被多次调用,因此,高效的写这个函数很重要;DragOver在键盘修饰符改变(shift/control等)或当鼠标移动的时候被调用。告诉OLE采用什么样基于键盘状态和鼠标位置的drop效果是这个函数的责任:

HRESULT __stdcall CDropTarget::DragOver(DWORD grfKeyState, POINTL pt, DWORD * pdwEffect)
{
    if(m_fAllowDrop)
    {
        *pdwEffect = DropEffect(grfKeyState, pt, *pdwEffect);
        PositionCursor(m_hWnd, pt);
    }
    else
    {
        *pdwEffect = DROPEFFECT_NONE;
    }
    return S_OK;
}

DragOver写的很简单,逻辑上与DragEnter相同,我们使用前面计算过的m_fAllowDropDropEffect协助函数来通过pdwEffect指针返回drop效果。

IDropTarget::DragLeave函数

这个函数在鼠标光标移到drop目标窗口外面的时候调用,或者按下Escape键来取消拖放操作时。它的原型如下:

HRESULT __stdcall CDropTarget::DragLeave (void)
{
    return S_OK;
}

这是这个函数的基本写法;这个函数存在的唯一原因是便于程序在鼠标移到窗口外面的时候使用图形返回效果来得到一个机会清理。例如:想象下面的场景,无论什么东西都拖过目标对象,DragEnter函数用来改变窗口边界的颜色;在这种情况下,DragLeave函数用来恢复窗口边界的颜色。

IDropTarget::Drop函数

Drop函数的原型与DragEnter函数相同:

HRESULT __stdcall CDropTarget::Drop (IDataObject *pDataObject, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
{
    PositionCursor(m_hWnd, pt);
    if(m_fAllowDrop)
    {
        DropData (m_hWnd, pDataObject);
        *pdwEffect = DropEffect (grfKeyState, pt, *pdwEffect);
    }
    else
    {
        *pdwEffect = DROPEFFECT_NONE;
    }
    return S_OK;
}

OLE判断拖放操作到头的时候调用该函数,我们得到一个在DragEnter同样的IDataObject的接口指针,我们可以从中得到数据并粘贴到我们的编辑窗口中。

DropData协助函数用来访问数据对象内部的CF_TEXT数据,并插入到edit控件中;这个程序是是纯理论的,我们已经知道怎么样访问一个数据对象了,这里不在不厌其烦的介绍,你可以看源代码。

posted on 2006-03-06 08:51 笨笨 阅读(4827) 评论(12)  编辑 收藏 引用 所属分类: OLE Drag&Drop

评论

# re: 第六部分:实现Drop目标对象(OLE drag&drop 之旅) 2006-05-24 20:11 王鴻杰

您好~我是來自台灣的讀者,想要請教您,如果我想要寫一個ActiveX元件,嵌入IE中作為一個檔案拖放的接受器,是否可行呢??

以我的經驗,我將程式寫在一般的桌面應用程式,是可以實現OLE拖放,可是嵌到網頁裡面以後,我將檔案拖進IE當中,我的ActiveX元件卻沒有反應。您是否有可以參考的ActiveX程序呢??

ps.我是使用Borland C++ Builder去開發Active Control和Active Form,結果都沒有用。  回复  更多评论   

# re: 第六部分:实现Drop目标对象(OLE drag&drop 之旅) 2006-05-25 13:19 笨笨

我没有相关的程序
我想你因看一下IE是如何处理DRAG OVER等事件的,我试验发现他已经是一个TARGET。
你的ACTIVEX control有窗口吗?  回复  更多评论   

# re: 第六部分:实现Drop目标对象(OLE drag&drop 之旅)[未登录] 2007-06-27 01:40 Peter

您好,拜讀您之大作,受益良多,在下最近再撰寫這方面的相關程式,可以限制使用者複製文件、內容,複製檔案的行為,說穿了,就是監控剪貼板來達成。目前唯一的瓶頸就是使用者利用檔案總管來 Drag-Drop 複製檔案的行為無法攔截。因為這動作不透過傳統的剪貼板,也不會透過 OLE 剪貼板,所以監視剪貼板似乎沒有用。不知道可否請您賜教這方面該如何下手呢?

我目前想到的方法,就是利用 APIHook, MouseHook 來攔截這些訊息。看看能不能做到此功能。  回复  更多评论   

# re: 第六部分:实现Drop目标对象(OLE drag&drop 之旅) 2011-03-15 14:25 laocui

说好的源码demo呢?  回复  更多评论   

# re: 第六部分:实现Drop目标对象(OLE drag&drop 之旅) 2012-02-01 12:11 PRESTONLetha26

Cars and houses are quite expensive and not every person is able to buy it. But, <a href="http://goodfinance-blog.com/topics/business-loans">business loans</a> was created to support different people in such hard situations.   回复  更多评论   

# re: 第六部分:实现Drop目标对象(OLE drag&drop 之旅) 2012-06-15 18:06 article submission

Are thirsting for good traffic? Do not know the way to do that? We are here just because of that! We provide outstanding quality manual article submission options at article submission directory company. We can guarantee you great publicity and great progress of your business.   回复  更多评论   

# re: 第六部分:实现Drop目标对象(OLE drag&drop 之旅) 2015-06-11 17:29 meifeng

IDropTarget可以支持图片拖拽么  回复  更多评论   


只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理