修改程序(word2003)的拖拽行为
2008-10-31 13:15
211 查看
引用Blog
最近在做的一个项目是一个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.Windows.Forms;
namespace xxx
{
public class RangeAwareDropTarget : ProxyDropTarget
{
private readonly RangeListener listener;
private readonly WordApplication application;
public RangeAwareDropTarget(WordApplication application, RangeListener listener, Ole32.DropTarget dropTarget)
: base(dropTarget)
{
this.listener = listener;
this.application = application;
}
public override int DragOver(int keyState, long point, ref int effect)
{
var result = base.DragOver(keyState, point, ref effect);
if (ShouldHandle())
{
effect = (int) DragDropEffects.None;
}
return result;
}
public override int Drop(object dataObject, int keyState, long point, ref int effect)
{
if (ShouldHandle())
{
DragLeave();
return 0;
}
return base.Drop(dataObject, keyState, point, ref effect);
}
private bool ShouldHandle()
{
var window = application.ActiveWindow;
if (window == null)
{
return false;
}
using (var detached = window.Detach())
{
var range = detached.RangeFromPoint(Cursor.Position.X, Cursor.Position.Y);
if (range == null)
{
return false;
}
return listener.HandleChange(range, ChangeSource.Drop);
}
}
}
}
最近在做的一个项目是一个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.Windows.Forms;
namespace xxx
{
public class RangeAwareDropTarget : ProxyDropTarget
{
private readonly RangeListener listener;
private readonly WordApplication application;
public RangeAwareDropTarget(WordApplication application, RangeListener listener, Ole32.DropTarget dropTarget)
: base(dropTarget)
{
this.listener = listener;
this.application = application;
}
public override int DragOver(int keyState, long point, ref int effect)
{
var result = base.DragOver(keyState, point, ref effect);
if (ShouldHandle())
{
effect = (int) DragDropEffects.None;
}
return result;
}
public override int Drop(object dataObject, int keyState, long point, ref int effect)
{
if (ShouldHandle())
{
DragLeave();
return 0;
}
return base.Drop(dataObject, keyState, point, ref effect);
}
private bool ShouldHandle()
{
var window = application.ActiveWindow;
if (window == null)
{
return false;
}
using (var detached = window.Detach())
{
var range = detached.RangeFromPoint(Cursor.Position.X, Cursor.Position.Y);
if (range == null)
{
return false;
}
return listener.HandleChange(range, ChangeSource.Drop);
}
}
}
}
相关文章推荐
- How to: 修改程序的拖拽行为
- How to: 修改程序的拖拽行为
- 阅读程序,分析输出结果。/修改程序第2行为const Student stud(101,78.5),修改程序使之正常运行。
- 谈谈软件快速交付压力下程序猿们的“养寇自保”行为---为什么总在加班和通宵定位修改bug?
- ASP.NET MVC程序中动态修改form的Action值
- 面向对象的继承与修改(拖拽实例)
- 快速的批量修改重命名.net程序的命名空间
- linux下修改MAC地址的macchanger程序简介
- QT如何修改程序图标
- 程序笔试题-const变量通过指针修改问题
- 修改程序堆栈的可执行属性
- trigger 根据绑定到匹配元素的给定的事件类型执行所有的处理程序和行为。
- Windows10系统默认程序如何修改以便更好的操作电脑
- 不同系统与程序修改java.library.path的位置(转)
- 个人居间行为或中介行为附和法律程序不?
- 连接MySQL并提供查询,删除,修改和添加的java程序
- 修改注册表,在运行程序后加一个pause;方便查看一闪而过的程序|打开加加 软件简介
- qt 拖拽 修改大小
- 修改XML默认打开程序,但不改变之前的图标
- 修改LR自带定机票的程序端口