Как перехватить все события клавиатуры и предотвратить потерю фокуса в приложении WinForms?

мой друг слепой, и у меня есть идея разработать программу, которая позволила бы ему использовать ПК с помощью слепого метода ввода и аудиальной обратной связи. Опыт был бы намного богаче (использование большего количества клавиш для определенных функций) и безупречен (например, предотвращение случайной потери фокуса), если бы мое приложение могло принудительно взять полный контроль над всем вводом клавиатуры, как только он запускается (я бы поставил его в Пуск для него). Я разработчик WinForms C# .Net, поэтому я хотел бы чтобы реализовать это в приложении, используя эту конкретную структуру и язык (не возражайте против обернутых вызовов WinAPI).

PS: Я не против, чтобы Система контролировала комбинацию Ctrl+Ald+Del, но я хотел бы взять под контроль все другие клавиши и комбинации, включая логотип Windows и стандартные кнопки запуска приложений.

2 ответов


вы можете использовать низкоуровневую реализацию крючка клавиатуры выложил здесь. Он не должен отвлекать внимание от любых программ, но ваша программа может быть уведомлена при нажатии клавиш. Это код из сообщения в случае, если ссылка перестает работать.

using System;
using System.Diagnostics;
using System.Windows.Forms;
using System.Runtime.InteropServices;

class InterceptKeys
{
    private const int WH_KEYBOARD_LL = 13;
    private const int WM_KEYDOWN = 0x0100;
    private static LowLevelKeyboardProc _proc = HookCallback;
    private static IntPtr _hookID = IntPtr.Zero;

    public static void Main()
    {
        _hookID = SetHook(_proc);
        Application.Run();
        UnhookWindowsHookEx(_hookID);
    }

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

    private delegate IntPtr LowLevelKeyboardProc(
        int nCode, IntPtr wParam, IntPtr lParam);

    private static IntPtr HookCallback(
        int nCode, IntPtr wParam, IntPtr lParam)
    {
        if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
        {
            int vkCode = Marshal.ReadInt32(lParam);
            Console.WriteLine((Keys)vkCode);
        }
        return CallNextHookEx(_hookID, nCode, wParam, lParam);
    }

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr SetWindowsHookEx(int idHook,
        LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool UnhookWindowsHookEx(IntPtr hhk);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
        IntPtr wParam, IntPtr lParam);

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr GetModuleHandle(string lpModuleName);
}

вы можете обрабатывать нажатия клавиш в HookCallback событие (или далее реферат основные части в отдельный класс и вызвать событие).


Я знаю, что эта тема старая, но я нашел возможное решение. Вы можете использовать последний ответ и немного отредактировать его:

вместо вызова следующего крючка

private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) {
    if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
    {
        int vkCode = Marshal.ReadInt32(lParam);
        Console.WriteLine((Keys)vkCode);
    }
    return CallNextHookEx(_hookID, nCode, wParam, lParam);
}

просто верните -1 для остановки обработки любого дескриптора к другим элементам управления

private static IntPtr HookCallback(
    int nCode, IntPtr wParam, IntPtr lParam)
{
    if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
    {
        int vkCode = Marshal.ReadInt32(lParam);
        Console.WriteLine((Keys)vkCode);
    }
    return -1;
}

это не хорошо, но это работает. Осторожнее с этим!