通过 JNA 向隐藏窗口发送击键

Posted

技术标签:

【中文标题】通过 JNA 向隐藏窗口发送击键【英文标题】:Sending Keystrokes to Hidden Window via JNA 【发布时间】:2019-06-11 11:34:51 【问题描述】:

背景:

我将击键发送到我隐藏的程序(文本编辑器),然后发送 F7 键,然后发送四个文本键(一种密码)。我正在使用 JNA 库和 Win32API 的 SendMessage 函数发送消息,不能使用 sendInput() 因为我需要发送到特定的窗口句柄。

代码:

private static void sendInputToWindow(WinDef.HWND editorWindowHandle, char[] password) throws InterruptedException 
        User32.INSTANCE.ShowWindow(editorWindowHandle, WinUser.SW_HIDE);
        User32.INSTANCE.SetForegroundWindow(editorWindowHandle);
        User32.INSTANCE.SetFocus(editorWindowHandle);

        //F7 KEY SENT
        WinDef.WPARAM wparam = new WinDef.WPARAM(F7_VIRTUAL_KEY);
        WinDef.LPARAM lparam = new WinDef.LPARAM(0);
        log.debug("SENDING F7");
        User32.INSTANCE.SendMessage(editorWindowHandle, WinUser.WM_KEYDOWN, wparam, lparam);
        Thread.sleep(1000);
        log.debug("SENDING PASSWORD");
        // PASSWORD SENT
        User32.INSTANCE.SendMessage(editorWindowHandle, WinUser.WM_CHAR, new WinDef.WPARAM(password[0]), lparam);
        User32.INSTANCE.SendMessage(editorWindowHandle, WinUser.WM_CHAR, new WinDef.WPARAM(password[1]), lparam);
        User32.INSTANCE.SendMessage(editorWindowHandle, WinUser.WM_CHAR, new WinDef.WPARAM(password[2]), lparam);
        User32.INSTANCE.SendMessage(editorWindowHandle, WinUser.WM_CHAR, new WinDef.WPARAM(password[3]), lparam);
        Thread.sleep(500);
        log.debug("SENDING ENTER");
        // ENTER KEY SENT
        User32.INSTANCE.SendMessage(editorWindowHandle, WinUser.WM_KEYDOWN, new WinDef.WPARAM(ENTER_KEY), lparam);
    

问题:

当我通过 SendMessage 发送击键时,经过一段时间或随机发送(我不知道是什么导致了这里的问题),但有时它根本不发送击键!

所以这是一个命中或未命中的情况,大多数情况下它会发送击键,而其他时候则不会。我想知道是否有更好的方法将击键发送到隐藏窗口?或者我在这里做错了什么。

谢谢。

【问题讨论】:

没有单一的机制可以做到这一点。 SendInput 是受支持的伪造输入方式。其他任何事情都只是黑客行为。当然,您不能指望直接发送WM_CHAR 消息。究竟什么对你有用取决于目标(又名受害者)窗口。正如我们在这里看到的大量输入伪造问题中经常出现的情况一样,重要的细节被省略了。例如,如果这只是一个 Win32 编辑控件,您可以使用WM_SETTEXT。但我们实际上并不知道这个控件到底是什么。 如果您确实想自动化 UI,可以使用 UI Automation。 @IInspectable 是否可以通过 Java 库(如在 JNA 中)使用,还是我必须为此使用 JNI? UI 自动化作为一组 COM 对象公开。 COM 与语言无关。我会惊讶地发现,没有用于 COM 的 JNA 包装器,甚至没有预构建的 UI 自动化库(如 this 之一)。 LPCWSTR Target_window_Name = TEXT("print.txt - Notepad"); HWND hWindowHandle = FindWindow(NULL, Target_window_Name); LRESULT 结果 = SendMessage(hWindowHandle, WM_SETFOCUS, 0, 0); std::cout 【参考方案1】:

如评论中所述,SendInput 是最受支持的。

我尝试在Win32 控制台中使用它,发现效果很好。代码如下。

#include <iostream>
#include <Windows.h>

int main()

    INPUT input[5];
    memset(input, 0, sizeof(input));

    input[0].type = input[1].type = input[2].type = input[3].type = input[4].type = INPUT_KEYBOARD; 
    SetForegroundWindow((HWND)0x000A09D8);//EDIT EDITOR HANDLE

    while (1)
           
        input[0].ki.wVk = '1';
        input[1].ki.wVk = '2';
        input[2].ki.wVk = '3';
        input[3].ki.wVk = '4';
        input[4].ki.wVk = VK_RETURN;

        SendInput(5, input, sizeof(INPUT));
        std::cout << GetLastError() << std::endl;
        Sleep(1000);
        input[0].ki.dwFlags = input[1].ki.dwFlags = input[2].ki.dwFlags = input[3].ki.dwFlags = input[4].ki.dwFlags = KEYEVENTF_KEYUP;
        SendInput(5, input, sizeof(INPUT));
        input[0].ki.dwFlags = input[1].ki.dwFlags = input[2].ki.dwFlags = input[3].ki.dwFlags = input[4].ki.dwFlags = 0;
        std::cout << GetLastError() << std::endl;
        Sleep(1000);
    

    return 0;

【讨论】:

但这需要窗口句柄聚焦,所以如果我将它设置为前景并将焦点设置为窗口,那么我可以发送它。有什么方法可以发送到后台窗口或强制焦点? @AbuBakarKhan SendInput 没有句柄参数,所以无法发送到后台窗口 @AbuBakarKhan SendMessage 可以发送到后台窗口,但不能保证消息的准确传输。这就是你担心的。我看了一些资料,@IInspectable 提到了, 使用UI Automation。这是唯一受支持的在不将 UI 置于前台的情况下实现 UI 自动化的方法。

以上是关于通过 JNA 向隐藏窗口发送击键的主要内容,如果未能解决你的问题,请参考以下文章

c语言能不能将dos窗口隐藏,不是最小化

Python 将击键发送到非活动应用程序

将鼠标事件发送到非活动和隐藏的窗口/WPF 窗体

GUI运行时如何在Matlab中截取击键

sendMessage 到隐藏窗口不起作用

jQuery.mobile 弹出窗口在显示后立即隐藏