您的位置:首页 > 其它

Revit API Hook 之 拦截鼠标双击元素事件

2017-10-03 18:04 190 查看
阅读本文章前应具有一定编程基础和 Window API 相关知识

第一步,先封装HookBase抽象类,因所有Hook的都具有注册、卸载逻辑,且注册、卸载大同小易。如下:

public abstract class HookBase : IHook
{
private static Dictionary<int, IHook> m_Hooks;
private IntPtr m_ProcessId;
private int m_ThreadId;
private HookType m_HookType;
private HookProc m_HookProc;

protected internal int m_HookId;

static HookBase()
{
m_Hooks = new Dictionary<int, IHook>();
}

private HookBase(HookType hookType)
{
m_HookType = hookType;
m_HookProc = HookProc;
}

protected HookBase(IntPtr processId, HookType hookType)
: this(hookType)
{
m_ProcessId = processId;
if (m_ProcessId == IntPtr.Zero)
{
m_ProcessId = HookHelper.GetCurrentProcessId();
}
}

protected HookBase(int threadId, HookType hookType)
: this(hookType)
{
m_ThreadId = threadId;
if (m_ThreadId == 0)
{
m_ThreadId = HookHelper.GetCurrentThreadId();
}
}

public void Install()
{
if (m_ThreadId != 0)
{
m_HookId = HookHelper.SetWindowsHookEx(m_HookType, m_HookProc, IntPtr.Zero, m_ThreadId);
}
else
{
if (m_ProcessId == IntPtr.Zero)
{
return;
}
m_HookId = HookHelper.SetWindowsHookEx(m_HookType, m_HookProc, m_ProcessId, 0);
}

if (m_HookId == 0)
{
return;
}

if (!m_Hooks.ContainsKey(m_HookId))
{
m_Hooks.Add(m_HookId, this);
}
}

public void Uninstall()
{
if (m_HookId == 0)
{
return;
}

var flag = HookHelper.UnhookWindowsHookEx(m_HookId);
if (flag)
{
if (m_Hooks.Remove(m_HookId))
{
m_HookId = 0;
}
}
}

protected abstract int HookProc(int nCode, IntPtr wParam, IntPtr lParam);


第二步 ,因鼠标Hook分为线程鼠标Hook以及全局鼠标Hook两种,仅注册方式有点区别。为使用方便,将其封装为事件注册方式。如下

public abstract class MouseHookBase : HookBase
{
protected MouseHookBase(IntPtr processId)

f8e6
: base(processId, HookType.WH_MOUSE_LL)
{

}

protected MouseHookBase(int threadId)
: base(threadId, HookType.WH_MOUSE)
{

}

/// <summary>
/// 鼠标双击
/// </summary>
public event HookHandler<MouseEventArgs> MouseDoubleClick;

/// <summary>
/// 鼠标移动
/// </summary>
public event HookHandler<MouseEventArgs> MouseMove;

/// <summary>
/// 鼠标按下
/// </summary>
public event HookHandler<MouseEventArgs> MouseDown;

/// <summary>
/// 鼠标弹起
/// </summary>
public event HookHandler<MouseEventArgs> MouseUp;

protected override int HookProc(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode < 0)
{
return HookHelper.CallNextHookEx(m_HookId, nCode, wParam, lParam);
}

var mouseMsg = (MouseMessage)wParam.ToInt32();
var mouseHookStruct = lParam.ToStruct<MOUSEHOOKSTRUCT>();

var button = this.GetMouseButtons(mouseMsg);

switch (mouseMsg)
{
case MouseMessage.WM_LBUTTONDOWN:
case MouseMessage.WM_RBUTTONDOWN:
case MouseMessage.WM_MBUTTONDOWN:

return this.OnRaiseMouseDown(button, 1, mouseHookStruct.pt.X, mouseHookStruct.pt.Y, mouseHookStruct.mouseData);

case MouseMessage.WM_LBUTTONUP:
case MouseMessage.WM_MBUTTONUP:
case MouseMessage.WM_RBUTTONUP:

return this.OnRaiseMouseUp(button, 1, mouseHookStruct.pt.X, mouseHookStruct.pt.Y, mouseHookStruct.mouseData);

case MouseMessage.WM_LBUTTONDBLCLK:
case MouseMessage.WM_RBUTTONDBLCLK:
case MouseMessage.WM_MBUTTONDBLCLK:

return this.OnRaiseMouseDoubleClick(button, 2, mouseHookStruct.pt.X, mouseHookStruct.pt.Y, mouseHookStruct.mouseData);

case MouseMessage.WM_MOUSEMOVE:

return this.OnRaiseMouseMove(MouseButtons.None, 0, mouseHookStruct.pt.X, mouseHookStruct.pt.Y, mouseHookStruct.mouseData);
default:
return HookHelper.CallNextHookEx(m_HookId, nCode, wParam, lParam);
}
}

private MouseButtons GetMouseButtons(MouseMessage mouseMsg)
{
MouseButtons result = MouseButtons.None;
switch (mouseMsg)
{
case MouseMessage.WM_LBUTTONDBLCLK:
case MouseMessage.WM_LBUTTONDOWN:
case MouseMessage.WM_LBUTTONUP:
result = MouseButtons.Left;
break;
case MouseMessage.WM_MBUTTONDBLCLK:
case MouseMessage.WM_MBUTTONDOWN:
case MouseMessage.WM_MBUTTONUP:
result = MouseButtons.Middle;
break;
case MouseMessage.WM_RBUTTONDBLCLK:
case MouseMessage.WM_RBUTTONDOWN:
case MouseMessage.WM_RBUTTONUP:
result = MouseButtons.Right;
break;
}
return result;
}

private int OnRaiseMouseDoubleClick(MouseButtons button, int clicks, int x, int y, int delta)
{
if (this.MouseDoubleClick != null)
{
return this.MouseDoubleClick(this, new MouseEventArgs(button, clicks, x, y, delta));
}
return 0;
}

private int OnRaiseMouseDown(MouseButtons button, int clicks, int x, int y, int delta)
{
if (this.MouseDown != null)
{
return this.MouseDown(this, new MouseEventArgs(button, clicks, x, y, delta));
}
return 0;
}

private int OnRaiseMouseUp(MouseButtons button, int clicks, int x, int y, int delta)
{
if (this.MouseUp != null)
{
return this.MouseUp(this, new MouseEventArgs(button, clicks, x, y, delta));
}
return 0;
}

private int OnRaiseMouseMove(MouseButtons button, int clicks, int x, int y, int delta)
{
if (this.MouseMove != null)
{
return this.MouseMove(this, new MouseEventArgs(button, clicks, x, y, delta));
}
return 0;
}
}


