关闭

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

标签: Revit-APIHook鼠标Hook
88人阅读 评论(0) 收藏 举报
分类:

阅读本文章前应具有一定编程基础和 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)
            : 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
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:5948次
    • 积分:187
    • 等级:
    • 排名:千里之外
    • 原创:12篇
    • 转载:0篇
    • 译文:0篇
    • 评论:15条
    最新评论