it-swarm.com.de

Verwenden des globalen Tastaturhakens (WH_KEYBOARD_LL) in WPF/C #

Ich habe aus dem Code, den ich im Internet selbst gefunden habe, WH_KEYBOARD_LL Hilfsklasse zusammengefügt:

Fügen Sie den folgenden Code in einige Ihrer utils-Bibliotheken ein, lassen Sie es YourUtils.cs sein:

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);
}

Was ich so benutze:

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();
    }
}

Das Problem ist, dass es nach einer kurzen Betätigung der Tasten nicht mehr funktioniert. Es wird kein Fehler gemeldet, ich bekomme nach einiger Zeit einfach nichts mehr auszugeben. Ich finde kein festes Muster, wenn es nicht mehr funktioniert.

Dieses Problem zu reproduzieren, ist ziemlich einfach. Schlagen Sie einige Tasten wie einen verrückten Mann an, normalerweise außerhalb des Fensters.

Ich vermute, es gibt ein böses Threading-Problem dahinter, hat jemand eine Ahnung, wie man das funktionieren lässt?


Was ich schon probiert habe:

  1. return HookCallbackInner(nCode, wParam, lParam); durch etwas einfaches ersetzen.
  2. Ersetzen Sie es durch einen asynchronen Aufruf und versuchen Sie, den Standby-Modus von 5000ms (usw.) zu aktivieren.

Der asynchrone Anruf hat es nicht besser gemacht, es scheint immer zu stoppen, wenn der Benutzer einen Brief für eine Weile hält.

54
Ciantic

Sie erstellen Ihren Callback-Delegaten inline im SetHook-Methodenaufruf. Dieser Delegierte wird schließlich Müll gesammelt, da Sie nirgendwo einen Hinweis darauf haben. Und sobald der Delegierte Müll gesammelt hat, werden Sie keine weiteren Rückrufe mehr erhalten.

Um dies zu verhindern, müssen Sie einen Verweis auf den Delegaten beibehalten, solange der Hook vorhanden ist (bis Sie UnhookWindowsHookEx aufrufen).

17
Mattias S

Der Gewinner ist: Erfassen Sie die Tastatureingaben in WPF , die Folgendes vorschlagen:

TextCompositionManager.AddTextInputHandler(this,
    new TextCompositionEventHandler(OnTextComposition));

... und verwenden Sie einfach die Texteigenschaft des Eventhandler-Arguments:

private void OnTextComposition(object sender, TextCompositionEventArgs e)
{
    string key = e.Text;
    ...
}
2
alexbk66

IIRC: Wenn Sie globale Hooks verwenden und Ihr DLL nicht schnell genug vom Callback zurückkehrt, werden Sie aus der Callback-Kette entfernt. 

Wenn Sie also sagen, dass es für eine Weile funktioniert, aber wenn Sie zu schnell tippen, hört es auf zu arbeiten. Ich empfehle Ihnen, die Schlüssel an einem bestimmten Ort im Speicher zu speichern und die Schlüssel später zu speichern. In einem Beispiel könnten Sie die Quelle für einige Keylogger überprüfen, da sie dieselbe Technik verwenden. 

Dies kann Ihr Problem zwar nicht direkt lösen, es sollte jedoch zumindest eine Möglichkeit ausschließen.

Haben Sie darüber nachgedacht, GetAsyncKeyState anstelle eines globalen Hooks zu verwenden, um Tastatureingaben zu protokollieren? Für Ihre Anwendung könnte es ausreichend sein, es gibt viele vollständig implementierte Beispiele und war persönlich einfacher zu implementieren.

1
mrduclaw

Ich habe wirklich danach gesucht. Vielen Dank, dass Sie dies hier gepostet haben.
Nun, als ich Ihren Code getestet habe, habe ich ein paar Fehler gefunden. Der Code funktionierte zunächst nicht. Und es konnte nicht mit zwei Knöpfen umgehen, d. H .: CTRL + P.
Was ich geändert habe, sind die folgenden Werte:
private void HookCallbackInner to

private void 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));
                }
            }
        }

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;
using FileManagerLibrary.Objects;

namespace FileCommandManager
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        readonly KeyboardListener _kListener = new KeyboardListener();
        private DispatcherTimer tm;

        private void Application_Startup(object sender, StartupEventArgs e)
        {
            _kListener.KeyDown += new RawKeyEventHandler(KListener_KeyDown);
        }

        private List<Key> _keysPressedIntowSecound = new List<Key>();
        private void TmBind()
        {
            tm = new DispatcherTimer();
            tm.Interval = new TimeSpan(0, 0, 2);
            tm.IsEnabled = true;
            tm.Tick += delegate(object sender, EventArgs args)
            {
                tm.Stop();
                tm.IsEnabled = false;
                _keysPressedIntowSecound = new List<Key>();
            };
            tm.Start();
        }

        void KListener_KeyDown(object sender, RawKeyEventArgs args)
        {
            var text = args.Key.ToString();
            var m = args;
            _keysPressedIntowSecound.Add(args.Key);
            if (tm == null || !tm.IsEnabled)
                TmBind();
        }

        private void Application_Exit(object sender, ExitEventArgs e)
        {
            _kListener.Dispose();
        }
    }
}

dieser Code funktioniert 100% in Windows 10 für mich :) Ich hoffe, das hilft dir

0
Alen.Toma

Ich habe die Dylans Methode verwendet, um das globale Schlüsselwort in der WPF-Anwendung einzuhaken und nach jedem Tastendruck einen Aktualisierungshaken auszuführen, um zu verhindern, dass Ereignisse nach wenigen Klicks aufhören. IDK, wenn es gute oder schlechte Praxis ist, aber die Arbeit erledigt.

      _listener.UnHookKeyboard();
      _listener.HookKeyboard();

Implementierungsdetails hier

0
EvilInside