2014-01-27 2 views
2

У меня есть интерфейс библиотеки Java (через JNA) с родной C++ DLL. Эта DLL предоставляет функции для установки обратных вызовов, которые вызывают при возникновении определенных аппаратных событий.C++ binary to Java получает «java.lang.Error: Недопустимый доступ к памяти»

Все эти обратные вызовы работают, кроме одного, хотя он имеет почти идентичное определение для другого.

Эти обратного вызова подписи и перечислений определены в коде 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 создается в основном цикле простого качели приложение используется для тестирования, RazerManager создает экземпляр Touchpad в конструкторе. Петля в то время используется для обработки сообщений с GetMessage с последующим Перевести и 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); 
     } 
    } 
    } 
} 

Теперь жест событие обрабатывается только в порядке, обратный вызов в классе Сенсорной панели вызывается и возвращает правильно. Когда динамическое событие ключа генерируются, генерируется исключение внутри в то время как сообщения обработки петли окон:

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) 

Это происходит только с динамическими ключевыми событиями, а не с помощью жестов или appevent. Я не могу понять, почему, так как динамические клавиши и обратные вызовы с жестами очень похожи на стороне C++.

EDIT: Полный код можно найти в GitHub repo, в частности RazerLibrary.java, RazerAPI.java, RazerManager.java и Touchpad.java. Испытание качания может быть найдено как gist.

+0

Посмотрите внимательно на отображение структуры события/объединения. Вы также можете избежать ненужных чтений/записи в памяти, используя версию Translate/DispatchMessage, которая принимает аргумент 'Pointer' (поскольку вы действительно не заботитесь о содержимом сообщения). – technomage

+0

Что ты имеешь в виду? Методы Translate/DispatchMessage JNA принимают только параметр MSG, не могут найти перегрузку, которая принимает указатель. – Sharparam

+0

Вы можете легко создать свои собственные «перегрузки», просто расширив существующий интерфейс. Вам не нравится этот метод с помощью 'Pointer' arg? Расширьте интерфейс, чтобы объявить тот же метод с аргументом 'MyStructure'. – technomage

ответ

1

Получается, что вы не можете (или, по крайней мере, вы должны сделать это иначе, чем я), чтобы один класс реализовал несколько интерфейсов обратного вызова. Создание явных реализаций различных интерфейсов обратного вызова и их назначение в поля обратного вызова в RazerManager.

Это объясняет, почему работает callback в Touchpad, но не в RazerManager (Touchpad реализовал один интерфейс, в то время как 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); 
    } 

кажется родной библиотеке или ЮНА запутается где-то, когда один класс реализует более одного интерфейса обратного вызова, и не знает, какой из них позвонить. Таким образом, он называет случайным (или первым определил?).

+0

Интересная проблема. Вы правы в том, что JNA будет использовать первый метод с именем 'callback' в первом интерфейсе, который он находит, исходя из' Callback'. В большинстве случаев ожидается, что обратный вызов будет иметь один открытый метод для использования в качестве метода обратного вызова, и в этом случае имя фактического метода может принимать любое значение. – technomage

Смежные вопросы