I try to do a VSTO key hook as discribed here: Excel VSTO Key hook
I used the answer of Alex Butenko to create this class which (should) call OnKeyPress every time a key is pressed. The problem is, when I press one key, OnKeyPress is called twice:
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);
}
}
So when I press crtl+q the debug output is:
nCode: 0 wParam:81
Keys: 81 Crtl: True
nCode: 0 wParam:81
Keys: 81 Crtl: True
One press of space results in this debug output:
nCode: 0 wParam:32
Keys: 32 Crtl: False
nCode: 0 wParam:32
Keys: 32 Crtl: False
The microsoft documentation https://msdn.microsoft.com/en-us/library/windows/desktop/ms644985(v=vs.85).aspx says that: "wParam is either WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWN or WM_SYSKEYUP" but in my case its the same in both calls.
So, what I'm doing wrong? Am I missing something?