C# 通过挂钩将击键直接发送到另一个进程

C# 通过挂钩将击键直接发送到另一个进程,c#,winapi,c#-4.0,code-injection,sendmessage,C#,Winapi,C# 4.0,Code Injection,Sendmessage,我想知道,在处理了SendInput、SendKeys、PostMessage、SendMessage、SendNotifyMessage、keybd_事件等各种问题之后。找到那口井。。。试图将键盘输入发送到另一个非前台进程是非常挑剔和不可靠的 我尝试了一种SendInput的方法,在这种方法中,我欺骗了Z顺序(保持当前窗口在顶部),并快速对第三方窗口进行前景显示,发送输入,然后重新对我的窗口进行前景显示。其中一个最终失败了,而且不知何故,也不知道为什么,在我的窗口上也成功地处理了击键,而不是前

我想知道,在处理了SendInput、SendKeys、PostMessage、SendMessage、SendNotifyMessage、keybd_事件等各种问题之后。找到那口井。。。试图将键盘输入发送到另一个非前台进程是非常挑剔和不可靠的

我尝试了一种SendInput的方法,在这种方法中,我欺骗了Z顺序(保持当前窗口在顶部),并快速对第三方窗口进行前景显示,发送输入,然后重新对我的窗口进行前景显示。其中一个最终失败了,而且不知何故,也不知道为什么,在我的窗口上也成功地处理了击键,而不是前台(导致两个窗口之间发送和接收的无限循环,直到我成功地关闭该进程)

我尝试了SendMessage和PostMessage的不同组合。一个用于向下,一个用于向上,因为同时用于向下和向上会导致问题,例如用于两者的PostMessage,导致密钥在接收窗口上重复。或者两者都使用SendMessage,这会导致文本输入出现问题,但其他功能工作正常。SendMessage for keydown和PostMessage for keyUp适用于所有功能,但可靠性急剧下降,并增加了关键事件的延迟。只有用于keydown的PostMessage和用于keyup的SendMessage的组合能够完成任何有用的操作,keyup注册的失败率可能为5-10%。SentNotifyMessage也是如此(就可靠性而言,其行为基本上与SendMessage相同)

所以本质上,我已经到了whit的尽头,我想知道如何直接向目标窗口注入一个钩子,并通过巫术向它发送击键,绕过消息队列等。这样做不会处理全局键事件,只会影响目标窗口。唯一的问题是,当涉及到注射/钩住等问题时,我非常不了解。所以我求助于你们,社区


怎么办?

您可能不得不处理这个问题,但您可以通过进程发送数据。这可能不适合你的情况,但这是一个想法

using System;
using System.Runtime.InteropServices;
using System.Diagnostics;

    [DllImport("user32.dll", EntryPoint = "FindWindowEx")]
    public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter,          string lpszClass, string lpszWindow);
    [DllImport("User32.dll")]
    public static extern int SendMessage(IntPtr hWnd, int uMsg, int wParam, string lParam);

     static void Send(string message)
     {
         Process[] notepads = Process.GetProcessesByName("notepad");

         if (notepads.Length == 0)
             return;

         if (notepads[0] != null)
         {
             IntPtr child = FindWindowEx(notepads[0].MainWindowHandle, 
                 new IntPtr(0), "Edit", null);

             SendMessage(child, 0x000C, 0, message);
         }
     }

如果这不起作用,您可以始终执行以下操作:

System.Threading.Thread.Sleep(1000);
//User clicks on active form.
System.Windows.Forms.Sendkeys.Sendwait("<Message>");
System.Threading.Thread.Sleep(1000);
//用户单击活动窗体。
System.Windows.Forms.Sendkeys.Sendwait(“”);

这是一个小代码,允许您向后台应用程序发送消息。例如,要发送“A”字符,只需调用sendKeystroke(Keys.A),不要忘记使用namespace System.windows.forms即可使用Keys对象

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace keybound
{
class WindowHook
{
    [DllImport("user32.dll")]
    public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
    [DllImport("user32.dll")]
    public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
    [DllImport("user32.dll")]
    public static extern IntPtr PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

    public static void sendKeystroke(ushort k)
    {
        const uint WM_KEYDOWN = 0x100;
        const uint WM_SYSCOMMAND = 0x018;
        const uint SC_CLOSE = 0x053;

        IntPtr WindowToFind = FindWindow(null, "Untitled1 - Notepad++");

        IntPtr result3 = SendMessage(WindowToFind, WM_KEYDOWN, ((IntPtr)k), (IntPtr)0);
        //IntPtr result3 = SendMessage(WindowToFind, WM_KEYUP, ((IntPtr)c), (IntPtr)0);
    }
}
}

发送输入后,您需要等待一段时间,以便在将自己的进程设置回前台之前将其交付。不管名称如何,SendInput实际上将输入发布到输入队列;如果您的窗口在之后立即对焦,那么当系统处理输入时,它可能最终进入您的窗口!值得一提的是,将输入发送到非前台应用程序实际上是不受支持的;没有干净可靠的方法来做这件事。将目标应用程序置于前台并使用SendInput是唯一可靠的方法。SendMessage和PostMessage都有问题。你想解决的更大的问题是什么?为什么你要先发送输入?谢谢你的回复,BrendanMcK。我可以试着玩重新前景化窗口的时机,但我基本上希望避免100%。我之所以需要输入,是因为我正在编写一个多路复用器,用于跨局域网上的多个应用程序和物理机器进行键盘/鼠标输入。其中之一用于多拳击视频游戏(通常是MMO和RTS游戏)。对我来说,最好的情况是,如果我能弄明白怎么做的话,直接挂接到第三方进程中,并以某种方式向它发送输入。我的选择是什么?我应该把我的研究指向哪里?有人提到Spy++,我认为这确实给了我一点动力,帮助我解决我的问题。这样,我就可以对这两条消息使用PostMessage(),而不会出现密钥注册问题。然而,我似乎不明白这是怎么发生的:,当我只使用
PostMessage(handle,WM_KEYDOWN,key,0)时用于向下键和
后消息(句柄,WM_键向上,键,0)我已经设法用PostMessage()解决了重复问题;现在我可以使用PostMessage();对于keydown和keyup事件,这似乎极大地提高了可靠性。在有限的初始测试中,我还没有经历过一次关键的失败。文本输入很好。发生的事情是,我需要将LPRAM设置为0xC0000001,以便它在键控后不会生成WM_CHAR消息。而且,Spy++非常棒,没有它我永远也不会明白这一点。然而,我仍然对回答我最初的问题感兴趣,如何才能进行这种注射呢?PostMessage在这方面做得非常好。使用Spy++帮助我组织消息,使其看起来与来自实际键盘的消息完全相同。只是在Windows 10上尝试了一下,没有任何效果。不确定我是否做错了什么,或者它不再起作用了?我只想补充一点,这对我不再起作用了。我要做的是找到编辑窗口的句柄,它是WindowToFind的子窗口,并且使用PostMessage而不是SendMessage。我的代码是:IntPtr editHandle=(IntPtr)0x00850766//使用Spy++PostMessage(editHandle,0x100,(IntPtr)0x56,(IntPtr)0)找到;这正是我需要的。但是,我如何知道进程的lpszClass是什么?您可以使用Spy++找出lpClassName是什么。如果lpClassName为NULL,FindWindow将查找标题与lpWindowName参数匹配的任何窗口。()