WM_MOUSEHOVER不适用于全局钩子

时间:2013-07-01 14:19:18

标签: c# winapi interop

public class InputHook
{
    #region Windows function imports

    [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
    private static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, int dwThreadId);

    [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
    private static extern int UnhookWindowsHookEx(int idHook);

    [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
    private static extern int CallNextHookEx(int idHook, int nCode, int wParam, IntPtr lParam);

    [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr GetModuleHandle(string lpModuleName);

    [DllImport("user32.dll")]
    internal static extern IntPtr GetActiveWindow();
    [DllImport("user32.dll", EntryPoint = "TrackMouseEvent", CallingConvention = CallingConvention.StdCall)]
    private extern static bool Win32TrackMouseEvent(ref TRACKMOUSEEVENT tme);

    private delegate int HookProc(int nCode, int wParam, IntPtr lParam);

    #endregion

    #region Windows constants

    // ReSharper disable InconsistentNaming
    private const int WH_MOUSE_LL = 14;
    private const int WH_MOUSE = 7;
    private const int WH_GETMESSAGE = 3;
    private const int WM_MOUSEMOVE = 0x200;
    private const int WM_MOUSEHOVER = 0x2A1;
    private const int GWL_WNDPROC = -4;
    private const int WM_IME_SETCONTEXT = 0x281;
    private const int WM_INPUTLANGCHANGE = 0x51;
    private const int WM_GETDLGCODE = 0x87;
    private const int WM_IME_COMPOSITION = 0x10F;
    private const int DLGC_WANTALLKEYS = 4;

    private const int TID_POLLMOUSE = 100;
    private const int MOUSE_POLL_DELAY = 500;
    private const int WM_TIMER = 0x0113;
    private const int WM_MOUSEENTER = 0x41E;
    private const int WM_MOUSELEAVE = 0x41F;
    // ReSharper restore InconsistentNaming

    #endregion

    #region Events

    /// <summary>Event raised when the mouse has hovered in the same location for a short period of time.</summary>
    public event MouseEventHandler MouseHover;

    #endregion

    public InputHook(bool installMouseHook = true)
    {
        Start(installMouseHook);
    }

    ~InputHook()
    {
        Stop(true, false);
    }

    private int m_hMouseHook;

    private static HookProc s_mouseHookProcedure;

    private void Start(bool installMouseHook)
    {
        // install Mouse hook only if it is not installed and must be installed
        if(m_hMouseHook == 0 && installMouseHook)
        {
            s_mouseHookProcedure = MouseHookProc;
            m_hMouseHook = SetWindowsHookEx(WH_MOUSE, s_mouseHookProcedure, (IntPtr)0, AppDomain.GetCurrentThreadId());

            if(m_hMouseHook == 0)
            {
                var errorCode = Marshal.GetLastWin32Error();
                Stop(true, false);
                throw new Win32Exception(errorCode);
            }
        }
    }

    public void Stop(bool uninstallMouseHook = true, bool throwExceptions = true)
    {
        if(m_hMouseHook != 0 && uninstallMouseHook)
        {
            var retMouse = UnhookWindowsHookEx(m_hMouseHook);
            m_hMouseHook = 0;
            if(retMouse == 0 && throwExceptions)
            {
                var errorCode = Marshal.GetLastWin32Error();
                throw new Win32Exception(errorCode);
            }
        }
    }

    #region Mouse Input

    private int MouseHookProc(int nCode, int wParam, IntPtr lParam)
    {
        if((nCode >= 0) && (MouseHover != null))
        {
            short x, y;
            MouseLocationFromLParam(lParam.ToInt32(), out x, out y);

            switch(wParam)
            {
                case WM_MOUSEMOVE:
                    TRACKMOUSEEVENT tme = new TRACKMOUSEEVENT();
                    tme.cbSize = Marshal.SizeOf(tme);
                    tme.dwFlags = TMEFlags.TME_HOVER;
                    tme.dwHoverTime = 100;
                    tme.hwndTrack = lParam;
                    Win32TrackMouseEvent(ref tme);
                    break;
                case WM_MOUSEHOVER:
                    if(MouseHover != null)
                        MouseHover(null, new MouseEventArgs(MouseButtons.None, 0, x, y, 0));
                    break;
            }
        }

        return CallNextHookEx(m_hMouseHook, nCode, wParam, lParam);
    }

    #endregion

    #region Mouse Message Helpers

    private static void MouseLocationFromLParam(int lParam, out short x, out short y)
    {
        // Cast to signed shorts to get sign extension on negative coordinates (of course this would only be possible if mouse capture was enabled).
        x = (short)(lParam & 0xFFFF);
        y = (short)(lParam >> 16);
    }

    #endregion

    [StructLayout(LayoutKind.Sequential)]
    private struct TRACKMOUSEEVENT
    {
        internal int cbSize;
        internal TMEFlags dwFlags;
        internal IntPtr hwndTrack;
        internal int dwHoverTime;
    }

    [Flags]
    private enum TMEFlags
    {
        TME_HOVER = 0x00000001,
        TME_LEAVE = 0x00000002,
        TME_NONCLIENT = 0x00000010,
        TME_QUERY = unchecked((int)0x40000000),
        TME_CANCEL = unchecked((int)0x80000000)
    }
}

我正在使用此类进行鼠标悬停事件。但不知怎的,这不起作用,它适用于移动和其他鼠标事件,但对于鼠标悬停我无论如何都无法使用它。尝试了所有在线但没有的东西。我在哪里错了?

感谢您帮助我。

2 个答案:

答案 0 :(得分:0)

MouseHookProc()lParam)的最后一个参数实际上不是HWND,而是指向MOUSEHOOKSTRUCT的指针,因此您将无效的有效内容传递给tme.hwndTrack

查看相关的MSDN页面:

不确定它是否是那里唯一的错误。

答案 1 :(得分:0)

WM_MOUSEHOVER不是真正的鼠标消息,它是由内部定时器proc生成的,当你调用TrackMouseEvent时(它检查鼠标是否移动并向窗口发送消息,如果仍然存在)仍然在给定的时间)。因此,WH_MOUSE钩子不会捕获它。

您可以使用WH_CALLWNDPROCWH_GETMESSAGE挂钩来抓住它。