Java JNA本机函数调用和具有双**指针/数组内存分配的结构

Java JNA本机函数调用和具有双**指针/数组内存分配的结构,java,c++,native,jna,Java,C++,Native,Jna,我使用java C++框架中的本地C++来实现JNA框架。我对函数调用有问题。也许我没有正确分配内存?我没有意见,下一步该怎么办。我没有得到更多的信息来帮助我摆脱文档和论坛线程。我希望你能给我一个提示,那太好了 我想调用一个本机函数(在示例FooInterface中)。此函数将结构tnativefo作为输入和输出参数。结构tnativefo包含一个Double**/Double[][]数据。该字段可视为多维数组,但第一个维度的长度仅为1。所以它更像是指向双数组的指针,大小为Items 结构(ch

我使用java C++框架中的本地C++来实现JNA框架。我对函数调用有问题。也许我没有正确分配内存?我没有意见,下一步该怎么办。我没有得到更多的信息来帮助我摆脱文档和论坛线程。我希望你能给我一个提示,那太好了

我想调用一个本机函数(在示例
FooInterface
中)。此函数将结构
tnativefo
作为输入和输出参数。结构
tnativefo
包含一个
Double**
/
Double[][]
数据。该字段可视为多维数组,但第一个维度的长度仅为1。所以它更像是指向双数组的指针,大小为
Items

结构(
char**
/
String[]
)中还有一个
StringArray
,长度
stringscont
,这是不相关的,因为它没有被使用。我提到它是因为我不确定这个错误是否与此有关。下面是本机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);
                }
        }
}
我还可以想象在64位系统上运行的32位JVM和本机代码存在问题。我在我的64位系统上安装了一个32位JVM,因为我遇到了一个问题JNA“没有找到本机DLL”

编辑:
我发现我犯了一个错误,我查错了结构定义,看起来是一样的,但不是。所以我必须改变我的问题

但我也提出了我的问题是错误的,我尝试了你们所有人提出的建议。它们帮助我理解了这个主题,在我找到正确的结构定义后,我尝试了它们。谢谢你

但例外情况保持不变。也许代码中有不止一个错误。所以这里我的问题再次出现,希望现在是正确的,并且比以前有更多的信息。不得不做一些事情(关于公司),但语义保持不变

我没有真正的h文件或源代码。我参加这个项目很晚,我只是兼职。其他人正在用C++/CLI编程本机调用,它成功了。所以我在下面附上了代码。c++/CLI代码下面是更改后的Java代码

c++/CLI: Java调用: 编辑2 在我解决了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.
#

为什么对结构使用不对齐?这是你坠机的最可能原因。验证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是以位为单位的Double的大小,而不是字节,因此您分配的大小是实际需要大小的8倍。

我有两个建议:-

  • 您应该尝试使用jnavior为给定的c/c++头生成java代码。 (这将是一个非常好的起点)

  • 根据环境的不同,本机双精度/浮点大小将不同于Java双精度/浮点大小。i、 e native double可能等于Java float。所以你也需要检查一下

  • 因此,您需要使用不同的结构组合进行一些分析,以获得native所需要的内容。如果你没有通过考试,那么它将导致崩溃


    请同时发布您的答案。

    #2极不可能。我想不出任何现代CPU不符合IEEE-754规范(IBM有自己的格式,但他们也支持IEEE-754)。也感谢您的回答。我没有头文件*g。几天前来到这个项目,看起来头文件不存在。如果你没有头文件,你怎么知道结构是什么样子的?dll的作者实现了一个use-demo。可能连use-demo都有结构的定义,对吧?谢谢你的帮助!我会这样尝试,然后回来!谢谢你的帮助。。。我试过这样做,但没有解决问题。也许我现在更接近了。我也尝试了所有的路线,也没有改变。可能未使用的句柄参数有问题,我从方法调用中排除了该参数。在函数接口定义中将句柄声明为指针可以吗。我稍后使用null调用该函数。TNATEVEFO参数是C++定义中的空洞*指针,但我相信它可以作为TNATEVFO声明并传递。你有什么进一步的建议吗?或者很好的阅读。我没有找到那么多关于JNA的解释。真的吗?JNA在项目网站、JavaDoc以及包含的示例和单元测试中非常广泛地记录了它的用法。在关于支持的一节中,它明确指出,在请求帮助时,您应该为所有相关的数据结构和方法提供本机和(建议的)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);
    }
    
    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();
            }
    }
    
    #
    # 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.
    #
    
       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);
    }