3

I want to call GetRawInputDeviceInfo from my Java application using JNA.

This is what my JNA Library interface looks like:

public static final UINT RIDI_DEVICEINFO = new UINT(0x2000000b);

public static DWORD RIM_TYPE_KEYBOARD = new DWORD(1);

public static class RID_DEVICE_INFO extends Structure {
  public DWORD cbSize;
  public DWORD dwType;
  public RID_DEVICE_INFO_ ridDeviceInfo_;

  @Override
  protected List<String> getFieldOrder() {
    return asList("cbSize", "dwType", "ridDeviceInfo_");
  }
}

public static class RID_DEVICE_INFO_ extends Structure {
  public RID_DEVICE_INFO_MOUSE mouse;
  public RID_DEVICE_INFO_KEYBOARD keyboard;
  public RID_DEVICE_INFO_HID hid;

  @Override
  protected List<String> getFieldOrder() {
    return asList("mouse", "keyboard", "hid");
  }
}

public static class RID_DEVICE_INFO_MOUSE extends Structure {
  public DWORD dwId;
  public DWORD dwNumberOfButtons;
  public DWORD dwSampleRate;
  public BOOL fHasHorizontalWheel;

  @Override
  protected List<String> getFieldOrder() {
    return asList("dwId", "dwNumberOfButtons", "dwSampleRate", "fHasHorizontalWheel");
  }
}

public static class RID_DEVICE_INFO_KEYBOARD extends Structure {
  public DWORD dwType;
  public DWORD dwSubType;
  public DWORD dwKeyboardMode;
  public DWORD dwNumberOfFunctionKeys;
  public DWORD dwNumberOfIndicators;
  public DWORD dwNumberOfKeysTotal;

  @Override
  protected List<String> getFieldOrder() {
    return asList("dwType", "dwSubType", "dwKeyboardMode", "dwNumberOfFunctionKeys", "dwNumberOfIndicators", "dwNumberOfKeysTotal");
  }
}

public static class RID_DEVICE_INFO_HID extends Structure {
  public DWORD dwVendorId;
  public DWORD dwProductId;
  public DWORD dwVersionNumber;
  public USHORT usUsagePage;
  public USHORT usUsage;

  @Override
  protected List<String> getFieldOrder() {
    return asList("dwVendorId", "dwProductId", "dwVersionNumber", "usUsagePage", "usUsage");
  }
}

public UINT GetRawInputDeviceInfo(HANDLE hDevice, UINT uiCommand, PointerByReference pData, IntByReference pcbSize);

Then, I use GetRawInputDeviceInfo like this: (Assume hDevice is a valid HANDLE obtained from somewhere.)

UINT uiCommand = RIDI_DEVICEINFO;
RID_DEVICE_INFO deviceInfo = new RID_DEVICE_INFO();
deviceInfo.cbSize.setValue(deviceInfo.size());
PointerByReference pData = new PointerByReference(deviceInfo.getPointer());
IntByReference pcbSize = new IntByReference(deviceInfo.size());
UINT lResult = INSTANCE.GetRawInputDeviceInfo(hDevice, uiCommand, pData, pcbSize);

My problem is that lResult is -1 which GetRawInputDeviceInfo's documentation says indicates that pData was not large enough for the device info. Did I define pData or cbSize incorrectly? If so, what is the correct way to define them in this case?

My JNA version is 4.1.0. My application is running on Java 8 on Windows 7 64-bit.

Ortomala Lokni
  • 56,620
  • 24
  • 188
  • 240
Chry Cheng
  • 3,378
  • 5
  • 47
  • 79
  • I think your inner `RID_DEVICE_INFO_` should be a `Union`. It's also a bit easier to use `int` rather than `DWORD`, especially for `cbSize`. – technomage May 29 '15 at 14:56
  • Compare the size returned by your call with that returned by `Structure.size()` and the value of `sizeof(RID_DEVICE_INFO)`. – technomage May 29 '15 at 14:58

1 Answers1

1

At the very least you need to modify your structure to have the proper representation. You currently have three individual fields where instead you should have a Union. That will make your structure's size larger than intended.

In addition, you should pass the structure directly as the third argument (using PointerByReference is not only incorrect, but the native code will receive the address of a pointer rather than the address of your structure). When passing the structure directly, JNA knows when it needs to sync the Java fields with native memory.

public interface DeviceAccess extends StdCallLibrary {
    public static class RID_DEVICE_INFO extends Structure {
        public int cbSize;
        public int dwType;
        public RID_DEVICE_INFO_ ridDeviceInfo_;

        // Ensure the active field corresponds to what is read back from native memory
        protected void read() {
            super.read();
            type = RID_DEVICE_INFO_HID.class;
            switch(dwType) {
            case RID_DEVICE_INFO_MOUSE:
                type = RID_DEVICE_INFO_MOUSE.class; break;
            case RID_DEVICE_INFO_KEYBOARD:
                type = RID_DEVICE_INFO_KEYBOARD.class; break;
            default:
                break;
            }
            ridDeviceInfo_.setType(type);
        }

        @Override
        protected List<String> getFieldOrder() {
            return asList("cbSize", "dwType", "ridDeviceInfo_");
        }
    }

    public static class RID_DEVICE_INFO_ extends Union {
        public RID_DEVICE_INFO_MOUSE mouse;
        public RID_DEVICE_INFO_KEYBOARD keyboard;
        public RID_DEVICE_INFO_HID hid;
    }

    public UINT GetRawInputDeviceInfo(HANDLE hDevice, UINT uiCommand, RID_DEVICE_INFO pData, IntByReference pcbSize);
}

Then use it like this:

UINT uiCommand = RIDI_DEVICEINFO;
RID_DEVICE_INFO deviceInfo = new RID_DEVICE_INFO();
// Could also just put this in the constructor
deviceInfo.cbSize = deviceInfo.size();

IntByReference pcbSize = new IntByReference(deviceInfo.size());
UINT lResult = INSTANCE.GetRawInputDeviceInfo(hDevice, uiCommand, deviceInfo, pcbSize);
technomage
  • 9,861
  • 2
  • 26
  • 40