每次击键都会调用 LowLevelKeyboardProc 两次
LowLevelKeyboardProc is called twice with every keystroke
我尝试按照此处所述进行 VSTO 键挂钩:
我使用 Alex Butenko 的答案创建了这个 class 每次按下一个键时(应该)调用 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
按一下 space 会产生此调试输出:
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 is either WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWN or WM_SYSKEYUP" 但在我的情况下,两次调用都相同。
所以,我做错了什么?我错过了什么吗?
好的,看来我找到问题所在了:
我混淆了 LowLevelKeyboardProc 和 KeyboardProc。
当我使用“SetWindowsHookEx(WH_KEYBOARD,...”时,函数是 KeyboardProc 而不是 LowLevelKeyboardProc。所以这是正确的微软文档:
https://msdn.microsoft.com/en-us/library/ms644984(v=vs.85).aspx
所以 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 ulong HiWord(IntPtr ptr)
{
if (((ulong)ptr & 0x80000000) == 0x80000000)
return ((ulong)ptr >> 16);
else
return ((ulong)ptr >> 16) & 0xffff;
}
}
我尝试按照此处所述进行 VSTO 键挂钩:
我使用 Alex Butenko 的答案创建了这个 class 每次按下一个键时(应该)调用 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
按一下 space 会产生此调试输出:
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 is either WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWN or WM_SYSKEYUP" 但在我的情况下,两次调用都相同。
所以,我做错了什么?我错过了什么吗?
好的,看来我找到问题所在了:
我混淆了 LowLevelKeyboardProc 和 KeyboardProc。
当我使用“SetWindowsHookEx(WH_KEYBOARD,...”时,函数是 KeyboardProc 而不是 LowLevelKeyboardProc。所以这是正确的微软文档:
https://msdn.microsoft.com/en-us/library/ms644984(v=vs.85).aspx
所以 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 ulong HiWord(IntPtr ptr)
{
if (((ulong)ptr & 0x80000000) == 0x80000000)
return ((ulong)ptr >> 16);
else
return ((ulong)ptr >> 16) & 0xffff;
}
}