Модификация кода для подключения клавиатуры для моих собственных целей ... с чего начать? - PullRequest
0 голосов
/ 12 января 2010

Я пишу программу, которая находится в systray. Он будет иметь клавиатуру во всех типизированных вводах в любом открытом приложении. Я хочу, чтобы он перехватывал любой типизированный ввод, запускал код для этого ввода и затем отправлял новый «правильный» символ для ввода. Этот новый персонаж будет тем, что появляется в приложении, которое имеет фокус.

Я нашел этот код в другом вопросе о StackOverflow, и он выглядит хорошо. http://blogs.msdn.com/toub/archive/2006/05/03/589423.aspx

Я добавил его в качестве класса в своем решении, создал новый экземпляр в конструкторе Form1, и он компилируется. Любой типизированный ввод в любом приложении отображается на панели вывода Visual Studio по одному символу за раз.

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

Я предполагал, что эта программа будет работать примерно так:

key is pressed, triggers an event

get key information from the event

do computation on the key information, pick the character to be typed

send that key to the relevant program

my character is typed rather than the original keypress character

Как этот код вписывается в эту цепочку событий? Мне нужно прочитать входные символы, прежде чем они вводятся. Тогда где будет код для анализа ввода и принятия решения о правильном вводе в прямом эфире? И, наконец, какая команда типа sendInput () используется для отправки символа в любое приложение, в котором есть фокус?

Вот полный код для справки:

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

Спасибо за любой совет! Или есть лучший способ сделать это?

Спасибо!


UPDATE


Мой метод HookCallback теперь выглядит следующим образом:

    private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
    {
        if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
        {
            //Console.WriteLine((Keys)vkCode);

            KBDLLHOOKSTRUCT replacementKey = new KBDLLHOOKSTRUCT();
            Marshal.PtrToStructure(lParam, replacementKey);
            replacementKey.vkCode = 90; // char 'Z'
            Marshal.StructureToPtr(replacementKey, lParam, true);

        }
        return CallNextHookEx(_hookID, nCode, wParam, lParam);
    }

Shoot ... добавление Marshal.StructureToPtr близко, но приводит к ошибке A first chance exception of type 'System.ArgumentException' occurred in foobar.exe.


ВЕРСИЯ С БЕЗОПАСНЫМ КОДОМ, БЕЗ РАСПРОСТРАНЕНИЯ и т. Д. (Все еще не выводит 'Z'!)


    unsafe private static IntPtr HookCallback(int nCode, IntPtr wParam, void* lParam)
    {
        if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
        {
            KBDLLHOOKSTRUCT* replacementKey = (KBDLLHOOKSTRUCT*)lParam;
            replacementKey->vkCode = 90;
        }
        return CallNextHookEx(_hookID, nCode, wParam, (IntPtr) lParam);
    }

Ответы [ 2 ]

1 голос
/ 12 января 2010

На данный момент этот код просто пишет в консоль. Это происходит в строке 4 метода HookCallback. Поэтому вам нужно заменить эту строку своим собственным кодом.

Однако информация, которую вы предоставляете, не очень дружественна для C #. Вам нужно взглянуть на документы SDK для LowLevelKeyboardProc , чтобы понять, как его распаковать. Оказывается, все хорошее в lParam, который является указателем на структуру KBDLLHOOKSTRUCT.

Таким образом, ваш код должен преобразовать это в эквивалент C #. Вам нужно объявить структуру KBDLLHOOKSTRUCT или посмотреть, есть ли она на pinvoke.net, и использовать Marshal.PtrToStructure для ее распаковки. Затем вы можете использовать эту структуру настолько, насколько позволит Windows - я думаю, что изменение данных о событии лучше, чем попытка уничтожить событие клавиатуры, а затем смоделировать новое (для начала, я думаю, что имитированные «клавиши отправки» «Бежит прямо в сам крюк!) - однако я не пробовал это, чтобы увидеть, что это возможно. Структура SDK включает в себя код виртуального ключа и код сканирования, так что вы можете заменить их для достижения желаемого эффекта, но это, вероятно, потребует некоторых проб и ошибок и будет очень специфичным для вашего приложения.

1 голос
/ 12 января 2010

Как этот код вписывается в эту цепочку событий? Мне нужно прочитать входные символы, прежде чем они вводятся. Тогда где будет код для анализа ввода и принятия решения о правильном вводе в реальном времени?

Метод HookCallback - это метод, который вы устанавливаете для перехвата нажатий клавиш.

И, наконец, какая команда типа sendInput () используется для отправки символа в любое приложение, в котором есть фокус?

Чтобы изменить символ, передаваемый другим процессам, измените параметры (nCode, wParam и / или lParam), прежде чем передать их методу CallNextHookEx.

...