I’m using a native c++ dll out of an Java application with the JNA framework. I have a problem with a function invocation. Perhaps I’m not allocating the memory properly? I have no opinion, what to try next. I’m not getting any more information helping me out of the documentation and forum threads. I hope you can give me a hint, would be nice.
I want to call a native function (in example FooInterface). This function takes a structure TNativeFoo as input and output parameter. The structure TNativeFoo contains a Double**/Double[][] data. This field could be seen as a multidimensional array, but the first dimension has only the lenght 1. So its more a pointer pointing to a double array with the size Items.
There is also a StringArray in the structure (char**/String[]) with the length StringsCount, this is irrellevant, because it isn’t used. I mention it because I’m not sure if the error has something to do with this. Below is the definition made for the native dll and the structure.
public interface Foodll extends StdCallLibrary {
Foodll INSTANCE = (Foodll) Native.loadLibrary("foodll.dll", Foodll.class);
public static class TNativeFoo extends com.sun.jna.Structure {
public TNativeFoo (){
super();
setAlignType(ALIGN_NONE);
}
public TNativeFoo(com.sun.jna.Pointer pointer,int offset) {
super();
setAlignType(ALIGN_NONE);
useMemory(pointer,offset);
read();
}
public TNativeFoo(TNativeFoo struct) {
this(struct.getPointer(),0);
}
public static class ByReference extends TNativeFoo implements com.sun.jna.Structure.ByReference {
ByReference() {}
ByReference(TNativeFoo struct){super(struct.getPointer(),0);}
}
public static class ByValue extends TNativeFoo implements com.sun.jna.Structure.ByValue {
ByValue() {}
ByValue(TNativeFoo struct){super(struct.getPointer(),0);}
}
public PointerByReference Data;
public NativeLong Items;
public PointerByReference IrrelevantStringArray;
public NativeLong StringsCounts = new NativeLong(0);
}
NativeLong FooInterface(TNativeFoo input, TNativeFoo output);
}
For the call to the function I try to allocate the memory on the native heap and write the data to it. After writing the data to the native heap like in the example I could read it like I did in the example with alternative 2 (I tried this without invoking the native function, and directly from the inputFoo). Invoking the native function like in the example below throws me a fatal exception.
Invocation:
public class FooInvocationClass
{
public static FooInvocationMethod(double[] fooData)
{
Foodll foodllJnaLib = Foodll.INSTANCE;
Foodll.TNativeFoo outputFoo = new Foodll.TNativeFoo();
Foodll.TNativeFoo inputFoo = new Foodll.TNativeFoo();
//Writing input data to the native heap
Memory dataPointer = new Memory (fooData.length * Double.SIZE);
dataPointer.write(0, fooData, 0, fooData.length);
inputFoo.Data = new PointerByReference();
inputFoo.Data.setValue(dataPointer);
outputProfile.Data = new PointerByReference();
inputFoo.Items = outputFoo.Items = new NativeLong(fooData.length);
//Setting some irrelevant StringArray Parameters
inputFoo.IrrelevantStringArray = outputFoo.IrrelevantStringArray = new PointerByReference();
inputFoo.StringsCounts = outputFoo.StringsCounts = new NativeLong(0);
//Invocation
foodllJnaLib.FooInterface(inputFoo, outputFoo);
//Reading Output
Pointer outputFooDataPointer = outputFoo.Data.getValue();
//Reading Output alternative 1
Double[] outDataAlt1 = outputFooDataPointer.getDoubleArray(0, outputFoo.items);
//Reading Output alternative 2
Double[] outDataAlt2 = new Double[outputFoo.items];
for (int x = 0; x < outputFoo.items; x += 1)
{
outDataAlt2[x] = outputFooDataPointer.getDouble(x * 8);
}
}
}
Exception:
# A fatal error has been detected by the Java Runtime Environment:
#
# Internal Error (0x0), pid=5904, tid=220
#
# JRE version: 7.0_04-b22
# Java VM: Java HotSpot(TM) Client VM (23.0-b21 mixed mode, sharing windows-x86 )
# Problematic frame:
# C [KERNELBASE.dll+0xb9bc] RaiseException+0x58
#
# Failed to write core dump. Minidumps are not enabled by default on client versions of Windows
#
# An error report file with more information is saved as:
# E:\Programme\apache-tomcat-7.0.27\bin\hs_err_pid5904.log
#
I also could imagine that there is a problem with the 32bit JVM and native code running on a 64bit system. I had installed a 32bit JVM on my 64-bit system as I was struckeling with a problem JNA “didn’t find the native dlls”.
Edit:
I found a mistake I made, I looked in the wrong Structure definition, which looks the same, but isn’t. So I have to change my question.
But also I made my question was wrong, I tried the suggestions you all made. They helped me understand the topic, and I tried them after I found out about the right structure definition. Thank you for them.
But the Exception stays the same. Perhaps I have more than one error in the code. So here my question again, hopefully now correct and with more Information than before. Had to Foo a few things (in respect to the company), but the semantic stays the same.
I do not really have the h-files or the source code. I came into the Project late and I’m only working on it parttime. Someone else was programming the native calls in C++/CLI and it worked. So I have attached the code below. Below the c++/CLI codes is the changed Java-Code.
c++/CLI:
typedef void (__stdcall *TFooInterface) (
LONGLONG *APrgId,
void *input,
void *output,
double value,
HANDLE AAppHandle,
HANDLE AProgessBar,
char *AText,
char **ReturnText
);
//-APrgID is a ID, which tells the Method, what type of processing it should do.
//-The void Pointers both take a TNativeFOO Structure. One is for the input, the other one is for the output.
//-The double-value is a Parameter for the algorithm.
//-The Handle Parameters are for a programm, the dll was created for. They give status about the processing or something like that. We now use the dll in an other context, so they aren't needed anymore.
//-char *AText amd. char **ReturnText are, also not needed anymore. They are for exception handling as far as I know. We handle the Exception in the new Context more general.
typedef struct {
double *Data;
long Items;
unsigned char *String;
long StringCounts;
double Value1, Value2;
char *FileName;
char *DataObject;
char *Comment;
bool Valid;
} TNativeFoo;
//-*Data is a double Array holding the data to process in TFooInterface
//-Items is the lenght of Data
//-*String and StringCounts are not needed anymore, because the dll is used in another way. I can't tell what it is for.
//-Value1, Value2 are Parameters used in the algorithm
//-FileName is for storing the result. Isn't used.
//-DataObject, Comment is also never used. I can't tell what it is for.
//-Valid is a success-flag of the algorithm.
void FooClass::FooIt(double[] FooData, HMODULE hFooLib)
{
TNativeFoo Input, Output; //Declaration of the Structures
LONGLONG PrgID;
LONGLONG ID;
double value;
double[] OutputData = new double[FooData.length];
ID =-35547318716283305;
//setting up the Structures
memset(&Input, 0, sizeof(TNativeFoo));
memset(&Output, 0, sizeof(TNativeFoo));
Output.Data = new double[FooData.length];
Input.Data = &(FooData[0]);
Input.Items = Output.Items = FooData.length;
Input.Value1 = Output.Value1 = 1.3;
Input.Value2 = Output.Value2 = 2.3;
Input.Valid = Output.Valid = true;
FooInterface = (TFooInterface) GetProcAddress(hFooLib, "FooInterface");
FooInterface(&ID, &Input, &Output, value, 0, 0, "", NULL);
for (long i=0; i<Output.Items; i++)
{
OutputData[i]= Output.Data[i];
}
...
}
The Java Definitions:
public interface Foodll extends StdCallLibrary {
Foodll INSTANCE = (Foodll) Native.loadLibrary("foodll.dll", Foodll.class);
//Structure
public static class TNativeFoo extends com.sun.jna.Structure {
public TNativeFoo(){
setAlignType(Structure.ALIGN_NONE);
}
public TNativeFoo(com.sun.jna.Pointer pointer, int offset) {
super(pointer.share(offset));
setAlignType(Structure.ALIGN_NONE); //Tested all align-types. I was told this is the one used by the dlls
read();
}
public TNativeFoo(TNativeProfile struct) {
super(struct.getPointer());
setAlignType(Structure.ALIGN_NONE); //Tested all align-types. I was told this is the one used by the dlls
read();
}
public static class ByReference extends TNativeFoo implements com.sun.jna.Structure.ByReference {
ByReference() {}
ByReferenceTNativeFoo struct){super(struct.getPointer(),0);}
}
public static class ByValue extends TNativeFoo implements com.sun.jna.Structure.ByValue {
ByValue() {}
ByValueTNativeFoo struct){super(struct.getPointer(),0);}
}
public Pointer Data;
public double[] getData() {
if (this.Data == null) return null;
return this.Data.getDoubleArray(0, Items.intValue());
}
public void setData(double[] data) {
if (this.Data == null) {
this.Data = new Memory(data.length * 8);
}
this.Data.write(0, data, 0, data.length);
}
public NativeLong Items;
public String String;
public NativeLong StringCounts = new NativeLong(0);
public double Value1;
public double Value2;
public String FileName;
public String DataObject;
public String Comment;
public boolean Valid;
}
NativeLong FooInterface(DoubleByReference prgid, TNativeFoo input, TNativeFoo output, double value, Pointer aAppHandle, Pointer aProgessBar, String AText, String[] ReturnText);
}
Java-Invocation:
public class FooInvocationClass
{
public static FooInvocationMethod(double[] fooData, ID)
{
Foodll foodllJnaLib = Foodll.INSTANCE;
Foodll.TNativeFoo outputFoo = new Foodll.TNativeFoo();
Foodll.TNativeFoo inputFoo = new Foodll.TNativeFoo();
inputFoo.setData(fooData);
outputFoo.setData(new double[fooData.length]);
inputFoo.Items = outputFoo.Items = new NativeLong(fooData.length);
inputFoo.String = outputFoo.String = "";
inputFoo.StringCounts = outputFoo.StringCounts = new NativeLong(0);
inputFoo.Value1 = outputFoo.Value1 = 0.1;
inputFoo.Value2 = outputFoo.Value2 = 0.3;
Double ID =-35547318716283305;
double value = 0.025;
impDriveFiltJnaLib.ProfileFilterInterface(new DoubleByReference(ID), input, output, value, null, null, null, null);
Double[] outDataAlt1 = outputFoo.getDate();
}
}
Edit 2
Exception has changed after I resolved a Problem with a file error in the dll. The dll had to be copied again. That resolved a problem the dll couldn’t be loaded.
#
# A fatal error has been detected by the Java Runtime Environment:
#
# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x027047b8, pid=800, tid=7584
#
# JRE version: 7.0_04-b22
# Java VM: Java HotSpot(TM) Client VM (23.0-b21 mixed mode, sharing windows-x86 )
# Problematic frame:
# C [FooDll.dll+0x47b8] FooInterface+0x2288
#
# Failed to write core dump. Minidumps are not enabled by default on client versions of Windows
#
# An error report file with more information is saved as:
# E:\Programme\apache-tomcat-7.0.27\bin\hs_err_pid800.log
#
# If you would like to submit a bug report, please visit:
# http://bugreport.sun.com/bugreport/crash.jsp
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
#
Why are you using no alignment for your Structure? That’s the most likely cause of your crash. Verify that the field offsets in Java (Structure.toString() will tell you) are the same as those in native code.
PointerByReferenceis intended to be passed to a function where the callee writes the value of a pointer into the address passed by the caller. While your usage is technically correct, it actually obfuscates your actual intent.If your structure contains a pointer of any kind, you should start with a type mapping of
Pointer, and thereafter adjust the type mapping as needed.PointerByReferenceis never appropriate as a structure field.Note that Double.SIZE is the size of a double in bits, not bytes, so you’re allocating 8 times the size you actually need.