每一次击键,LowLevelKeyboardProc都被称为twiche

时间:2018-08-23 12:58:15

标签: c# keyboard vsto

我尝试执行VSTO密钥挂钩,如此处所述:Excel VSTO Key hook

我使用Alex Butenko的答案来创建此类,每次按下一个键(应)调用OnKeyPress。问题是,当我按一个键时,OnKeyPress被调用了两次:

static class ShortcutManager
{
    delegate int LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
    static readonly LowLevelKeyboardProc _proc = HookCallback;
    static IntPtr _hookID = IntPtr.Zero;
    const int WH_KEYBOARD = 2;
    const int HC_ACTION = 0;

    const int WM_KEYDOWN = 0x0100;


    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    static extern bool UnhookWindowsHookEx(IntPtr idHook);
    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
    [DllImport("user32.dll")]
    static extern short GetKeyState(int nVirtKey);

    static bool _keyHookingStarted;
    public static void Start(IShortcutDistributor dist)
    {
        m_dist = dist;
        if (!_keyHookingStarted)
        {
#pragma warning disable 0618
            _hookID = SetWindowsHookEx(WH_KEYBOARD, _proc, IntPtr.Zero, (uint)AppDomain.GetCurrentThreadId());
#pragma warning restore 0618
            _keyHookingStarted = true;
        }
    }
    public static void Stop()
    {
        m_dist = null;
        if (_keyHookingStarted)
        {
            UnhookWindowsHookEx(_hookID);
            _hookID = IntPtr.Zero;
            _keyHookingStarted = false;
        }
    }

    static IShortcutDistributor m_dist = null;

    static void OnKeyPress(uint keys)
    {
        var crtl = IsKeyDown(Keys.LControlKey) || IsKeyDown(Keys.RControlKey);
        Debug.WriteLine("Keys: "+ keys.ToString()+ " Crtl: "+ crtl.ToString());
        m_dist?.RaiseKeyPressedEvent(new KeyPressedEventArgs((Keys)keys, crtl));
    }
    static bool IsKeyDown(Keys keys)
    {
        return (GetKeyState((int)keys) & 0x8000) == 0x8000;
    }

    static int HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
    {

        if (nCode < 0)
        {
            return (int)CallNextHookEx(_hookID, nCode, wParam, lParam);
        }
        if (nCode == HC_ACTION)
        {
            Debug.WriteLine("nCode: " + nCode.ToString() + " wParam:" + wParam.ToString());
            OnKeyPress((uint)wParam);
        }
        return (int)CallNextHookEx(_hookID, nCode, wParam, lParam);
    }
}

因此,当我按crtl + q时,调试输出为:

nCode: 0 wParam:81
Keys: 81 Crtl: True
nCode: 0 wParam:81
Keys: 81 Crtl: True

按一下空格将产生以下调试输出:

nCode: 0 wParam:32
Keys: 32 Crtl: False
nCode: 0 wParam:32
Keys: 32 Crtl: False

Microsoft文档https://msdn.microsoft.com/en-us/library/windows/desktop/ms644985(v=vs.85).aspx说:“ wParam是WM_KEYDOWN,WM_KEYUP,WM_SYSKEYDOWN或WM_SYSKEYUP”,但在我的情况下,这两个调用都相同。

那么,我做错了什么?我想念什么吗?

1 个答案:

答案 0 :(得分:0)

好吧,看来我已经找到了我的问题所在:

我混合使用LowLevelKeyboardProc和KeyboardProc。

当我使用“ SetWindowsHookEx(WH_KEYBOARD,...)”时,该函数是KeyboardProc而不是LowLevelKeyboardProc。因此,这是正确的Microsoft文档:

https://msdn.microsoft.com/en-us/library/ms644984(v=vs.85).aspx

https://docs.microsoft.com/de-de/windows/desktop/inputdev/about-keyboard-input#_win32_Keystroke_Message_Flags

因此lParam是“按键消息标志”。这意味着标志KF_REPEAT = 30告诉我按键重复的频率。当我检查是否repeat = 0时,我才接到第一个电话。这是我的(希望是正确的)代码:

static class ShortcutManager
{
    delegate int KeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
    static readonly KeyboardProc _proc = HookCallback;
    static IntPtr _hookID = IntPtr.Zero;
    const int WH_KEYBOARD = 2;
    const int WH_KEYBOARD_LL = 13;
    const int HC_ACTION = 0;

    const int WM_KEYDOWN = 0x0100;


    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    static extern IntPtr SetWindowsHookEx(int idHook, KeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    static extern bool UnhookWindowsHookEx(IntPtr idHook);
    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
    [DllImport("user32.dll")]
    static extern short GetKeyState(int nVirtKey);

    static bool _keyHookingStarted;
    public static void Start(IShortcutDistributor dist)
    {
        m_dist = dist;
        if (!_keyHookingStarted)
        {
#pragma warning disable 0618
            _hookID = SetWindowsHookEx(WH_KEYBOARD, _proc, IntPtr.Zero, (uint)AppDomain.GetCurrentThreadId());
#pragma warning restore 0618
            _keyHookingStarted = true;
        }
    }
    public static void Stop()
    {
        m_dist = null;
        if (_keyHookingStarted)
        {
            UnhookWindowsHookEx(_hookID);
            _hookID = IntPtr.Zero;
            _keyHookingStarted = false;
        }
    }



    static IShortcutDistributor m_dist = null;

    const int KF_REPEAT = 0x4000;

    static void OnKeyPress(uint keys)
    {
        var crtl = IsKeyDown(Keys.LControlKey) || IsKeyDown(Keys.RControlKey);
        Debug.WriteLine("Keys: "+ keys.ToString()+ " Crtl: "+ crtl.ToString());
        m_dist?.RaiseKeyPressedEvent(new KeyPressedEventArgs((Keys)keys, crtl));
    }
    static bool IsKeyDown(Keys keys)
    {
        return (GetKeyState((int)keys) & 0x8000) == 0x8000;
    }

    static int HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
    {

        if (nCode < 0)
        {
            return (int)CallNextHookEx(_hookID, nCode, wParam, lParam);
        }
        if (nCode == HC_ACTION)
        {
            var repeat = (HiWord(lParam) & KF_REPEAT);
            Debug.WriteLine("nCode: " + nCode.ToString() + " wParam:" + wParam.ToString() + " repeat: "+ repeat.ToString());
            if (repeat == 0)
            {
                OnKeyPress((uint)wParam);
            }
        }
        return (int)CallNextHookEx(_hookID, nCode, wParam, lParam);
    }

    private static uint HiWord(IntPtr ptr)
    {
        if (((uint)ptr & 0x80000000) == 0x80000000)
            return ((uint)ptr >> 16);
        else
            return ((uint)ptr >> 16) & 0xffff;
    }
}