Je l'ai assemblé à partir d'un code que j'ai trouvé moi-même sur Internet. WH_KEYBOARD_LL
classe d'aide :
Ce problème est maintenant résolu, grâce à Mattias S le fichier YourUtils.cs suivant est corrigé .
Mettez le code suivant dans certaines de vos librairies utils, laissez-le être YourUtils.cs :
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Windows.Input;
namespace YOURUTILSNAMESPACE.Keyboard
{
/// <summary>
/// Listens keyboard globally.
///
/// <remarks>Uses WH_KEYBOARD_LL.</remarks>
/// </summary>
public class KeyboardListener : IDisposable
{
/// <summary>
/// Creates global keyboard listener.
/// </summary>
public KeyboardListener()
{
// We have to store the HookCallback, so that it is not garbage collected runtime
hookedLowLevelKeyboardProc = (InterceptKeys.LowLevelKeyboardProc)LowLevelKeyboardProc;
// Set the hook
hookId = InterceptKeys.SetHook(hookedLowLevelKeyboardProc);
// Assign the asynchronous callback event
hookedKeyboardCallbackAsync = new KeyboardCallbackAsync(KeyboardListener_KeyboardCallbackAsync);
}
/// <summary>
/// Destroys global keyboard listener.
/// </summary>
~KeyboardListener()
{
Dispose();
}
/// <summary>
/// Fired when any of the keys is pressed down.
/// </summary>
public event RawKeyEventHandler KeyDown;
/// <summary>
/// Fired when any of the keys is released.
/// </summary>
public event RawKeyEventHandler KeyUp;
#region Inner workings
/// <summary>
/// Hook ID
/// </summary>
private IntPtr hookId = IntPtr.Zero;
/// <summary>
/// Asynchronous callback hook.
/// </summary>
/// <param name="nCode"></param>
/// <param name="wParam"></param>
/// <param name="lParam"></param>
private delegate void KeyboardCallbackAsync(InterceptKeys.KeyEvent keyEvent, int vkCode);
/// <summary>
/// Actual callback hook.
///
/// <remarks>Calls asynchronously the asyncCallback.</remarks>
/// </summary>
/// <param name="nCode"></param>
/// <param name="wParam"></param>
/// <param name="lParam"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.NoInlining)]
private IntPtr LowLevelKeyboardProc(int nCode, UIntPtr wParam, IntPtr lParam)
{
if (nCode >= 0)
if (wParam.ToUInt32() == (int)InterceptKeys.KeyEvent.WM_KEYDOWN ||
wParam.ToUInt32() == (int)InterceptKeys.KeyEvent.WM_KEYUP ||
wParam.ToUInt32() == (int)InterceptKeys.KeyEvent.WM_SYSKEYDOWN ||
wParam.ToUInt32() == (int)InterceptKeys.KeyEvent.WM_SYSKEYUP)
hookedKeyboardCallbackAsync.BeginInvoke((InterceptKeys.KeyEvent) wParam.ToUInt32(), Marshal.ReadInt32(lParam), null, null);
return InterceptKeys.CallNextHookEx(hookId, nCode, wParam, lParam);
}
/// <summary>
/// Event to be invoked asynchronously (BeginInvoke) each time key is pressed.
/// </summary>
private KeyboardCallbackAsync hookedKeyboardCallbackAsync;
/// <summary>
/// Contains the hooked callback in runtime.
/// </summary>
private InterceptKeys.LowLevelKeyboardProc hookedLowLevelKeyboardProc;
/// <summary>
/// HookCallbackAsync procedure that calls accordingly the KeyDown or KeyUp events.
/// </summary>
/// <param name="keyEvent">Keyboard event</param>
/// <param name="vkCode">VKCode</param>
void KeyboardListener_KeyboardCallbackAsync(InterceptKeys.KeyEvent keyEvent, int vkCode)
{
switch (keyEvent)
{
// KeyDown events
case InterceptKeys.KeyEvent.WM_KEYDOWN:
if (KeyDown != null)
KeyDown(this, new RawKeyEventArgs(vkCode, false));
break;
case InterceptKeys.KeyEvent.WM_SYSKEYDOWN:
if (KeyDown != null)
KeyDown(this, new RawKeyEventArgs(vkCode, true));
break;
// KeyUp events
case InterceptKeys.KeyEvent.WM_KEYUP:
if (KeyUp != null)
KeyUp(this, new RawKeyEventArgs(vkCode, false));
break;
case InterceptKeys.KeyEvent.WM_SYSKEYUP:
if (KeyUp != null)
KeyUp(this, new RawKeyEventArgs(vkCode, true));
break;
default:
break;
}
}
#endregion
#region IDisposable Members
/// <summary>
/// Disposes the hook.
/// <remarks>This call is required as it calls the UnhookWindowsHookEx.</remarks>
/// </summary>
public void Dispose()
{
InterceptKeys.UnhookWindowsHookEx(hookId);
}
#endregion
}
/// <summary>
/// Raw KeyEvent arguments.
/// </summary>
public class RawKeyEventArgs : EventArgs
{
/// <summary>
/// VKCode of the key.
/// </summary>
public int VKCode;
/// <summary>
/// WPF Key of the key.
/// </summary>
public Key Key;
/// <summary>
/// Is the hitted key system key.
/// </summary>
public bool IsSysKey;
/// <summary>
/// Create raw keyevent arguments.
/// </summary>
/// <param name="VKCode"></param>
/// <param name="isSysKey"></param>
public RawKeyEventArgs(int VKCode, bool isSysKey)
{
this.VKCode = VKCode;
this.IsSysKey = isSysKey;
this.Key = System.Windows.Input.KeyInterop.KeyFromVirtualKey(VKCode);
}
}
/// <summary>
/// Raw keyevent handler.
/// </summary>
/// <param name="sender">sender</param>
/// <param name="args">raw keyevent arguments</param>
public delegate void RawKeyEventHandler(object sender, RawKeyEventArgs args);
#region WINAPI Helper class
/// <summary>
/// Winapi Key interception helper class.
/// </summary>
internal static class InterceptKeys
{
public delegate IntPtr LowLevelKeyboardProc(int nCode, UIntPtr wParam, IntPtr lParam);
public static int WH_KEYBOARD_LL = 13;
public enum KeyEvent : int {
WM_KEYDOWN = 256,
WM_KEYUP = 257,
WM_SYSKEYUP = 261,
WM_SYSKEYDOWN = 260
}
public static IntPtr SetHook(LowLevelKeyboardProc proc)
{
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
return SetWindowsHookEx(WH_KEYBOARD_LL, proc,
GetModuleHandle(curModule.ModuleName), 0);
}
}
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, UIntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
}
#endregion
}
(La méthode ci-dessus est asynchrone ce qui signifie qu'il n'arrêtera pas l'exécution de la méthode de rappel. Si, pour une raison quelconque, vous souhaitez intercepter les touches système (en créant par exemple un remplacement Alt+Tab) et empêcher Windows de manipuler vous devez modifier le BeginInvoke
a Invoke
. Ajoutant également Handled
aux événements KeyDown, KeyUp pour voir si elle est traitée. Après cela, vous pouvez soit appeler la fonction CallNextHookEx
ou pas.)
Que j'utilise comme ça :
App.xaml :
<Application ...
Startup="Application_Startup"
Exit="Application_Exit">
...
App.xaml.cs :
public partial class App : Application
{
KeyboardListener KListener = new KeyboardListener();
private void Application_Startup(object sender, StartupEventArgs e)
{
KListener.KeyDown += new RawKeyEventHandler(KListener_KeyDown);
}
void KListener_KeyDown(object sender, RawKeyEventArgs args)
{
Console.WriteLine(args.Key.ToString());
}
private void Application_Exit(object sender, ExitEventArgs e)
{
KListener.Dispose();
}
}
Le problème est que Correction de et peut être vu dans le code YourUtils.cs à la ligne // We have to store the HookCallback, so that it is not garbage collected runtime
Mise à jour Il existe une autre version (améliorée) disponible comme GitHub forkable Gist bien qu'il fasse une chose un peu différente donc je ne mets pas à jour le code ci-dessus.