我正在使用JNA框架的Java应用程序中使用本机c ++ dll。我有一个函数调用的问题。也许我没有正确分配内存?我没有意见,下一步该尝试什么。我没有得到任何更多信息帮助我摆脱文档和论坛主题。我希望你能给我一个提示,会很好。
我想调用本机函数(例如FooInterface
)。此函数将结构TNativeFoo
作为输入和输出参数。结构TNativeFoo
包含Double**
/ Double[][]
数据。这个字段可以看作是一个多维数组,但第一个维度只有长度1.因此它更多的是一个指向大小为Items
的双数组的指针。
结构中StringArray
(char**
/ String[]
)的长度为StringsCount
,这是不相关的,因为它没有使用。我提到它是因为我不确定错误是否与此有关。下面是对本机dll和结构的定义。
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);
}
对于函数的调用,我尝试在本机堆上分配内存并将数据写入其中。在将数据写入本机堆之后,就像在示例中一样,我可以像在替代2中的示例中那样读取它(我尝试了这一点而没有调用本机函数,而是直接从inputFoo
)。如下例所示调用本机函数会引发一个致命的异常。
调用:
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);
}
}
}
例外:
# 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
#
我也可以想象32位JVM和64位系统上运行的本机代码存在问题。我在我的64位系统上安装了一个32位JVM,因为我遇到了问题JNA“找不到原生dll”。
我发现了一个错误,我查了错误的结构定义,看起来一样,但事实并非如此。所以我必须改变我的问题。
但我也提出了我的问题,我尝试了你们所提出的建议。他们帮助我理解了这个主题,在我发现正确的结构定义之后我尝试了它们。谢谢你。
但是Exception保持不变。也许我在代码中有多个错误。所以这里再次提出我的问题,希望现在更正确,并提供比以前更多的信息。不得不做一些事情(就公司而言),但语义保持不变。
我真的没有h文件或源代码。我很晚才进入项目,我只在兼职工作。其他人在C ++ / CLI中编写本机调用并且它有效。所以我附上了以下代码。 c ++ / CLI代码下面是更改的Java代码。
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];
}
...
}
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);
}
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();
}
}
在我解决了dll中的文件错误问题后,异常发生了变化。必须再次复制该DLL。这解决了无法加载DLL的问题。
#
# 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.
#
答案 0 :(得分:2)
我有两个建议: -
您应该尝试使用jnaerator为给定的c / c ++标头生成java代码。 (这将是一个非常好的基础开始)。
根据环境,您的原生double / float大小将与Java double / float不同。即native double可能等于Java float。所以你也需要检查一下。
因此,您需要使用不同的结构组合对自己进行一些分析,以获得本机所需的内容。如果你没有传递确切,那么它将导致崩溃。
请发布你的答案。
答案 1 :(得分:1)
为什么你的结构没有对齐?这是导致崩溃的最可能原因。验证Java中的字段偏移量(Structure.toString()将告诉您)与本机代码中的字段偏移量相同。
PointerByReference
旨在传递给一个函数,其中被调用者将指针的值写入调用者传递的地址。虽然您的使用在技术上是正确的,但它实际上会混淆您的实际意图。
如果您的结构包含任何类型的指针,则应该从Pointer
的类型映射开始,然后根据需要调整类型映射。 PointerByReference
从不适合作为结构字段。
public static class TNativeFoo extends com.sun.jna.Structure {
public TNativeFoo (){ }
public TNativeFoo(com.sun.jna.Pointer pointer, int offset) {
super(pointer.share(offset));
read();
}
public TNativeFoo(TNativeFoo struct) {
super(struct.getPointer());
read();
}
// NOTE: use Java conventions for field names, please
private Pointer buffer;
public Pointer data = new Memory(Pointer.SIZE);
public NativeLong items;
public Pointer irrelevantStringArray = new Memory(Pointer.SIZE);
public NativeLong stringsCounts = new NativeLong(0);
public double[] getData() {
Pointer p = data.getPointer(0);
if (p == null) return null;
return p.getDoubleArray(items.intValue());
}
public void setData(double[] data) {
Pointer p = this.data.getPointer(0);
if (p == null) {
p = buffer = new Memory(data.length * 8);
this.data.setPointer(0, buffer);
}
p.write(0, data, 0, data.length);
}
}
NativeLong FooInterface(TNativeFoo input, TNativeFoo output);
}
请注意,Double.SIZE是以位为单位的大小,而不是字节,因此您分配的大小是实际需要的8倍。