C ++二进制文件到Java获取“java.lang.Error:无效的内存访问”

时间:2014-01-27 20:26:28

标签: java c++ winapi callback jna

我有一个Java库接口(通过JNA)与本机C ++ DLL。此DLL提供了用于设置在发生某些硬件事件时调用的回调的函数。

除了一个回调之外,所有这些回调都有效,即使它与另一回调的定义几乎相同。

这些回调签名和枚举在C ++代码中定义:

typedef enum _KEYSTATE
{
  KEYSTATE_NONE = 0, 
  KEYSTATE_UP, 
  KEYSTATE_DOWN, 
  KEYSTATE_HOLD, 
  KEYSTATE_INVALID, 
} KEYSTATETYPE, *P_KEYSTATETYPE;

typedef enum _DKTYPE
{
  DK_NONE = 0,
  DK_1,
  DK_2,
  DK_3,
  DK_4,
  DK_5,
  DK_6,
  DK_7,
  DK_8,
  DK_9,
  DK_10,
  DK_INVALID,
  DK_COUNT = 10
} DKTYPE, *P_DKTYPE;

typedef enum _GESTURETYPE
{
  GESTURE_NONE      = 0x00000000, 
  GESTURE_PRESS     = 0x00000001,
  GESTURE_TAP       = 0x00000002,
  GESTURE_FLICK     = 0x00000004,
  GESTURE_ZOOM      = 0x00000008,
  GESTURE_ROTATE    = 0x00000010,
  GESTURE_MOVE      = 0x00000020,
  GESTURE_HOLD      = 0x00000040,
  GESTURE_RELEASE   = 0x00000080,
  GESTURE_SCROLL    = 0x00000100,
  GESTURE_ALL       = 0xFFFF
} GESTURETYPE, *P_GESTURETYPE;

typedef enum _EVENTTYPE
{
  EVENT_NONE = 0,
  EVENT_ACTIVATED,
  EVENT_DEACTIVATED,
  EVENT_CLOSE,
  EVENT_EXIT,
  EVENT_INVALID,
} EVENTTYPETYPE, *P_EVENTTYPETYPE;

typedef HRESULT (CALLBACK *DynamicKeyCallbackFunctionType)(DKTYPE, KEYSTATETYPE); 
typedef HRESULT (CALLBACK *AppEventCallbackType) (EVENTTYPETYPE, DWORD, DWORD);
typedef HRESULT (CALLBACK *TouchpadGestureCallbackFunctionType)(GESTURETYPE, DWORD, WORD, WORD, WORD);
typedef HRESULT (CALLBACK *KeyboardCallbackFunctionType)(UINT uMsg, WPARAM wParam, LPARAM lParam);

以下接口在Java中定义用于回调:

interface DynamicKeyCallbackFunction extends StdCallLibrary.StdCallCallback {
  int callback(int rawDynamicKeyType, int rawDynamicKeyState);
}

interface AppEventCallbackFunction extends StdCallLibrary.StdCallCallback {
  int callback(int appEventType, WinDef.UINT dwAppMode, WinDef.UINT dwProcessID);
}

interface TouchpadGestureCallbackFunction extends StdCallLibrary.StdCallCallback {
  int callback(int gestureType, WinDef.UINT dwParameters,
               WinDef.USHORT wXPos, WinDef.USHORT wYPos, WinDef.USHORT wZPos);
}

interface KeyboardCallbackFunction extends StdCallLibrary.StdCallCallback {
  int callback(WinDef.UINT uMsg, WinDef.UINT_PTR wParam, WinDef.INT_PTR lParam);
}

使用API​​ / Library类/接口中的这​​些函数来设置它们:

// These are defined in an RazerAPI.java file as wrappers to simplify usage
// lib is an instance of a RazerLibrary from JNA

public Hresult RzSBAppEventSetCallback(AppEventCallbackFunction callback) {
  return Hresult.getFromApiValue(lib.RzSBAppEventSetCallback(callback));
}

public Hresult RzSBDynamicKeySetCallback(DynamicKeyCallbackFunction callback) {
  return Hresult.getFromApiValue(lib.RzSBDynamicKeySetCallback(callback));
}

public Hresult RzSBKeyboardCaptureSetCallback(KeyboardCallbackFunction callback) {
  return Hresult.getFromApiValue(lib.RzSBKeyboardCaptureSetCallback(callback));
}

public Hresult RzSBGestureSetCallback(TouchpadGestureCallbackFunction callback) {
  return Hresult.getFromApiValue(lib.RzSBGestureSetCallback(callback));
}

// These are the methods in the interface RazerLibrary.java file passed to JNA

