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é:
- Remplacement
return HookCallbackInner(nCode, wParam, lParam);
avec quelque chose de simple. - 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 laDispose
la mise en œuvre d'un) seulement faire l'appel, sihookId
n'est pasIntPtr.Zero
, et b) leshookId
àIntPtr.Zero
après avoir fait l'appel. - gist.github.com/Ciantic/471698
Vous devez vous connecter pour publier un commentaire.
Vous êtes à la création de votre délégué de rappel en ligne dans le SetHook appel de méthode. Ce délégué finira par le garbage collector, puisque vous n'êtes pas garder une référence à n'importe où. Et une fois que le délégué est des ordures collectées, vous n'obtiendrez pas plus de rappels.
Pour éviter cela, vous devez garder une référence au délégué en vie aussi longtemps que le crochet est en place (jusqu'à ce que vous appelez UnhookWindowsHookEx).
Le gagnant est: La Capture de l'Entrée de Clavier dans WPF, qui suggère de le faire :
...et puis il suffit d'utiliser le gestionnaire d'événement de l'argumentation du Texte de propriété:
IIRC, lors de l'utilisation mondiale des crochets, si votre fichier DLL n'est pas de retour de la fonction de rappel assez rapide, vous êtes retiré de la chaîne de rappels.
Donc, si vous êtes en train de dire que son travail pour un peu, mais si vous tapez trop vite, il s'arrête de fonctionner, je propose juste de stocker les clés à un certain point dans la mémoire et le dumping, les touches plus tard. Pour un exemple, vous pouvez vérifier la source pour certains enregistreurs de frappe, car ils utiliser cette même technique.
Bien que ce ne peut pas résoudre votre problème directement, il devrait au moins d'écarter une possibilité.
Avez-vous pensé à utiliser
GetAsyncKeyState
au lieu d'un hook global pour enregistrer les touches? Pour votre demande, il pourrait être suffisant, il y a beaucoup de exemples de mises en œuvre, et a été personnellement plus facile à mettre en œuvre.BeginInvoke
dans mon code fonctionne (voir code ci-dessus). Jusqu'à présent, il se sent comme il travaille depuis que j'ai essayé avec un peu fou filetage dort et il continue à fonctionner.J'ai vraiment été à la recherche pour cela. Merci de poster cela ici.
Maintenant, quand j'ai testé votre code, j'ai trouvé quelques bugs. Le code ne fonctionne pas la première. Et il ne pouvait pas gérer deux boutons de clic c'est à dire: CTRL + P.
Ce que j'ai changé, ce sont ces valeurs regardez ci-dessous:
private void HookCallbackInner
àce code de travail de 100% dans windows 10 pour moi 🙂
J'espère que cela contribuera u
J'ai utilisé le Dylan méthode à crochet mot clé global en application WPF et actualiser crochet après chaque pression sur la touche afin de prévenir les événements de cesser le tir après quelques clics . IDK, si elle est bonne ou une mauvaise pratique, mais fait le travail.
Détails de mise en œuvre ici