Search code examples
javac++jnasendinput

SendInput won't execute correctly via Java Native Access for mouse movement


Intent

I am trying to create a localized implementation of cloud gaming where a user's own PC serves as the server. What I am trying to achieve is the last piece stopping me. That is implementing hardware mouse movements. This is where Windows' SendInput() comes into play.

Issue

The issue I have right now is that My entire code is based on Kotlin/Java. So, I don't exactly know how to replicate the functions of SendInput() in Java other than to use JNA to access C++ functions. More specifically access SendInput(). But, here's where I'm stuck. My Java code compiles, but doesn't execute when called.

Code to be translated to Java

#include<windows.h>
#include<iostream>
using namespace std;

void moveMouse(int x, int y) {
    INPUT input;
    input.type = INPUT_MOUSE;
    input.mi.dx = x;
    input.mi.dy = y;
    input.mi.time = 0;
    input.mi.dwFlags = MOUSEEVENTF_MOVE;
    UINT qwe = SendInput(1, &input, sizeof(input));
    cout<< qwe;
}

Code in Java using JNA

look into the comments in the code below.

import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef.DWORD;
import com.sun.jna.platform.win32.WinDef.LONG;
import com.sun.jna.platform.win32.WinUser.INPUT;

//import static com.sun.jna.Native.sizeof;//compilation error 'sizeof(int)' has private access in 'com.sun.jna.Native'
import static com.sun.jna.platform.win32.WinUser.INPUT.INPUT_MOUSE;

public class Test {

    public static void main(String[] a) {
        moveMouse(
                1_000L,
                1_000L
        );
    }

    /**
     * @param x change required in x co-ordinate
     * @param y change required in y co-ordinate
     *
     */
    public static void moveMouse(Long x, Long y) {
        INPUT input = new INPUT();
        input.type = new DWORD(INPUT_MOUSE);
        input.input.mi.dx = new LONG(x);
        input.input.mi.dy = new LONG(y);
        input.input.mi.time = new DWORD(0);
        input.input.mi.dwFlags = new DWORD(0x0001L);

        INPUT[] inputArray = {input};

        DWORD result = User32.INSTANCE.SendInput(new DWORD(1), inputArray, input.size());

        // sizeof (used below in the commented code) returns the compilation error
        // 'sizeof(int)' has private access in 'com.sun.jna.Native'
        // I got the idea to use sizeof from another StackOverflow post
//        DWORD result = User32.INSTANCE.SendInput(new DWORD(1), inputArray, sizeof(input));
        System.out.println("result = " + result.longValue());
        System.out.println("size = " + input.size());
    }
}

Output

result = 1
size = 40

You'll see that in SendInput, I'm sending the size of the input variable instead of the array's size which is what I'm supposed to send. To get the size of array in bytes, I got the idea to use sizeof, but, as you have seen above in the comments, you'll have already understood why I can't use it as I can't import a private function.

The mouse doesn't move a pixel when the Java code is executed.


Solution

  • I forgot to net type. Didn't realize this was necessary. Anyways, here is the code -

    public static void moveMouse(Long x, Long y) {
        INPUT input = new INPUT();
        input.type = new DWORD(INPUT_MOUSE);
        input.input.setType("mi");//--------------------------------------added this
        input.input.mi.dx = new LONG(x);
        input.input.mi.dy = new LONG(y);
        input.input.mi.time = new DWORD(0);
        input.input.mi.dwFlags = new DWORD(0x0001L);
        INPUT[] inputArray = {input};
        DWORD result = User32.INSTANCE.SendInput(new DWORD(1), inputArray, input.size());
    }
    

    For anybody having this issue, this function can be straight up copy pasted and it should work.