Globale en utilisant le clavier crochet (WH_KEYBOARD_LL) dans WPF / C#

J'ai cousu ensemble de code que j'ai trouvé dans internet me WH_KEYBOARD_LL de la classe helper:

Mettre le code suivant à certaines de vos utils libs, que ce soit YourUtils.cs:

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Windows.Input;
namespace MYCOMPANYHERE.WPF.KeyboardHelper
{
public class KeyboardListener : IDisposable
{
private static IntPtr hookId = IntPtr.Zero;
[MethodImpl(MethodImplOptions.NoInlining)]
private IntPtr HookCallback(
int nCode, IntPtr wParam, IntPtr lParam)
{
try
{
return HookCallbackInner(nCode, wParam, lParam);
}
catch
{
Console.WriteLine("There was some error somewhere...");
}
return InterceptKeys.CallNextHookEx(hookId, nCode, wParam, lParam);
}
private IntPtr HookCallbackInner(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0)
{
if (wParam == (IntPtr)InterceptKeys.WM_KEYDOWN)
{
int vkCode = Marshal.ReadInt32(lParam);
if (KeyDown != null)
KeyDown(this, new RawKeyEventArgs(vkCode, false));
}
else if (wParam == (IntPtr)InterceptKeys.WM_KEYUP)
{
int vkCode = Marshal.ReadInt32(lParam);
if (KeyUp != null)
KeyUp(this, new RawKeyEventArgs(vkCode, false));
}
}
return InterceptKeys.CallNextHookEx(hookId, nCode, wParam, lParam);
}
public event RawKeyEventHandler KeyDown;
public event RawKeyEventHandler KeyUp;
public KeyboardListener()
{
hookId = InterceptKeys.SetHook((InterceptKeys.LowLevelKeyboardProc)HookCallback);
}
~KeyboardListener()
{
Dispose();
}
#region IDisposable Members
public void Dispose()
{
InterceptKeys.UnhookWindowsHookEx(hookId);
}
#endregion
}
internal static class InterceptKeys
{
public delegate IntPtr LowLevelKeyboardProc(
int nCode, IntPtr wParam, IntPtr lParam);
public static int WH_KEYBOARD_LL = 13;
public static int WM_KEYDOWN = 0x0100;
public static int WM_KEYUP = 0x0101;
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,
IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
}
public class RawKeyEventArgs : EventArgs
{
public int VKCode;
public Key Key;
public bool IsSysKey;
public RawKeyEventArgs(int VKCode, bool isSysKey)
{
this.VKCode = VKCode;
this.IsSysKey = isSysKey;
this.Key = System.Windows.Input.KeyInterop.KeyFromVirtualKey(VKCode);
}
}
public delegate void RawKeyEventHandler(object sender, RawKeyEventArgs args);
}

Que j'utilise comme ceci:

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());
//I tried writing the data in file here also, to make sure the problem is not in Console.WriteLine
}
private void Application_Exit(object sender, ExitEventArgs e)
{
KListener.Dispose();
}
}

Le problème est qu'il cesse de fonctionner après l'appui sur les touches d'un moment. Aucune erreur n'est générée que si jamais, j'ai simplement ne pas obtenir quoi que ce soit à la sortie après un certain temps. Je ne peux pas trouver un modèle solide quand il cesse de travailler.

De reproduire ce problème est assez simple, frapper quelques touches comme un fou, souvent en dehors de la fenêtre.

J'ai l'impression que certains mal filetage problème derrière, quelqu'un a eu une idée comment faire pour garder ce travail?


Ce que j'ai déjà essayé:

  1. Remplacement return HookCallbackInner(nCode, wParam, lParam); avec quelque chose de simple.
  2. De le remplacer avec de l'appel asynchrone, en essayant de mettre de Sommeil 5000ms (etc).

Appel asynchrone ne pas faire fonctionner mieux, il semble arrêtez de toujours lorsque l'utilisateur maintient une simple lettre et vers le bas pour un certain temps.

  • pouvons-nous intercepter la clé et envoyer une touche différente à la place de celui pressé? Par Exemple en appuyant sur un envoie la clé de l'e .
  • Complet, pratique et documenté. C'est ce que j'aime dans StackOverflow
  • Oui, en effet c'est une nouvelle version. Je vais ajouter un lien vers le corps de texte.
  • J'ai un problème avec ce code et un lecteur de carte USB. Parfois, les touches ne sont pas décalées à droite. Dans la même session, je peux lire une fois %WHATEVERò1234_ et la prochaine 5WHAteVERò!"34_ (où ò ; et _ devrait être ?, mais cela dépend de mon clavier). Toute aide avant d'écrire mon propre clé virtuelle de l'analyseur?
  • Pas sûr de ce qui s'est passé ici. Matt révisions ont été ramenées en arrière pour une version qui est toujours affectée par la collecte des ordures. Vérifier les révisions précédentes; 15 pour Ciantic dernier, ou 16 pour Matt première, que j'ai tendance à préférer l'appellation. Les 17 et 18 sont cassés les versions.
  • Ma compréhension est que Dispose est destiné à être idempotent, qu'il pourrait être appelé à plusieurs reprises sur un objet donné. Je recommande de modifier la Dispose la mise en œuvre d'un) seulement faire l'appel, si hookId n'est pas IntPtr.Zero, et b) les hookId à IntPtr.Zero après avoir fait l'appel.
  • gist.github.com/Ciantic/471698