0

UI Automation要素を実装していないように見えるプログラムのUIで動作する必要があるアプリケーションを作成しています(Inspect.Exeはメインペインのみを表示し、子は表示しません)。

そこで、必要な機能を実装するための最良の方法を調べたところ、明らかに keybd_event() と mouse_event() の新しいバージョンである SendInput() が見つかりました。

ただし、キーボードフォーカスが必要であり、ターゲットウィンドウをフォアグラウンドに設定する余裕がないため (実行中にユーザーを煩わせないようにするため)、この回答が見つかるまで検索を続けました。Skurmedel が言ったことを実行し、アプリケーションのスレッドをターゲットのウィンドウ スレッドに結合しました。しかし今、ターゲットに SetFocus() を実行してから SendInput() を実行しても、ターゲット ウィンドウは影響を受けません。

私の質問は、「なぜこれが機能しないのですか?」です。または「私は何を間違っていますか?」、しかし、コード例がこれを整理するのに役立つと思います:

ThreadHandler クラス

class ThreadHandler
{
    #region P/Invoking and constants definition
    const uint WM_GETTEXT = 0x000D;

    [DllImport("user32.dll")]
    static extern IntPtr SetFocus(IntPtr hWnd);

    [DllImport("user32.dll")]
    private static extern int GetWindowThreadProcessId(IntPtr hWnd, uint lpdwProcessId = 0);

    delegate bool EnumThreadDelegate(IntPtr hWnd, IntPtr lParam);
    [DllImport("user32.dll")]
    static extern bool EnumThreadWindows(int dwThreadId, EnumThreadDelegate lpfn,
        IntPtr lParam);

    [DllImport("user32.dll")]
    static extern bool AttachThreadInput(int idAttach, int idAttachTo, bool fAttach);

    [DllImport("kernel32.dll")]
    static extern int GetCurrentThreadId();

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, int wParam, StringBuilder lParam);

    #endregion

    public readonly string ProcessName, WindowName;
    protected readonly int TargetThreadID, CurrentThreadID;
    protected readonly IntPtr TargetWindowHandle;

    public ThreadHandler(string processName, string windowName)
    {
        CurrentThreadID = GetCurrentThreadId();
        ProcessName = processName;
        WindowName = windowName;

        object[] objs = GetWindowThread(processName, windowName);
        if (objs == null)
        {
            throw new ArgumentException("Could not find the specified process/window.");
        }

        TargetThreadID = (int)objs[0];
        TargetWindowHandle = (IntPtr)objs[1];
    }

    public ThreadHandler(string processName)
    {
        CurrentThreadID = GetCurrentThreadId();
        ProcessName = processName;

        var processes = Process.GetProcessesByName(ProcessName);
        if (processes.Length == 0)
        {
            throw new ArgumentException("Could not find the specified process.");
        }
        var appProc = processes[0];

        WindowName = appProc.MainWindowTitle;
        TargetThreadID = GetWindowThreadProcessId(appProc.MainWindowHandle);
        TargetWindowHandle = appProc.MainWindowHandle;
    }

    public bool AttachThreadInput()
    {
        return AttachThreadInput(CurrentThreadID, TargetThreadID, true);
    }

    public bool DetachThreadInput()
    {
        return AttachThreadInput(CurrentThreadID, TargetThreadID, false);
    }

    public void SetFocus()
    {
        SetFocus(TargetWindowHandle);
    }

    static object[] GetWindowThread(string processName, string windowName)
    {
        var processes = Process.GetProcessesByName(processName);
        if (processes.Length > 0)
        {
            //Fill a list of handles
            var handles = new List<IntPtr>();
            foreach (ProcessThread thread in processes[0].Threads)
                EnumThreadWindows(thread.Id,
                    (hWnd, lParam) => { handles.Add(hWnd); return true; }, IntPtr.Zero);

            //Create a stringbuilder to function as storage unit
            StringBuilder nameBuffer = new StringBuilder(64);
            foreach (var hWnd in handles)
            {
                //And finally compare the caption of the window with the requested name
                nameBuffer.Clear();
                SendMessage(hWnd, WM_GETTEXT, nameBuffer.Capacity, nameBuffer);
                if (nameBuffer.ToString() == windowName)
                {
                    return new object[2] { GetWindowThreadProcessId(hWnd), hWnd };
                }
            }
        }
        return null;
    }
}

主な申請方法

static void Main(string[] args)
    {
        Console.WriteLine("Please input the name of the process to hook: ");
        string pName = Console.ReadLine();
        Console.WriteLine("Input the name of a specific window, or leave blank: ");
        string pWnd = Console.ReadLine();
        ThreadHandler threadHandler;
        try
        {
            if(!String.IsNullOrWhiteSpace(pWnd))
                threadHandler = new ThreadHandler(pName, pWnd);
            else
                threadHandler = new ThreadHandler(pName);
        }
        catch
        {
            Console.WriteLine("Error: " + pName +" does not seem to be running.");
            Console.ReadKey();
            return;
        }

        if (!threadHandler.AttachThreadInput())
        {
            Console.WriteLine("Error: The application tried to attach its Input Processing Mechanism to " + threadHandler.ProcessName + ", but failed.");
            Console.ReadKey();
            return;
        }
        Console.WriteLine("Input Processing Mechanism correctly attached to " + threadHandler.ProcessName + ".");
        threadHandler.SetFocus();
        InputSimulator.SimulateTextEntry("test"); //InputSimulator is a seemingly famous SendInput wrapper. Replacing this line with the code for a keystroke also doesn't work.
        Console.ReadLine();
        Console.WriteLine("Detaching Input Processing Mechanism.");
        threadHandler.DetachThreadInput();
    }

SendInput()の難解な芸術について私を解明できれば、事前に感謝します。

4

1 に答える 1

1

キーストロークを送信する特定のコントロールが適切にフォーカスされていることを確認してください。

SetFocusを使用して、キーストロークを送信するコントロールにフォーカスを与えることができるはずです。

SendMessagePostMessageを使用してキーストロークを送信することもできますが、これはBAD PRACTICEであり、避ける必要があります。

.NET の Forms クラスを介してキーストロークを送信する方法については、System.Windows.Forms.SendKeysを参照してください。

多くの場合、キーストローク自体が必要ない場合は、SendMessageWM_SETTEXTを使用してウィンドウのテキストを変更するだけで済みます。

于 2013-07-17T13:04:54.750 に答える