第三步,依次实现线程鼠标Hook以及全局鼠标Hook.

/// <summary>
/// 线程鼠标Hook.
/// </summary>
/// <seealso cref="DotNet.Hook.Achieve.MouseHookBase" />
public class MouseHook : MouseHookBase
{
public MouseHook(int threadId = 0)
: base(threadId)
{

}
}

/// <summary>
/// 全局鼠标钩子
/// </summary>
/// <seealso cref="DotNet.Hook.Achieve.MouseHookBase" />
public class GlobalMouseHook : MouseHookBase
{
public GlobalMouseHook(IntPtr processId)
: base(processId)
{

}
}


第四步,有了鼠标Hook,我们如果在Revit内使用并且拦截鼠标双击元素事件呢?我们继续封装一个元素监控类 ,如下:

/// <summary>
/// 元素监控.
/// </summary>
public class ElementMonitor
{
private static ElementMonitor m_Instance;
private MouseHook m_MouseHook;
private bool m_IsMonitor;
private UIApplication m_UIApplication;

private ElementMonitor(UIApplication uiApp)
{
m_Instance = this;
m_UIApplication = uiApp;

m_MouseHook = new MouseHook();
m_MouseHook.Install();

m_MouseHook.MouseDoubleClick += OnRaiseMouseDoubleClick;
}

/// <summary>
/// 静态实例,可在入口类判断此实例是否为null,防止重复注册.
/// </summary>
public static ElementMonitor Instance
{
get
{
return m_Instance;
}
}

/// <summary>
/// 当鼠标双击元素时触发此事件.
/// </summary>
public event HookHandler<DoubleClickElementEventArgs> DoubleClickElement;

/// <summary>
/// 注册元素监控,并指定是否立即监控.
/// </summary>
public static void Register(UIApplication uiApp, bool immediatelyMonitor = true)
{
if (uiApp == null)
{
throw new ArgumentNullException(nameof(uiApp));
}

new ElementMonitor(uiApp)
{
m_IsMonitor = immediatelyMonitor
};
}

/// <summary>
/// 注册元素监控,并指定是否立即监控.
/// </summary>
public static void Register(UIControlledApplication uiControllApp, bool immediatelyMonitor = true)
{
if (uiControllApp == null)
{
throw new ArgumentNullException(nameof(uiControllApp));
}

var flag = System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.InvokeMethod;

var uiApp = (UIApplication)uiControllApp.GetType().InvokeMember("getUIApplication", flag, Type.DefaultBinder, uiControllApp, null);

Register(uiApp, immediatelyMonitor);
}

/// <summary>
/// 返回1,则拦截鼠标消息,返回0则传递给真正消息接收者.
/// </summary>
private int OnRaiseMouseDoubleClick(object sender, System.Windows.Forms.MouseEventArgs e)
{
if (!m_IsMonitor || e.Button != MouseButtons.Left || e.Clicks != 2)
{
return 0;
}

var uiDoc = m_UIApplication.ActiveUIDocument;

if (uiDoc == null)
{
return 0;
}

var elemIds = uiDoc.Selection.GetElementIds();

if (elemIds.Count == 1)
{
var elem = uiDoc.Document.GetElement(elemIds.First());

if (elem == null)
{
return 0;
}

if (this.DoubleClickElement == null)
{
return 0;
}

return this.DoubleClickElement(this, new DoubleClickElementEventArgs(elem));
}

return 0;
}
}


第五步,调用测试,如下

[Transaction(TransactionMode.Manual)]
public class MouseHookTest : IExternalCommand
{
Result IExternalCommand.Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
if (ElementMonitor.Instance == null)
{
ElementMonitor.Register(commandData.Application);
}

ElementMonitor.Instance.DoubleClickElement += OnRaiseDoubleClickElement;

return Result.Succeeded;
}

private int OnRaiseDoubleClickElement(object sender, DoubleClickElementEventArgs e)
{
if (e.Element == null)
{
return 0;
}

System.Windows.Forms.MessageBox.Show(string.Format("双击击元素Id: {0}", e.Element.Id));

return 1;

}
}


Github 源码 : https://github.com/HeZhongHao/DotNet.Revit/tree/master/DotNet.Revit/DotNet.Revit.Hook[/code] 
                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: