web-dev-qa-db-ja.com

フックを介してキーストロークを別のプロセスに直接送信する

SendInput、SendKeys、PostMessage、SendMessage、SendNotifyMessage、keybd_eventなどのさまざまな問題をいじった後、私は不思議に思っています。それをうまく見つけるには...キーボード入力を別の非フォアグラウンドプロセスに送信しようとするのは、かなり厄介で信頼性がありません。

(現在のウィンドウを最前面に表示するために)Zオーダーをだまし、サードパーティのウィンドウをすばやくフォアグラウンドし、入力を送信して、ウィンドウを再フォアグラウンドにするSendInputメソッドを試しました。その中で最終的に失敗し、なぜかわからないのですが、フォアグラウンドではないときにもウィンドウでキーストロークを処理できました(プロセスを閉じることができるまで、2つのウィンドウ間で送受信の無限ループが発生していました)。

SendMessageとPostMessageのさまざまな組み合わせを試しました。 1つはダウン、もう1つはアップです。ダウンとアップの両方を使用すると、PostMessageの両方で問題が発生し、受信ウィンドウでキーが重複するためです。またはSendMessageの両方でテキスト入力に問題が発生しましたが、他の機能は問題なく動作しました。 keydownのSendMessageおよびkeyUpのPostMessageはすべての機能で機能しましたが、信頼性率は劇的に低下し、キーイベントにレイテンシが追加されました。 keydownのPostMessageとkeyupのSendMessageの組み合わせのみが、なんとかして有用なことを何とか実行でき、keyup登録の失敗率はおそらく5〜10%です。 SentNotifyMessageについても同様です(信頼性に関する限り、基本的にはSendMessageと同じように動作します)。

だから本質的には、私は聖霊降臨祭の終わりにいて、ターゲットウィンドウに直接フックを挿入し、それにメッセージキューをバイパスするなどの方法でキーストロークを送信するためのブードゥーを行うことを知りたかったのです。 procグローバルキーイベントではなく、ターゲットウィンドウにのみ影響します。唯一のことは、注入/フックなどに関しては私がかなり知らないことです。それで、私はあなた、コミュニティに目を向けます。

どうする?

19
Hydra

これは、バックグラウンドアプリケーションにメッセージを送信できるようにする小さなコードです。たとえば「A」文字を送信するには、sendKeystroke(Keys.A)を呼び出すだけで、Keysオブジェクトを使用できるように名前空間System.windows.formsを使用することを忘れないでください。

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);
    }
}
}
16
Louisbob

これをいじる必要があるかもしれませんが、プロセスを介してデータを送信できます。これはあなたの状況ではうまくいかないかもしれませんが、それは考えです。

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>");
8
Guest