JNA本机函数调用和具有双**指针/数组内存分配的结构 [英] JNA native function call and a Structure with a Double**-Pointer/Array Memory-Allocation

查看:168
本文介绍了JNA本机函数调用和具有双**指针/数组内存分配的结构的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用JNA框架从Java应用程序中使用本机c ++ dll.我对函数调用有疑问.也许我没有正确分配内存?我没有意见,下一步该怎么做.我没有更多的信息可以帮助我脱离文档和论坛讨论区.我希望你能给我一个提示,会很好.

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.

我想调用一个本机函数(例如 FooInterface ).此函数将结构 TNativeFoo 作为输入和输出参数.结构 TNativeFoo 包含 Double ** / Double [] [] 数据.该字段可以看作是多维数组,但是第一个维度只有长度1.因此,它更多的是指向大小为 Items 的双精度数组的指针.

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.

结构( char ** / String [] )中还有一个 StringArray ,其长度为 StringsCount ,这是激怒的,因为未使用它.我提到它是因为我不确定该错误是否与此有关.下面是对本机dll和结构所做的定义.

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);
}

对于该函数的调用,我尝试在本机堆上分配内存并将数据写入其中.在像示例中那样将数据写入本机堆之后,我可以像在替代例2的示例中那样读取数据(我尝试了这种操作而没有调用本机函数,并且直接从 inputFoo 进行).像下面的示例一样,调用本机函数会引发致命错误.

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.

调用:

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位系统上运行的本机代码存在问题.由于遇到了JNA找不到本地dll"的问题,我在64位系统上安装了32位JVM.

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".

我发现我犯了一个错误,我查看了错误的Structure定义,该定义看起来相同,但事实并非如此.所以我必须改变我的问题.

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.

但是异常保持不变.也许我在代码中有多个错误.因此,在这里再次提出我的问题,希望现在可以更正并提供比以前更多的信息.(相对于公司)不得不对Foo进行一些操作,但是语义保持不变.

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.

我实际上没有h文件或源代码.我参加这个项目很晚,而我只是在做兼职.其他人正在用C ++/CLI编写本机调用,并且可以正常工作.所以我附上了下面的代码.在c ++/CLI代码下面是更改后的Java代码.

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.

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];
    }

    ...

}

Java定义:

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调用:

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();
        }
}

编辑2

我解决了dll中文件错误的问题后,异常已更改.该dll必须再次复制.这就解决了无法加载dll的问题.

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.
#

推荐答案

为什么不对结构使用对齐方式?那是您崩溃的最可能原因.验证Java中的字段偏移量(Structure.toString()会告诉您)与本机代码中的字段偏移量相同.

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.

PointerByReference 旨在传递给一个函数,在该函数中,被调用方将指针的值写入调用方传递的地址中.虽然您的用法在技术上是正确的,但实际上会混淆您的实际意图.

PointerByReference is 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.

如果您的结构包含 any 类型的指针,则应从 Pointer 的类型映射开始,然后根据需要调整类型映射.绝不适合将 PointerByReference 作为结构字段.

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. PointerByReference is never appropriate as a structure field.

   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倍.

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.

这篇关于JNA本机函数调用和具有双**指针/数组内存分配的结构的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