欧非资源网:安全、免费、专业放心的资源下载站! 最新软件|软件分类

您的位置:欧非资源网 > Word专区 > Word教程 > 控制用户在Word中的拖拽行为

控制用户在Word中的拖拽行为

时间:2016-08-29 22:39作者:admin来源:未知人气:325我要评论(0)

最近在做的一个项目是一个Word 2003的插件。项目的一个需求是控制用户在Word中的拖拽行为。具体来说有三种:

1、用户完全不能把某些文字Drag起来

2、对于某些文字,可以Drag起来,但是不能Drop到除当前文档之外的任何地方


3、对于某些文字,任意东西都不能Drop于其上

说实话,我了解到这个是需求的时候, 第一反应就是,这可能吗?借用阿迪的广告语,Impossible is Nothing。的确,Windows之所以千疮百孔,在我看来很大程度上就是它提供了太多的可Hack的手段了。在实现这个可控的拖拽行为之前,已经通过SetWindowsHook控制了用户的鼠标和键盘(当然这种技术已经用烂了,我就不再炒冷饭了)。这次,我们使用Windows API Hook来达到这个目的。


如果不了解拖拽到底是怎么实现的,我们是不可能控制它的行为的。 我们要做的,其实就是找出标准的拖拽实现方式。然后在其中插一脚,把我们感兴趣的东西拦截下来,并篡改掉本来的输出结果。说实话,Hook的善恶就在一念之间。那么我们先来简单了解一下拖拽的流程:


Drag:

1、应用程序在用户用鼠标拖拽了一个物体之后,调用 DoDragDrop(dataObject, dropSource, okEffect, effects) 开始拖拽


2、Windows 回调 dropSource 的 QueryContinueDrag 来决定是不是继续Drag


Drop:

1、应用程序在初始化的时候,调用 RegisterDragDrop(hwnd, dropTarget)


2、当有物体拖拽进了 hwnd 所在的区域时,Windows 回调 dropTarget 的 DragEnter


3、当物体在 hwnd 所在区域内滑动时,Windows 回调 dropTarget 的 DragOver

4、当物体拖拽出 hwnd 所在区域时,Windows 回调 dropTarget 的 DragLeave


5、当拖拽的物体放下是,Windows 回调 dropTarget 的 Drop

所以对于我们来说,重点关注的就是两个API,两个Com Interface。分别是 Ole32 的 DoDragDrop 和 RegisterDragDrop。以及 Ole32 中的 IDropTarget, IDropSource。要实现开头所述的三种行为,我们只需要:


1、 用户完全不能把某些文字Drag起来:

拦截DoDragDrop的调用,如果dataObject或者用户当前选中的区域是受到保护的,就不调用Ole32的真实实现,直接返回0。

2、 对于某些文字,可以Drag起来,但是不能Drop到除当前文档之外的任何地方:

拦截DoDragDrop的调用,真实地去调用Ole32的真实实现,但是不直接使用原装的DropSource(包装之后再用)。因为我们要监听Windows对DropSource的回调,再“恰当”的时候篡QueryContinueDrag的返回值。从而使得用户无法Drop到当前文档之外的区域。

3、 对于某些文字,任意东西都不能Drop于其上:

拦截RegisterDragDrop的调用,确实使用Ole32的真实实现,但是也不直接使用原装的DropTarget(包装之后再用)。因为我们要监听Windows对DropTarget的回调。同样,在“恰当” 的时候篡改DragOver和Drop的返回值。

