Страницы

Поиск по вопросам

воскресенье, 5 января 2020 г.

Как отловить все клавиши через низкоуровневый хук?

#c_sharp #winapi #клавиатура #keyboardhook


Как отловить в HookCallback все клавиши с проверкой по шифтам и.т.п?
Обычный (Keys) Marshal.ReadInt32 (lParam) просто пишет большими буквами.

public delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
private static readonly LowLevelKeyboardProc _proc = HookCallback;

private static IntPtr _hookID = IntPtr.Zero;

private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
  if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
  {
    try
    {
       Console.WriteLine((Keys)Marshal.ReadInt32(lParam));
    }
    catch (AccessViolationException ex) { Console.WriteLine($"Error: {ex.ToString()}"); }
  }
  return NativeMethods.CallNextHookEx(_hookID, nCode, wParam, lParam);
}


У меня есть идея занести список клавиш в List и оттуда как-то делать проверку. 

Но может у Вас есть идеи по лучше?! ( желательно с примером )
    


Ответы

Ответ 1



Как-то так: using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices; using System.ComponentModel; using System.Windows.Forms; namespace ConsoleApplication1 { class Program { static KeyLogger k; static void Main(string[] args) { k = new KeyLogger(); k.Start(); Console.CancelKeyPress += Console_CancelKeyPress; Console.WriteLine("Started listening for keyboard events..."); Application.Run(); } private static void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e) { k.Stop(); Application.Exit(); } } sealed class KeyLogger : IDisposable //отслеживает события клавиатуры с помощью низкоуровневого хука Windows { //объявления типов, констант и функций Windows API delegate IntPtr KeyboardProc(int nCode, IntPtr wParam, IntPtr lParam); const int WH_KEYBOARD_LL = 13; const int HC_ACTION = 0; const int WM_KEYDOWN = 0x0100; const uint VK_CAPITAL = 0x14; [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] static extern IntPtr SetWindowsHookEx(int idHook, KeyboardProc lpfn, IntPtr hInstance, int threadId); [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool UnhookWindowsHookEx(IntPtr hhk); [DllImport("kernel32.dll")] private static extern IntPtr LoadLibrary(string dllToLoad); [DllImport("USER32.dll")] public static extern short GetKeyState(int vKey); [DllImport("user32.dll")] public static extern short GetAsyncKeyState(int vKey); [DllImport("user32.dll")] public static extern int ToUnicodeEx( uint wVirtKey, uint wScanCode, byte[] lpKeyState, [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszBuff, int cchBuff, uint wFlags, IntPtr dwhkl); [DllImport("user32.dll")] public static extern bool GetKeyboardState(byte[] lpKeyState); [DllImport("user32.dll")] public static extern uint MapVirtualKey( uint uCode, uint uMapType); [DllImport("user32.dll")] public static extern IntPtr GetKeyboardLayout(uint idThread); [DllImport("user32.dll")] public static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId); [DllImport("user32.dll")] public static extern IntPtr GetForegroundWindow(); //преобразование виртуального кода клавиши в его Юникод-представление public static string VKCodeToUnicode(uint vkCode) { StringBuilder buf = new StringBuilder(); byte[] keyboardState = new byte[255]; short x; byte y; for (int i = 0; i < 255; i++) { if (i == VK_CAPITAL) { x = GetKeyState(i); } else { x = GetAsyncKeyState(i); } y = 0; if ((x & 0x8000) != 0) y = (byte)(y | 0x80); if ((x & 0x0001) != 0) y = (byte)(y | 0x01); keyboardState[i] = y; } ToUnicodeEx(vkCode, MapVirtualKey(vkCode, 0), keyboardState, buf, 5, 0, GetKeyboardLayout(GetWindowThreadProcessId(GetForegroundWindow(), IntPtr.Zero))); return buf.ToString(); } IntPtr _hookID = IntPtr.Zero; //начинает отслеживание событий клавиатуры public void Start() { if (disposed) throw new ObjectDisposedException("KeyLogger"); if (_hookID != IntPtr.Zero) return; _hookID = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookCallback, IntPtr.Zero, 0); if (_hookID == IntPtr.Zero) { int error = Marshal.GetLastWin32Error(); throw new Win32Exception(error, "Failed to install hook! Error: "+ error.ToString()); } } //останавливает отслеживание событий клавиатуры public void Stop() { if (disposed) return; if (_hookID == IntPtr.Zero) return; UnhookWindowsHookEx(_hookID); _hookID = IntPtr.Zero; } //вызывается Windows при нажатии клавиши IntPtr KeyboardHookCallback(int nCode, IntPtr wParam, IntPtr lParam) { if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN) { int vkCode = Marshal.ReadInt32(lParam); string s = VKCodeToUnicode((uint)vkCode); Console.Out.WriteLine("Key: " + ((Keys)vkCode).ToString() + "; Character: " + s); } return CallNextHookEx(_hookID, nCode, wParam, lParam); } //так как KeyLogger использует неуправляемый ресурс, реализуем Dispose и Finalize... bool disposed = false; public void Dispose() { if (disposed) return; Stop(); disposed = true; GC.SuppressFinalize(this); } ~KeyLogger() { Dispose(); } } } Известная проблема (Windows 10): если активно консольное окно, всегда распознаются символы раскладки, установленной в момент создания консоли, вместо текущей раскладки. Основано на данных вопросах: GetKeyboardState: определение состояния клавиши Некорректная работа перехватчика нажатых клавиш (кейлоггер) C#

Комментариев нет:

Отправить комментарий