Reputation: 7622
I'm accessing a native lib with JNA. This is my first time ever working with JNA and I also don't have experience with c/c++. I was able to get the application to work and I get a correct result returned and displayed using System.out.println()
. This is the last line of my code. The value is displayed in console and then Java crashes, eg. the dialog Java Platform SE binary has stopped working
is displayed with option to close or debug.
The console then shows this:
Java Result: -1073741819
Windows Event viewer says 0xc0000005 which according to existing answers is a me
EDIT 5 21th August 2018:
This has still not been solved and now I'm looking into it again. What was solved was the issues with MemoryError when looping. Since I really need a solution now I also stop obfuscating the dll. AFAIK the supplier it out of business. They don't reply to questions and the web site is outdated.
My "documentation" comes from here
Here the current java code:
public interface CLogP extends StdCallLibrary {
CLogP INSTANCE = (CLogP) Native.loadLibrary("BB-CLOGP", CLogP.class);
NativeLong calcLogP(String smiles, FloatByReference logP, NativeLongByReference numContrb, HANDLEByReference contrib);
}
public static void main(String[] args) {
//contrib can also be a PointerByReference, the behaviour is the same
HANDLEByReference contrib = new HANDLEByReference();
FloatByReference cLogP = new FloatByReference();
NativeLongByReference numContrib = new NativeLongByReference();
NativeLong err = CLogP.INSTANCE.calcLogP("c1ccccc1", cLogP, numContrib, contrib);
System.out.println(err.intValue());
System.out.println(cLogP.getValue());
System.out.println(numContrib.getValue());
//needs to be done after ever call to calcLogP
//then no issue running in a loop
Kernel32.INSTANCE.GlobalFree(contrib.getValue().getPointer());
// Tried to free other variables, does not have any effect
//Kernel32.INSTANCE.GlobalFree(cLogP.getPointer());
//Kernel32.INSTANCE.GlobalFree(numContrib.getPointer());
}
I'm kind of lost. Above works with an outdated version of the dll but not with the newest one. What's going wrong? Would disassembling it help in any way?
OLD CODE and INFO, only partially relevant now:
My code:
public static void main(String[] args) {
FloatByReference result = new FloatByReference();
//EDIT: changed to NativeLongByReference as per suggested answer
NativeLongByReference numContrib = new NativeLongByReference();
// This is a struct that needs to be passed by reference
MyDll.PContribution.ByReference contrib = new MyDll.PContribution.ByReference();
NativeLong err = MyDll.INSTANCE.calcResult("myValue", result, numContrib, contrib);
// I only care about result and not the other out-parameters
System.out.println(result.getValue());
//crash here
}
Defintion of the function in c:
typedef long (CALCRESULT)(const char*, float*, long*, HANDLE*);
What is going wrong? Do I need to perform some cleanup before the application terminates?
EDIT:
I can run the method call in a loop and it works. It only ever crashed when it terminates.
EDIT2:
As per comment here code for MyDll:
public interface MyDll extends Library {
public static class PContribution extends Structure {
public static class ByReference extends PContribution implements Structure.ByReference {
public byte[] Class = new byte[10];
public byte[] Type = new byte[6];
public byte[] Description = new byte[40];
public byte[] Comment = new byte[10];
public float Value;
}
protected List<String> getFieldOrder() {
return Arrays.asList(new String[] { "Class", "Type", "Description", "Comment", "Value" });
}
}
MyDll INSTANCE = (MyDll) Native.loadLibrary("MyDll", MyDll.class);
NativeLong calcResult(String smi, FloatByReference result, NativeLongByReference numContrb, PContribution contrib);
}
Definition of the struct
:
typedef struct {
char Class[10];
char Type[6];
char Description[40];
char Comment[10];
float Value;
} PContribution;
EDIT 3:
Damn. I figured out that the available documentation is for an old version of the dll. Using the old dll, everything works. So now I need to get docs of new version from supplier.
EDIT 4:
It works with old dll but the app consistently crashes after 65533 iterations (calls). Each call uses exactly the same parameters.
java.lang.Error: Invalid memory access
at com.sun.jna.Native.invokeInt(Native Method)
at com.sun.jna.Function.invoke(Function.java:390)
at com.sun.jna.Function.invoke(Function.java:323)
at com.sun.jna.Library$Handler.invoke(Library.java:236)
at com.sun.proxy.$Proxy0.calcLogP(Unknown Source)
The whole point of the exercise is to be able to make thousands of calls quickly.
Upvotes: 0
Views: 1218
Reputation: 9131
The Java result you get is an error message. -1073741819 is 0xc0000005, which is the code for STATUS_ACCESS_VIOLATION: you're accessing memory that you don't have permissions for.
The fact that it's occurring when your application closes indicates that it's associated with object finalization, which occurs when your program terminates and could also occur when garbage collection is triggered. It is likely that Java/JNA is attempting to release memory that C has not allocated.
When you get this sort of error you should investigate:
Type Mapping
A comparison of your code vs. the c function definition shows two mismatches.
The CALCRESULT function has a pointer to a long
in C. Whether this is a 32-bit or 64-bit long is operating system dependent while Java's long
is always 64-bit. You should map native long
values to JNA's NativeLong type. While this might not be causing the crash in this case, it is vitally important to know this distinction when the value is inside an array or a structure because it will throw off the byte offsets. As a pointer to a native long
, your variable numContrib
should therefore be of type NativeLongByReference
The CALCRESULT function has a pointer to the HANDLE
type in C, but you pass a structure (by reference). A few notes here:
Pointer
directly, creating a new Structure using that Pointer. Probably the same thing but it's more clear to me what's happening.)HANDLE
. A Handle is a reference to an internally maintained table that has access to other system resources, etc. Handles should be released when you are done using them, or you tie up those resources. JNA models the HANDLE
type; in this case you have a pointer so the appropriate type would probably be a HANDLEByReference. Memory allocation mismatches
It's unclear why you are using a structure when the API provides you a pointer to a HANDLE
type (JNA HANDLEByReference
). Java allocates the necessary memory when you define a new
instance of the structure (In your case the PContribution
) and JNA will map that Java memory to the local memory for you. However, the C function is giving a pointer to the HANDLE
type which takes up less memory than the structure you've defined. It's entirely possible that when Java is freeing up the structure memory on the Java side and then trying to release the same memory on the C side, it's running into problems because it's telling C to release more memory (for the structure) than C allocated (for the HANDLE). Answering further about this is not possible without more clear documentation from your API on how you get from this HANDLE to the structure you have listed in its place.
Releasing resources
Since the API is using the HANDLE
type it's likely other resources than memory are involved, and you have a responsibility to release those references. See the CloseHandle() function. Whether you have to close the handle yourself with this function, or whether the API will close it for you with one of its own methods implementing CloseHandle()
internall should be clearly documented in the API (in this case for the CALCRESULT function). Without the API to inspect I can't help much further, but carefully read the documentation to see if any of the structures you create in your code are required to be explicitly freed / released, and how.
Upvotes: 1