那么问题就集中在了如何拦截Ole32的两个API上。这个问题很好解决,直接使EasyHook(http://www.codeplex.com/easyhook)库就行了。我阅读了这个库的所有源代码,有机会可以给大家讲讲Windows API Hook的原理,也挺有意思的。而且使用EasyHook,我们还可以做到远程注入(底层实现是CreateRemoteThread,老套但是可靠,关键是支持.NET)。这样,就不用受限于当前进程了。不过由于RegisterDragDrop一般在初始化的时候调用,所以最好使用EasyHook的CreateAndInject来做,要不然就时机太晚了。在本例中是一个Word 2003的插件,而且恰巧Word 2003是先加载AddIn再RegisterDragDrop,要不然也就Hook无门了。


下面是一些关键细节的源代码:


安装DoDragDrop的钩子

 

Code
using System;
using System.Runtime.InteropServices.ComTypes;
using EasyHook;

namespace xxx
{
public class RangeDragEvents : IDisposable
{
private readonly WordApplication application;
private readonly RangeListener listener;
private readonly Ole32._DoDragDrop doDragDropHandler;
private readonly LocalHook doDragDropHook;

public RangeDragEvents(WordApplication application, RangeListener listener)
{
this.application = application;
this.listener = listener;
doDragDropHandler = HandleDoDragDrop;
doDragDropHook = LocalHook.Create(LocalHook.GetProcAddress("ole32.dll", "DoDragDrop"),
doDragDropHandler, new object());
doDragDropHook.ThreadACL.SetExclusiveACL(new int[0]);
}

private int HandleDoDragDrop(IDataObject rawDataObject, Ole32.DropSource originalDropSource, int allowedEffect,
int[] finalEffect)
{
Ole32.DropSource dropSource = originalDropSource;
switch (GetDragAction())
{
case DragAction.JAILED:
dropSource = new JailDropSource(originalDropSource);
break;
case DragAction.CANCELLED:
return 0;
}
return Ole32.DoDragDrop(rawDataObject, dropSource, allowedEffect, finalEffect);
}

private DragAction GetDragAction()
{
WordSelection selection = application.Selection;
if (selection == null)
{
return DragAction.PROCEEDED;
}
using (var detached = selection.Detach())
{
var range = detached.Range;
if (range.Start == range.End)
{
return DragAction.PROCEEDED;
}
return listener.HandleDrag(range);
}
}

public void Dispose()
{
doDragDropHook.Dispose();
}
}
}
安装RegisterDragDrop的钩子

Code
using System;
using EasyHook;

namespace xxx
{
public class RangeDropEvents : IDisposable
{
private readonly Ole32._RegisterDragDrop registerDragDropHandler;
private readonly LocalHook registerDragDropHook;
private readonly RangeListener listener;
private readonly WordApplication application;

public RangeDropEvents(WordApplication application, RangeListener listener)
{
this.listener = listener;
this.application = application;
registerDragDropHandler = HandleRegisterDragDrop;
registerDragDropHook = LocalHook.Create(LocalHook.GetProcAddress("ole32.dll", "RegisterDragDrop"),
registerDragDropHandler, new object());
registerDragDropHook.ThreadACL.SetExclusiveACL(new int[0]);
}

private int HandleRegisterDragDrop(IntPtr hwnd, Ole32.DropTarget target)
{
return Ole32.RegisterDragDrop(hwnd, new RangeAwareDropTarget(application, listener, target));
}

public void Dispose()
{
registerDragDropHook.Dispose();
}
}
}
把Drag的物体“Jail”在当前文档中


Code
using System;
using System.Drawing;
using System.Windows.Forms;
using log4net;

namespace xxx
{
public class JailDropSource : Ole32.DropSource
{
private static readonly ILog LOGGER = LogManager.GetLogger(typeof (JailDropSource));
private const int DRAGDROP_S_DROP = 0x00040100;
private const int DRAGDROP_S_CANCEL = 0x00040101;
private readonly Ole32.DropSource dropSource;
private readonly IntPtr jailedIn;

public JailDropSource(Ole32.DropSource dropSource)
{
jailedIn = User32.GetForegroundWindow();
this.dropSource = dropSource;
}

public int QueryContinueDrag(int escapePressed, int keyState)
{
int result = dropSource.QueryContinueDrag(escapePressed, keyState);
if (result == DRAGDROP_S_DROP)
{
if (!IsDropable())
{
return DRAGDROP_S_CANCEL;
}
}
return result;
}

public int GiveFeedback(int effect)
{
if (!IsDropable())
{
Cursor.Current = Cursors.No;
return 0;
}
return dropSource.GiveFeedback(effect);
}

private bool IsDropable()
{
try
{
if (jailedIn != User32.GetForegroundWindow())
{
return false;
}
User32.RECT rect = User32.GetWindowRect(jailedIn);
var rectangle = new Rectangle(rect.Left, rect.Top, rect.Right - rect.Left, rect.Bottom - rect.Top);
return rectangle.Contains(Cursor.Position);
}
catch (Exception e)
{
LOGGER.Error("can not decide it is droppable or not, default to false", e);
return false;
}
}
}
}
监听DropTarget的回调,拒绝某些场合下的Drop下来东西


Code

相关阅读 12Word 样式Word 使用模板Word 目录Word 打印预览文档Word 文档比较Word 文档安全性Word 设置水印Word 快捷键大全​word段落间距怎么设置

文章评论
发表评论

热门文章 word表格中有一段空白处却不能输入文字,是怎么回事?九九乘法口诀表 可以下载直接打印使用(Word/PDF版)word 表格设置浅色底纹强调文字颜色两种方法如何利用word制做思维导图

最新文章 12Word 样式 Word 使用模板Word 目录Word 打印预览文档Word 文档比较

人气排行 word表格中有一段空白处却不能输入文字,是怎么回事?九九乘法口诀表 可以下载直接打印使用(Word/PDF版)word 橡皮擦在哪里? 橡皮擦的功能和使用方法word 表格设置浅色底纹强调文字颜色两种方法如何利用word制做思维导图word 如何设置某一页为横向页面word2016免费中文激活版下载 word2016官方免费完整版下载word字体颜色深浅不一怎么办

盖楼回复X

(您的评论需要经过审核才能显示)