我尝试执行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”,但在我的情况下,这两个调用都相同。
那么,我做错了什么?我想念什么吗?
答案 0 :(得分:0)
好吧,看来我已经找到了我的问题所在:
我混合使用LowLevelKeyboardProc和KeyboardProc。
当我使用“ SetWindowsHookEx(WH_KEYBOARD,...)”时,该函数是KeyboardProc而不是LowLevelKeyboardProc。因此,这是正确的Microsoft文档:
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 uint HiWord(IntPtr ptr)
{
if (((uint)ptr & 0x80000000) == 0x80000000)
return ((uint)ptr >> 16);
else
return ((uint)ptr >> 16) & 0xffff;
}
}