int RzSBAppEventSetCallback(RazerAPI.AppEventCallbackFunction callback);
int RzSBDynamicKeySetCallback(RazerAPI.DynamicKeyCallbackFunction callback);
int RzSBKeyboardCaptureSetCallback(RazerAPI.KeyboardCallbackFunction callback);
int RzSBGestureSetCallback(RazerAPI.TouchpadGestureCallbackFunction callback);

当注册回调时,会使用与此类似的东西(简化为不会混乱数百行,在帖子末尾指向完整代码文件的链接)。

public class RazerManager implements DynamicKeyCallbackFunction {
  private static DynamicKeyCallbackFunction dkCallback;

  private RazerManager() {
    dkCallback = this;

    RazerAPI api = RazerAPI.INSTANCE;

    api.RzSBDynamicKeySetCallback(dkCallback);
  }

  @Override
  public int callback(int type, int state) {
    System.out.printf("DK Callback: %s %s", type, state);
    return 0; // S_OK
  }
}

public class Touchpad implements TouchpadGestureCallbackFunction {
  private static TouchpadGestureCallbackFunction gestureCallback;

  private Touchpad() {
    gestureCallback = this;

    RazerAPI api = RazerAPI.INSTANCE;

    api.RzSBGestureSetCallback(gestureCallback);
  }

  @Override
  public int callback(int gestureType, UINT param, USHORT x, USHORT y, USHORT z) {
    System.out.printf("Gesture: %s", gestureType);
  }
}

RazerManager的实例是在用于测试的简单swing应用程序的主循环中创建的,RazerManager在其构造函数中创建了一个Touchpad实例。 while循环用于处理带有GetMessage的消息,后跟Translate和DispatchMessage(如果有效),如下所示:

public class Main {
  public static void main(String[] args) throws IOException {
    // call javax's invokeLater to run app
  }

  public Main() {
    /* JFrame created here and then shown with setVisible(true); */ 

    RazerManager manager = RazerManager.getInstance();

    // Yes this is horrible, blocking everything but it's simply
    // used to get messages to go through and trigger the callbacks
    WinUser.MSG msg = new WinUser.MSG();
    while (true) {
      int hasMessage = User32.INSTANCE.GetMessage(msg, null, 0, 0);
      if (hasMessage != 0) {
        User32.INSTANCE.TranslateMessage(msg);
        User32.INSTANCE.DispatchMessage(msg);
      }
    }
  }
}

现在,手势事件处理得很好,Touchpad类中的回调被调用并正确返回。生成动态键事件时,会在while循环处理窗口消息中抛出异常:

Exception in thread "AWT-EventQueue-0" java.lang.Error: Invalid memory access
  at com.sun.jna.Native.invokeInt(Native Method)
  at com.sun.jna.Function.invoke(Function.java:383)
  at com.sun.jna.Function.invoke(Function.java:315)
  at com.sun.jna.Library$Handler.invoke(Library.java:212)
  at com.sun.proxy.$Proxy11.DispatchMessage(Unknown Source)
  at com.sharparam.jblade.tester.Main.<init>(Main.java:113)
  at com.sharparam.jblade.tester.Main$1.run(Main.java:25)
  at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:251)
  at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:733)
  at java.awt.EventQueue.access$200(EventQueue.java:103)
  at java.awt.EventQueue$3.run(EventQueue.java:694)
  at java.awt.EventQueue$3.run(EventQueue.java:692)
  at java.security.AccessController.doPrivileged(Native Method)
  at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
  at java.awt.EventQueue.dispatchEvent(EventQueue.java:703)
  at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:242)
  at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:161)
  at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:150)
  at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:146)
  at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:138)
  at java.awt.EventDispatchThread.run(EventDispatchThread.java:91)

这仅适用于动态键事件,而不是手势或附加事件。我无法理解为什么,因为动态键和手势回调在C ++方面非常相似。

编辑:可在GitHub repo中找到完整代码,具体为RazerLibrary.javaRazerAPI.javaRazerManager.javaTouchpad.java。挥杆测试可以找到gist

1 个答案:

答案 0 :(得分:1)

事实证明,你不能(或者至少你必须以与我不同的方式做到这一点)让一个类实现多个回调接口。创建不同回调接口的显式实现并将它们分配给RazerManager中的回调字段解决了它。

这解释了为什么Touchpad中的回调正在工作,而不是RazerManager中的回调(触摸板实现了一个接口,而RazerManager做了三个)。

演示:

public class MyClass {
  private static MyCallbackInterface myCallback;

  private MyClass() {
    myCallback = new CallbackInterface() {
      @Override
      public int callback(/* parameters */) {
        // Do stuff with data here
        return 0;
      }
    }

    nativeLib.SetCallback(myCallback);
  }

当单个类实现多个回调接口时,本机库或JNA似乎在某处混淆,并且不知道要调用哪个。所以它随机调用一个(或第一个定义?)。