i use jna 4.2.1
I have a method in the dll which returns a pointer to an object (C++)
basic_hash* getAlgorithmInstance( int algorithm )
basic_hash has the following methods (C++):
void reset ();
void partial (const byte* data, uint64 size);
void finalize (vector_byte& hash);
void hash (const byte* data, uint64 size, vector_byte& hash).
i have interface (java)
public interface HAL extends Library {
HAL INSTANCE = (HAL) Native.loadLibrary(
(Platform.isWindows() ? "HAL" : "libHAL"), HAL.class);
BasicHash getAlgorithmInstance(int i);
}
public static class BasicHash extends Structure {
public BasicHash() {}
public BasicHash(Pointer p) {
super(p);
read();
}
#Override
protected List getFieldOrder() {
return Arrays.asList(new String[] { "reset", "hash", "partial", "finalize" });
}
public interface Reset extends Callback { public void invoke();}
public Reset reset;
public interface Hash extends Callback {public void invoke(byte[] data, long size, byte[] hash);}
public Hash hash;
public interface Partial extends Callback {public void invoke(Pointer data, long size);}
public Partial partial;
public interface Finalize extends Callback {public void invoke(byte[] hash);}
public Finalize finalize;
}
and when I use the method without parameters in main()
HAL lib = HAL.INSTANCE;
BasicHash b = lib.getAlgorithmInstance(0);
b.reset.invoke();
I get an error:
Exception in thread "main" java.lang.Error: Invalid memory access
at com.sun.jna.Native.invokeVoid(Native Method)
at com.sun.jna.Function.invoke(Function.java:374)
at com.sun.jna.Function.invoke(Function.java:323)
at com.sun.jna.Function.invoke(Function.java:275)
at com.sun.jna.CallbackReference$NativeFunctionHandler.invoke(CallbackReference.java:646)
at com.sun.proxy.$Proxy1.invoke(Unknown Source)
at net.erver.ItServer.main(ItServer.java:79)
Why did I receive this error if the method resets the variables within the library? the method itself fulfills without problems (according to developers dll)
EDIT:
vector_byte has definition:
typedef unsigned char byte;
typedef std::vector< byte > vector_byte
and basic_hash has definition:
namespace HAL { namespace algorithms {
HAL_HASH_API enum class State : byte {
Normal,
Finished,
};
class HAL_HASH_API basic_hash
{
public:
virtual ~basic_hash() {}
virtual void reset() = 0;
virtual void partial( const byte*, uint64 ) = 0;
virtual void finalize( vector_byte& ) = 0;
virtual void hash( const byte*, uint64, vector_byte& ) = 0;
bool isFinished() {
return ( _state == State::Finished ? true : false );
}
protected:
State _state;
};
}
}
You need to use plain vanilla struct to pass data between JNA and your library. A C++ class (including a vector template) has a much different memory layout than does a simple C struct.
Related
I am working on a windows device manager which will work with java.
I stick on trying to pass without error SetupDiSetClassInstallParams function. (I am trying disable an device.)
I am running exact same structure(necessary way) in C++ and I do not have any problem.
I am getting ERROR_INVALID_USER_BUFFER error. When I tried get this error in C++ I need to change SP_PROPCHANGE_PARAMS structs values with wrong ones.
My struct declerations:
public static class SP_CLASSINSTALL_HEADER extends Structure {
public static class ByReference extends SP_CLASSINSTALL_HEADER implements Structure.ByReference {
public ByReference() {
}
public ByReference(Pointer memory) {
super(memory);
}
}
public SP_CLASSINSTALL_HEADER() {
cbSize = size();
}
public SP_CLASSINSTALL_HEADER(Pointer memory) {
super(memory);
read();
}
public int cbSize;
public long InstallFunction; **/* <-- this should be int or else buffer size changes, dll cannot place variables on right places. */**
protected List getFieldOrder() {
return Arrays.asList(new String[] { "cbSize", "InstallFunction" });
}
}
public static class SP_PROPCHANGE_PARAMS extends Structure {
public static class ByReference extends SP_PROPCHANGE_PARAMS implements Structure.ByReference {
public ByReference() {
}
public ByReference(Pointer memory) {
super(memory);
}
}
public SP_PROPCHANGE_PARAMS() {
}
public SP_PROPCHANGE_PARAMS(Pointer memory) {
super(memory);
read();
}
public SP_CLASSINSTALL_HEADER ClassInstallHeader = new SP_CLASSINSTALL_HEADER();
public int StateChange;
public int Scope;
public int HwProfile;
protected List getFieldOrder() {
return Arrays.asList(new String[] { "ClassInstallHeader", "StateChange", "Scope", "HwProfile" });
}
}
My function decleration:
boolean SetupDiSetClassInstallParams(WinNT.HANDLE hDevInfo, Pointer deviceInfoData, Pointer classInstallHeader, int size);
How do I calling this function:
SP_PROPCHANGE_PARAMS spPropChangeParams = new SP_PROPCHANGE_PARAMS();
spPropChangeParams.ClassInstallHeader.InstallFunction = DISetupApi.DIF_PROPERTYCHANGE;
spPropChangeParams.Scope = DISetupApi.DICS_FLAG_GLOBAL;
spPropChangeParams.HwProfile = 0;
spPropChangeParams.StateChange = DISetupApi.DICS_DISABLE;
int spPropChangeParamsSize = spPropChangeParams.size();
SP_CLASSINSTALL_HEADER classInstallHeaderReference = new SP_CLASSINSTALL_HEADER(spPropChangeParams.getPointer());
setupApi.SetupDiSetClassInstallParams(hDevInfo, device.getSPDeviceInfoData().getPointer(), classInstallHeaderReference.getPointer(),
spPropChangeParamsSize);
How It works in c++:
SP_PROPCHANGE_PARAMS spPropChangeParams;
spPropChangeParams.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
spPropChangeParams.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;
spPropChangeParams.Scope = DICS_FLAG_GLOBAL;
spPropChangeParams.HwProfile = 0;
spPropChangeParams.StateChange = DICS_DISABLE;
SetupDiSetClassInstallParams(hDeviceInfo, &device.getDeviceInfoData(), (SP_CLASSINSTALL_HEADER*)&spPropChangeParams, sizeof(spPropChangeParams));
Actually I mixed and matched too many ways these structs and function I changed variable types of structs and parameter types of function at the end I could not get anything but error. I cannot find what is my mistake. Could you please help me solve this.
Thanks in advance!
When you're passing around a Structure, don't use Structure.getPointer() unless you have to. When you do that, JNA can't automatically synch the native and Java data, and it's error-prone to remember where to do that yourself. In your case, whatever is in the Java fields never gets copied to native memory in your call to setupApi.SetupDiSetClassInstallParams.
Change your function mapping to this:
boolean SetupDiSetClassInstallParams(WinNT.HANDLE hDevInfo, SP_DEVINFO_DATA deviceInfoData, SP_CLASSINSTALL_HEADER classInstallHeader, int size);
and change the invocation to this:
setupApi.SetupDiSetClassInstallParams(hDevInfo, device.getSPDeviceInfoData(), classInstallHeaderReference, spPropChangeParamsSize);
EDIT
If you stick to your original struct definition (where SP_CLASSINSTALL_HEADER is a field), you need to add a function mapping to the interface (extend the interface and create your own instance of the native library):
public interface MySetupApi extends SetupApi {
MySetupApi INSTANCE = (MySetupApi)Native.loadLibrary(MySetupApi.class, W32APIOptions.DEFAULT_OPTIONS);
boolean SetupDiSetClassInstallParams(WinNT.HANDLE hDevInfo, SP_DEVINFO_DATA deviceInfoData, SP_PROPCHANGE_PARAMS propChangeParams, int size);
}
I'm using JNA to access some dll function from Java, this dll Native Function is declared as the following:
unsigned int ts3client_initClientLib(const struct ClientUIFunctions* functionPointers, const struct ClientUIFunctionsRare* functionRarePointers, int usedLogTypes, const char* logFileFolder, const char* resourcesFolder);
and so, I declared it inside library interface as the following:
int ts3client_initClientLib(Structure functionPointers, Structure functionRarePointers, int usedLogTypes, String logFileFolder, String resourcesFolder);
and then I call it the following way:
ts3client_initClientLib(null, null, 1, "log.log", "soundbackends");
but I'm getting the following Exception:
Exception in thread "main" 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.$Proxy0.ts3client_initClientLib(Unknown Source)
at pl.edu.tirex.ts3musicbot.MusicBot.main(MusicBot.java:17)
Correct solution:
Create event interface:
import com.sun.jna;
public interface ServerErrorEvent implements Callback
{
void invoke(long serverConnectionHandlerID, String errorMessage, int error, String returnCode, String extraMessage);
}
Create Client UI Functions Structure:
import com.sun.jna.Structure;
public class EventsStructure extends Structure
{
public ServerErrorEvent onServerErrorEvent;
#SuppressWarnings("rawtypes")
#Override
protected List<String> getFieldOrder()
{
List<String> fields = new ArrayList<String>();
for (Field f : this.getClass().getDeclaredFields())
{
boolean has = false;
for (Class<?> c : f.getType().getInterfaces())
{
if (c.equals(DefaultEvent.class))
{
has = true;
}
}
if (has)
{
fields.add(f.getName());
}
}
return fields;
}
}
Use following methods:
EventsStructure eventStructure = new EventsStructure();
eventStructure.onServerErrorEvent = new ServerErrorEvent() //implement server error event
{
#Override
public void invoke(long serverConnectionHandlerID, String errorMessage, int error, String returnCode, String extraMessage)
{
System.out.println("server error");
}
};
ts3client_initClientLib(eventStructure, null, 0, "logs", "soundbackends"); //invoke ts3client_initClientLib function from library
So i have native function in C declared as int NgSetEvent (int event, long callback). long Callback represent the callback function pointer. Callback prototype is declared as int oncodeline(int code, int documentid, char *string). The callback should be registered with method NgSetEvent. The problem is how to get pointer to that function which should be long or Nativelong? I have tried a lot of different aproaches but none of them gave a result. Callback was never invoked.
I have tried to do like it says in Turn a Callback into a Pointer in JNA, but with no success. i don't know what to try anymore.
Any help will be appreciated.
OnCodeline
public interface OnCodeline extends Callback {
int oncodeline (int code, int documentid, byte[] string);
}
OnCodelineStruct
public class OnCodelineStruct extends Structure {
public OnCodeline onc;
#Override
protected List getFieldOrder() {
return Arrays.asList(new String[] { "onc" });
}
}
Main class
OnCodelineStruct onCodelinStruct;
onCodelinStruct = new OnCodelineStruct();
onCodelinStruct.onc = new OnCodeline() {
#Override
public int oncodeline(int code, int documentid, byte[] string) {
System.out.println("This IS a CALLBACK!");
return 0;
}
};
sbDll.NgSetEvent(0, onCodelinStruct.getPointer().getNativeLong(0));
I have a Java library interfacing (via JNA) with a native C++ DLL. This DLL provides functions to set callbacks that are invoked when certain hardware events occur.
All of these callbacks work except one, even though it has near-identical definition to another one.
These callback signatures and enums are defined in C++ code:
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);
The following interfaces are defined in Java for callbacks:
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);
}
With these functions in the API/Library class/interface to set them:
// 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);
When registering callbacks, something similar to this is used (simplified to not clutter up with hundreds of lines, links to full code files at end of post).
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);
}
}
An instance of RazerManager is created in the main loop of a simple swing app used for testing, RazerManager creates an instance of Touchpad in its constructor. A while loop is used to process messages with GetMessage followed by Translate and DispatchMessage if valid, like the following:
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);
}
}
}
}
Now, gesture events are handled just fine, the callback in Touchpad class gets called and returns properly. When a dynamic key event is generated, an exception is thrown inside the while loop processing windows messages:
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)
This only happens with dynamic key events, not with gesture or appevent. I can't for the life of me understand why, as dynamic key and gesture callbacks are very similar on the C++ side.
EDIT: Full code can be found in the GitHub repo, specifically RazerLibrary.java, RazerAPI.java, RazerManager.java and Touchpad.java. The swing test can be found as a gist.
So it turns out that you can't (or at least you have to do it in a different way than I did) have one class implement multiple callback interfaces. Creating explicit implementations of the different callback interfaces and assigning them to the callback fields in RazerManager solved it.
This explains why the callback in Touchpad was working but not the ones in RazerManager (Touchpad implemented one interface while RazerManager did three).
To demonstrate:
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);
}
It seems the native library or JNA gets confused somewhere when a single class implements more than one callback interface, and doesn't know which one to call. So it calls one at random (or the first one defined?).
This is native method from ITLSSPProc.dll
NOMANGLE int CCONV OpenSSPComPort (SSP_COMMAND * cmd);
Here, SSP_COMMAND is structure in ITLSSPProc.dll which is in C++
Language.
struct SSP_COMMAND
{
unsigned long BaudRate;
unsigned char PortNumber;
};
So, I have to access OpenSSPComPort (SSP_COMMAND * cmd) in java using
JNI. Here is a code i have written,
public class Main {
public interface ITLSSPProc extends Library {
ITLSSPProc INSTANCE = (ITLSSPProc) Native.loadLibrary(
(Platform.isWindows() ? "ITLSSPProc" : "simpleDLLWindowsPort"), ITLSSPProc.class);
int OpenSSPComPort(Pointer param);
int CloseSSPComPort();
}
public static void main(String[] args)throws IOException {
ITLSSPProc sdll = ITLSSPProc.INSTANCE;
Memory intMem = new Memory(10); // allocating space
intMem.setLong(0,9600);
intMem.setString(1,"com7");
if(sdll.OpenSSPComPort(intMem)==1)
{// calling function with int parameter&result
System.out.println("connected");
}
else
{
System.out.println("failed");
}
}
}
Output : failed
Port number is COM7 on which we are working. So, when i run this
application and as i passing baud rate as manually as given in user
manual and if port number is correct it has to print "connected" on
console. So, anybody know where i am going wrong, i dont understand
where is actual problem..
JNA documentation for basic types (long, char).
JNA documentation for aggregate types (struct, struct *).
// tl;dr
class SSP_COMMAND extends Structure {
public NativeLong BaudRate;
public byte PortNumber;
}
int OpenSSPComPort(SSP_COMMAND param)