21

我想知道,在摆弄了 SendInput、SendKeys、PostMessage、SendMessage、SendNotifyMessage、keybd_event 等的各种问题之后。要找到那个好...尝试将键盘输入发送到另一个非前台进程是非常挑剔和不可靠的。

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

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

所以基本上,我已经走到了尽头,我想知道直接将一个钩子注入目标窗口,并做一些巫术来向它发送击键,绕过消息队列等。这样做的方式会不会触发全局键事件,只会影响目标窗口。唯一的问题是我在注射/钩住等方面一无所知。所以我求助于你,社区。

干吗?

4

2 回答 2

16

这是一个允许您向后台应用程序发送消息的小代码。例如,要发送“A”字符,只需调用 sendKeystroke(Keys.A),不要忘记使用命名空间 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);
    }
}
}
于 2012-09-29T11:36:42.113 回答
9

您可能不得不解决这个问题,但您可以通过进程发送数据。这可能不适用于您的情况,但这是一个想法。

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>");
于 2012-05-24T04:18:18.587 回答