Algok
Algok

Reputation: 213

How to fill an array of structures in JNA?

I am trying to use the following windows API in JNA :

UINT WINAPI GetRawInputDeviceList(
  _Out_opt_  PRAWINPUTDEVICELIST pRawInputDeviceList,
  _Inout_    PUINT puiNumDevices,
  _In_       UINT cbSize
);

UINT cbSize is the size of a RAWINPUTDEVICELIST structure, in bytes. How to know it in JNA? I had accidentally found that 16 is a correct value.

The structure is the following:

typedef struct tagRAWINPUTDEVICELIST {
  HANDLE hDevice;
  DWORD  dwType;
} RAWINPUTDEVICELIST, *PRAWINPUTDEVICELIST;

pRawInputDeviceList is An array of RAWINPUTDEVICELIST structures so in JNA I'm declaring the following signature :

UINT GetRawInputDeviceList(PointerByReference pRawInputDeviceList, IntByReference puiNumDevices, UINT cbSize);

And here is my structurre in JNA :

public static class RawInputDeviceList extends Structure {

    public HANDLE hDevice;
    public DWORD dwType;

    public RawInputDeviceList() {
            // required for toArray()
    }

    public RawInputDeviceList(Pointer pointer) {
        super(pointer);
        read();
    }

    @Override
    protected List<String> getFieldOrder() {
        return Arrays.asList(new String[] { "hDevice", "dwType" });
    }

}

When i'm getting the PointerByReference value to get the pointer it is null, what is wrong with that? The number of devices is correct bu i can't get the RawInputDeviceList array correctly.

Here is the full test code:

public class TestRawInput {

    public static void main(String[] args) {
        PointerByReference lRawInputDevicesReference = new PointerByReference();
        IntByReference lNumDevices = new IntByReference();

        UINT lRes = WindowsRawInput.INSTANCE.GetRawInputDeviceList(lRawInputDevicesReference, lNumDevices, new UINT(16));

        Pointer lRawInputDevicePointer = lRawInputDevicesReference.getValue();
        WindowsRawInput.RawInputDeviceList lRawInputDevice = new WindowsRawInput.RawInputDeviceList(lRawInputDevicePointer);
        WindowsRawInput.RawInputDeviceList[] lDevices = (WindowsRawInput.RawInputDeviceList[]) lRawInputDevice.toArray(lNumDevices.getValue());

        System.out.println("devices deteced=" + lNumDevices.getValue());
        for (int i=0;i<lDevices.length;i++) {
            System.out.println("device type: " + lDevices[i].dwType);
        }
    }

    public interface WindowsRawInput extends StdCallLibrary {

        WindowsRawInput INSTANCE = (WindowsRawInput) Native.loadLibrary("user32", WindowsRawInput.class, W32APIOptions.DEFAULT_OPTIONS);

        UINT GetRawInputDeviceList(PointerByReference pRawInputDeviceList,
            IntByReference puiNumDevices, UINT cbSize);

        public static class RawInputDeviceList extends Structure {

            public HANDLE hDevice;
            public DWORD dwType;

            public RawInputDeviceList() {
                // required for toArray()
            }

            public RawInputDeviceList(Pointer pointer) {
                super(pointer);
                read();
            }

            @Override
            protected List<String> getFieldOrder() {
                return Arrays.asList(new String[] { "hDevice", "dwType" });
            }

        }
    }

}

Upvotes: 2

Views: 2051

Answers (2)

Algok
Algok

Reputation: 213

Here is the final code after technomage's answer :

using JNA and native library :

    IntByReference lNumDevices = new IntByReference();
    WindowsRawInput.RAWINPUTDEVICELIST lRawInputDevice = new WindowsRawInput.RAWINPUTDEVICELIST();
    int lRawInputSize = lRawInputDevice.size();

    // getting the size of devices to get and setting the structure size
    WindowsRawInput.INSTANCE.GetRawInputDeviceList(null, lNumDevices, lRawInputSize);

    // creating the device list
    WindowsRawInput.RAWINPUTDEVICELIST[] lDevices = (WindowsRawInput.RAWINPUTDEVICELIST[]) lRawInputDevice.toArray(lNumDevices.getValue());

    WindowsRawInput.INSTANCE.GetRawInputDeviceList(lDevices[0], lNumDevices, lRawInputSize);

Here is the JNA interface and structure :

public interface WindowsRawInput extends StdCallLibrary { 

    WindowsRawInput INSTANCE = (WindowsRawInput) Native.loadLibrary("user32", WindowsRawInput.class, W32APIOptions.DEFAULT_OPTIONS);

    UINT GetRawInputDeviceList(RAWINPUTDEVICELIST pRawInputDeviceList, IntByReference puiNumDevices, int cbSize);

    public static class RAWINPUTDEVICELIST extends Structure {

        public HANDLE hDevice;
        public DWORD dwType;

        public RAWINPUTDEVICELIST() { }

        @Override
        protected List<String> getFieldOrder() {
            return Arrays.asList(new String[] { "hDevice", "dwType" });
        }

    }
}

Upvotes: 2

technomage
technomage

Reputation: 10069

  1. Create an array of continguously-allocated structures using Structure.toArray().
  2. Pass the first element of that array to your native function. In this case, struct* is equivalent to struct[] (native types), so use RawInputDeviceList as the argument type instead of PointerByReference. I'm not sure why they call the individual structure a "list", but that's windows for you.
  3. Pass the size of your array in puiNumDevices, which presumably the native function will update with the count actually filled in.
  4. The native function will fill in the memory, and JNA will sync the native memory to Java Structure on function return.
  5. cbSize may be obtained by calling Structure.size().

Upvotes: 5

Related Questions