Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/334.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 修改键盘钩子代码以适合我自己的用途。。。从哪里开始?_C#_Input_Keyboard Shortcuts_Keyboard Hook - Fatal编程技术网

C# 修改键盘钩子代码以适合我自己的用途。。。从哪里开始?

C# 修改键盘钩子代码以适合我自己的用途。。。从哪里开始?,c#,input,keyboard-shortcuts,keyboard-hook,C#,Input,Keyboard Shortcuts,Keyboard Hook,我正在写一个安装在系统中的程序。在任何打开的应用程序中,它都将有一个键盘挂钩,用于所有键入的输入。我希望它截取任何键入的输入,在该输入上运行代码,然后发送要键入的新“正确”字符。这个新角色将出现在有焦点的应用程序中 我通过StackOverflow上的另一个问题找到了这段代码,看起来不错。 我将它作为类添加到解决方案中,在Form1构造函数中创建了一个新的实例,然后进行编译。任何应用程序中键入的任何输入都会显示在Visual Studio输出窗格中,每次显示一个字符 问题是这段代码太离谱了。这

我正在写一个安装在系统中的程序。在任何打开的应用程序中,它都将有一个键盘挂钩,用于所有键入的输入。我希望它截取任何键入的输入,在该输入上运行代码,然后发送要键入的新“正确”字符。这个新角色将出现在有焦点的应用程序中

我通过StackOverflow上的另一个问题找到了这段代码,看起来不错。

我将它作为类添加到解决方案中,在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
    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);
    }
这段代码如何适应这一系列事件?我需要在输入字符之前读取它们。那么,代码在哪里分析输入并决定正确的输入?最后,要调用什么正确的“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方法现在如下所示:

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
    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
已关闭,但会导致在foobar.exe中发生
类型为“System.ArgumentException”的第一次意外异常


具有不安全代码、无编组等的版本(仍然不输出“Z”!)


这段代码如何适应这一系列事件?我需要在输入字符之前读取它们。那么,代码在哪里分析输入并决定正确的输入

HookCallback
方法是您正在安装以拦截击键的方法

最后,要调用什么正确的“sendInput()”类型命令将字符发送到具有焦点的任何应用程序


要修改传递给其他进程的字符,请先修改参数(
nCode
wParam
、和/或
lParam
),然后再将其传递给
CallNextHookEx
方法。

目前,此代码只写入控制台。这发生在HookCallback方法的第4行。因此,您需要用自己的代码替换该行

然而,你得到的信息并不是非常友好的格式。您需要查看的SDK文档以了解如何解包。事实证明,所有好东西都在LPRAM中,它是指向KBDLLHOOKSTRUCT结构的指针


因此,您的代码需要将其封送到C#等价物。您需要声明一个KBDLLHOOKSTRUCT结构,或者查看pinvoke.net上是否有一个,然后使用Marshal.PtrToStructure将其解包。然后,您可以在Windows允许的范围内尽可能多地参与此结构--我认为修改事件数据比尝试杀死键盘事件然后模拟新事件更好(首先,我认为模拟的“发送键”会直接进入钩子本身!)--但是我还没有尝试过这一点。SDK结构确实包含一个虚拟密钥代码和扫描代码,因此您可以替换它们以达到您想要的效果,但这可能需要一些尝试和错误,并且非常特定于您的应用程序。

感谢您的回答,我现在更了解他的代码。该KBDLLHOOKSTRUCT应在何处申报?这是必须放在“自己的文件”中的东西吗?为什么它需要“解包”,而不是像keyboardHook.vkcode='e'之类的东西?一旦我以我想要的方式声明了KBDLLHOOKSTRUCT,如何将其复制到原始生成的KBDLLHOOKSTRUCT中?“compHook=myHook”的行为会导致计算机输入不同的字符吗,还是我必须做其他事情?还是我认为那部分完全错了?对于所有的问题,我很抱歉,我真的很感激。一旦您有了KBDLLHOOKSTRUCT类型的keyboardHook对象,您就可以编写“keyboardHook.vkCode='e'或其他任何东西”。但这段代码只提供了一个IntPtr(lParam)。因此,您需要将其转换为具有vkCode成员的KBDLLHOOKSTRUCT。然后使用Marshal.PtrToStructure将IntPtr转换为KBDLLHOOKSTRUCT——比如说,keyboardHook。在这里声明BDLLHOOKSTRUCT是一个有趣的问题,但我将它声明为InterceptKeys类的私有嵌套结构(因为它是该类的一个实现细节)。然后,我将replacementKey.vkCode更改为另一个字符。这就是允许另一个角色的所有需要更改的内容吗?最后,如何获得所需的
return CallNextHookEx(_hookID,nCode,wParam,lParam)我的KBB对象的值?哦,我也更新了我原来的问题。到目前为止看起来还可以吗?您的更新看起来还可以,但我认为您需要做更多的工作,将更改后的vkCode推入原始LPRAM内存块。(Marshal.PtrToStructure生成一个副本,因此您正在修改副本,而不是原始的。)我认为您可以通过调用Marshal.StructureToPtr将修改后的KBB复制回原始KBB的顶部来实现这一点。但我还没有试过,你可能需要做一些实验。然后你就可以像现在一样给CallNextHookEx打电话了。你还有什么发现吗?我现在被困在同一点上-我只是无法替换vkCode。。。你成功了吗?然后,用
return(IntPtr)1“吃掉”你不想经历的按键
而不是
return(CallNextHookEx(…
),并使用此答案连接一个事件,以获得所需的按键:最后,使用输入模拟器:编写您自己的击键。--但是