下載app免費(fèi)領(lǐng)取會(huì)員
HOOK(鉤子,掛鉤)是一種實(shí)現(xiàn)Windows平臺(tái)下類似于中斷的機(jī)制。HOOK機(jī)制允許應(yīng)用程序攔截并處理Windows消息或指定事件,當(dāng)指定的消息發(fā)出后,HOOK程序就可以在消息到達(dá)目標(biāo)窗口之前將其捕獲,從而得到對(duì)消息的控制權(quán),進(jìn)而可以對(duì)該消息進(jìn)行處理或修改,加入我們所需的功能。鉤子按使用范圍分,可分為線程鉤子和系統(tǒng)鉤子,其中,系統(tǒng)鉤子具有相當(dāng)大的功能,幾乎可以實(shí)現(xiàn)對(duì)所有Windows消息的攔截、處理和監(jiān)控。這項(xiàng)技術(shù)涉及到兩個(gè)重要的API,一個(gè)是SetWindowsHookEx,安裝鉤子;另一個(gè)是UnHookWindowsHookEx,卸載鉤子。
本文使用的HOOK API技術(shù),是指截獲系統(tǒng)或進(jìn)程對(duì)某個(gè)API函數(shù)的調(diào)用,使得API的執(zhí)行流程轉(zhuǎn)向我們指定的代碼段,從而實(shí)現(xiàn)我們所需的功能。Windows下的每個(gè)進(jìn)程均擁有自己的地址空間,并且進(jìn)程只能調(diào)用其地址空間內(nèi)的函數(shù),因此HOOK API尤為關(guān)鍵的一步是,設(shè)法將自己的代碼段注入到目標(biāo)進(jìn)程中,才能進(jìn)一步實(shí)現(xiàn)對(duì)該進(jìn)程調(diào)用的API進(jìn)行攔截。然而微軟并沒(méi)有提供HOOK API的調(diào)用接口,這就需要開發(fā)者自己編程實(shí)現(xiàn)。
一般來(lái)說(shuō),HOOK API由兩個(gè)組成部分,即實(shí)現(xiàn)HOOK API的DLL文件,和啟動(dòng)注入的主調(diào)程序。本文采用HOOK API 技術(shù)對(duì)剪切板相關(guān)的API 函數(shù)進(jìn)行攔截,從而實(shí)現(xiàn)對(duì)剪切板內(nèi)容的監(jiān)控功能,同樣使用該技術(shù)實(shí)現(xiàn)進(jìn)程防終止功能。其中DLL文件支持HOOK API的實(shí)現(xiàn),而主調(diào)客戶端程序?qū)⒃诔跏蓟瘯r(shí)把帶有HOOK API功能的DLL隨著鼠標(biāo)鉤子的加載注入到目標(biāo)進(jìn)程中,這里的鼠標(biāo)鉤子屬于系統(tǒng)鉤子。
下面介紹在Revit中,如何應(yīng)用Hook對(duì)鼠標(biāo)雙擊元素事件進(jìn)行攔截。
第一步,先封裝HookBase抽象類,因所有Hook的都具有注冊(cè)、卸載邏輯,且注冊(cè)、卸載大同小易。如下
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);
第二步 ,因鼠標(biāo)Hook分為線程鼠標(biāo)Hook以及全局鼠標(biāo)Hook兩種,僅注冊(cè)方式有點(diǎn)區(qū)別。為使用方便,將其封裝為事件注冊(cè)方式。如下
public abstract class MouseHookBase : HookBase
{
protected MouseHookBase(IntPtr processId)
: base(processId, HookType.WH_MOUSE_LL)
{
}
protected MouseHookBase(int threadId)
: base(threadId, HookType.WH_MOUSE)
{
}
public event HookHandler<MouseEventArgs> MouseDoubleClick;
public event HookHandler<MouseEventArgs> MouseMove;
public event HookHandler<MouseEventArgs> MouseDown;
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, intdelta)
{
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;
}
}
第三步,依次實(shí)現(xiàn)線程鼠標(biāo)Hook以及全局鼠標(biāo)Hook.
public class MouseHook : MouseHookBase{
public MouseHook(int threadId = 0)
: base(threadId)
{
}
}
public class GlobalMouseHook : MouseHookBase
{
public GlobalMouseHook(IntPtr processId)
: base(processId)
{
}
}
第四步,有了鼠標(biāo)Hook,我們?nèi)绻赗evit內(nèi)使用并且攔截鼠標(biāo)雙擊元素事件呢?我們繼續(xù)封裝一個(gè)元素監(jiān)控類 ,如下:
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>
/// 靜態(tài)實(shí)例,可在入口類判斷此實(shí)例是否為null,防止重復(fù)注冊(cè).
/// </summary>
public static ElementMonitor Instance
{
get
{
return m_Instance;
}
}
/// <summary>
/// 當(dāng)鼠標(biāo)雙擊元素時(shí)觸發(fā)此事件.
/// </summary>
public event HookHandler<DoubleClickElementEventArgs> DoubleClickElement;
/// <summary>
/// 注冊(cè)元素監(jiān)控,并指定是否立即監(jiān)控.
/// </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>
/// 注冊(cè)元素監(jiān)控,并指定是否立即監(jiān)控.
/// </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,則攔截鼠標(biāo)消息,返回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;
}
}
第五步,調(diào)用測(cè)試,如下
[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;
}
}
本文版權(quán)歸腿腿教學(xué)網(wǎng)及原創(chuàng)作者所有,未經(jīng)授權(quán),謝絕轉(zhuǎn)載。
上一篇:Revit二次開發(fā)教程:Revit族參數(shù)可見性設(shè)置
下一篇:Revit二次開發(fā)教程:注冊(cè)Revit插件
推薦專題