Я пишу программу, которая находится в 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);
}