我正在编写一个位于系统托盘中的程序。在任何打开的应用程序中,它将有一个键盘挂钩到所有键入的输入。我希望它拦截任何输入的输入,在该输入上运行代码,然后发送要输入的新的“正确”字符。这个新角色将出现在具有焦点的应用程序中。
我通过 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);
}
感谢您的任何建议!或者,有没有更好的方法来解决这个问题?
谢谢!
更新
我的 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);
}
射击...添加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);
}