How do you send keystrokes to an inactive window?

2019-06-14 17:14发布

问题:

as the title describes: Is there a way to send simulated keystrokes to an inactive window by using JNA ( ,because Java is my strongest language)? Of course when there is an alternative language, which can achieve this goal, I'd go for that.

I read lots of stuff on the web, also besides JNA but with no succes for this goal.

Right now I am able to simulate keystrokes with sendInput() with JNA but thats not what want, because it effects the active window. You can read about that here: https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-sendinput

My understanding is that you can use sendMessage() for that topic, but I cant get it working. https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-sendmessage

LRESULT SendMessage( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam );

There are also SendMessageA and SendMessageW. Some say SendMessage is too old for some os, but I could not verify that.

Lets take Notepad as an example. The window title is 'new 2 - Notepad++'

Keydown: https://docs.microsoft.com/en-us/windows/desktop/inputdev/wm-keydown

Keyup: https://docs.microsoft.com/en-us/windows/desktop/inputdev/wm-keyup

import com.sun.jna.Native;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.platform.win32.WinDef.LPARAM;
import com.sun.jna.platform.win32.WinDef.WPARAM;
import com.sun.jna.win32.StdCallLibrary;
import com.sun.jna.win32.W32APIOptions;

public interface User32 extends StdCallLibrary {
User32 INSTANCE = (User32) Native.loadLibrary("user32", User32.class, W32APIOptions.DEFAULT_OPTIONS);
LRESULT SendMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
}

public void winAPI() throws InterruptedException {
    HWND handler = User32.INSTANCE.FindWindow(null, "new 2 - Notepad++");
    // 0x0100 WM_KEYDOWN
    User32.INSTANCE.SendMessage(handler, 0x0100, new WinDef.WPARAM(0x31), new WinDef.LPARAM(0)}
    // recommended for dedection
    Thread.sleep(200);
    // 0x0101 WM_KEYUP
    User32.INSTANCE.SendMessage(handler, 0x0101, new WinDef.WPARAM(0x31), new WinDef.LPARAM(0)}
}

I struggle with the right implementation of SendMessage(A?)(W?)() since its not implemented in JNA.

Also how do you create WPARAM and LPARAM? MSDN says there are message specific. So when pass WM_KEYDOWN or WM_KEYUP as message parameter:

WPARAM is the virual KeyCode: just an int?

LPARAM is a bytearray(?).

I guess it doesnt work because of wrong parameter datatypes of WPARAM and LPARAM.

回答1:

I am not familiar with JNA but I'll give the following information from winapi aspect. Hope this helps you find the solution.

There are also SendMessageA and SendMessageW. Some say SendMessage is too old for some os, but I could not verify that.

The SendMessageA and SendMessageW represent Ascii and Unicode version of SendMessage function. They have the same capability. Refer to "Unicode in the Windows API".

I struggle with the right implementation of SendMessage(A?)(W?)() since its not implemented in JNA.

So feel free use SendMessage in JNA.

For the inactive window, you can't receive a keystroke message like WM_KEYUP from the system because you have no focus. But you can simulate the system to send this kind of message to the inactive window. You can refer to the following code. (Initial thread)

#include <windows.h>
#include <iostream>
#include <string>


int main()
{
    LPCWSTR Target_window_Name = TEXT("Untitled - Notepad"); //<- Has to match window name
    HWND hWindowHandle = FindWindow(NULL, Target_window_Name);
    HWND EditClass = FindWindowEx(hWindowHandle, NULL, L"Edit", NULL);

    SendMessage(EditClass, WM_KEYDOWN, 0x5A, 0x002C0001);
    SendMessage(EditClass, WM_CHAR, 0x7A, 0x002C0001); //"z"
    SendMessage(EditClass, WM_KEYUP, 0x5A, 0xC02C0001);

    return(0);
}

Also how do you create WPARAM and LPARAM? MSDN says there are message specific.

You need create WPARAM and LPARAM based on a different message. For example, WM_KEYDOWN message, wParam is the virtual-key code of the nonsystem key. See Virtual-Key Codes. In sample code above, the virtual-key code of Z key is 0x5A. So wParam is 0x5A. Same with WM_KEYUP message. In WM_CHAR message, wParam is the character code of the key. You can find in the Ascii table, the lowercase "z" is 0x7A. You also need to provide the scan code for these messages. You can search "Keyboard Scan Code Specification - Microsoft". The scan code of "Z" is 0x2C. The last, bit 30 and 31 of lParam of WM_KEYUP message are always 1. So it starts as 0xC0.

More references: "WM_KEYDOWN message" "WM_KEYUP message" "WM_CHAR message"