【问题标题】:Transmit Structure Array from Java to C# using JNA and DLLExport使用 JNA 和 DLLExport 将结构数组从 Java 传输到 C#
【发布时间】:2021-02-23 14:10:37
【问题描述】:

我正在尝试使用 JNA 从 C# 向/从 Java 传输和接收结构数组。 在 Java 中从 C# 接收工作正常,但传输到 C# 只给出一行。 我认为问题是“pointerByReference.setPointer(array[0].getPointer())”。 但我不知道如何为数组创建 PointerByReference 以填充所有项目。 有人可以帮我吗?

C#

using System;
using System.Runtime.InteropServices;

namespace JNATest
{
    public class Class1
    {
        [StructLayout(LayoutKind.Sequential)]
        public struct Struct
        {
            public int x;
            public int y;
            public int z;
            public string name;
        }

        [DllExport]
        public static int getStructureArray(out Struct[] structureArray)
        {
            structureArray = new Struct[] {
                new Struct { x = 12345, y = 99, z = 65432, name = "Hello from C# 1" },
                new Struct { x = 4423, y = 44, z = 31, name = "Hello from C# 2" },
                new Struct { x = 65233, y = 1244, z = 323, name = "Hello from C# 3" },
            };
            return structureArray.Length;
        }

        [DllExport]
        public static void setStructureArray(Struct[] structureArray)
        {
            Console.WriteLine("Length: " + structureArray.Length);
            for (int i = 0; i < structureArray.Length; i++)
            {
                Console.WriteLine(structureArray[i].x);
                Console.WriteLine(structureArray[i].y);
                Console.WriteLine(structureArray[i].z);
                Console.WriteLine(structureArray[i].name);
            }
        }
    }
}

Java 接口

公共接口 IJNA 扩展库 { IJNA INSTANCE = (IJNA) Native.load("JNATest.dll", IJNA.class);

    @FieldOrder({ "x", "y", "z", "name" })
    public class Struct extends Structure {
        public int x;
        public int y;
        public int z;
        public String name;

        public Struct() {
        }

        public Struct(Pointer pointer) {
            super(pointer);
            read();
        }

        public Struct(Pointer pointer, int offset) {
            super(pointer.share(offset));
            read();
        }

        public Struct(Struct struct) {
            super(struct.getPointer());
            read();
        }
    
        public static class ByReference extends Struct implements Structure.ByReference {
            public ByReference(Pointer pointer) {
                super(pointer);
            }
        }

        public static class ByValue extends Struct implements Structure.ByValue {
        }
    }

    public int getStructureArray(PointerByReference structureArray);

    public void setStructureArray(PointerByReference structureArray);

Java 主程序

import com.sun.jna.ptr.PointerByReference;

public class Main {

    public static void main(String[] args) {
        getStructureArray();
        setStructureArray();
    }

    public static void getStructureArray() {
        System.out.println("--- getStructureArray ------------------------------------------------");
        PointerByReference pointerByReference = new PointerByReference();
        int length = IJNA.INSTANCE.getStructureArray(pointerByReference);
        IJNA.Struct.ByReference structure = 
            new IJNA.Struct.ByReference(pointerByReference.getValue());
        IJNA.Struct.ByReference structures[] = (IJNA.Struct.ByReference[]) structure.toArray(length);
        System.out.println("Length: " + length);
        for (int i = 0; i < structures.length; i++) {
            structure = structures[i];
            System.out.println("x: " + structure.x);
            System.out.println("y: " + structure.y);
            System.out.println("z: " + structure.z);
            System.out.println("name: " + structure.name);
        }
    }

    public static void setStructureArray() {
        System.out.println("--- setStructureArray ------------------------------------------------");
        IJNA.Struct[] array = new IJNA.Struct[5];
        for (int i = 0; i < array.length; i++) {
            array[i] = new IJNA.Struct();
            array[i].x = 1;
            array[i].y = 2;
            array[i].z = 3;
            array[i].name = "Hello from Java " + (i + 1);
            array[i].write();
        }

        PointerByReference pointerByReference = new PointerByReference();
        pointerByReference.setPointer(array[0].getPointer());

        IJNA.INSTANCE.setStructureArray(pointerByReference);
    }
}

输出:

--- getStructureArray ------------------------------------------------
Length: 3
x: 12345
y: 99
z: 65432
name: Hello from C# 1
x: 4423
y: 44
z: 31
name: Hello from C# 2
x: 65233
y: 1244
z: 323
name: Hello from C# 3
--- setStructureArray ------------------------------------------------
Length: 1
1
2
3
Hello from Java 1

编辑: 我更改了我的代码,但仍然得到相同的结果。

@FieldOrder({ "x", "y", "z", "name" })
public class Struct extends Structure {
    public int x;
    public int y;
    public int z;
    public String name;
}

public static void setStructureArray() {
    System.out.println("--- setStructureArray ------------------------------- 
    -----------------");
    IJNA.Struct struct = new IJNA.Struct();
    IJNA.Struct[] array = new IJNA.Struct[5];
    long size = struct.size();
    Memory memory = new Memory(array.length * size);
    for (int i = 0; i < array.length; i++) {
        array[i] = IJNA.Struct.newInstance(IJNA.Struct.class, memory.share(i 
                   * size, size));
        array[i].x = 1;
        array[i].y = 2;
        array[i].z = 3;
        array[i].name = "Hello from Java " + (i + 1);
    }
    IJNA.INSTANCE.setStructureArray(array);     
}

输出:

--- setStructureArray ------------------------------------------------
Length: 1
1
2
3
Hello from Java 1

编辑 2:

我自己找到了解决方案:

仅将 MarshalAs 参数添加到 Method 作品中。

C#

[DllExport]
    public static void setStructureArray([In, Out, 
        MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] Struct[] 
        structureArray, int length)
    {
        Console.WriteLine("Length: " + structureArray.Length);
        for (int i = 0; i < structureArray.Length; i++)
        {
            Console.WriteLine(structureArray[i].x);
            Console.WriteLine(structureArray[i].y);
            Console.WriteLine(structureArray[i].z);
            Console.WriteLine(structureArray[i].name);
        }
    }

【问题讨论】:

    标签: java c# native jna


    【解决方案1】:

    在 C 中,数组是“扁平的”,由单个连续内存块组成。

    您已经在 J​​ava 端定义了您的数组,如下所示:

    IJNA.Struct[] array = new IJNA.Struct[5];
    for (int i = 0; i < array.length; i++) {
                array[i] = new IJNA.Struct();
    ...
    }
    

    这会产生 5 个结构,每个结构在本机端都有不连续的内存支持。当您将指针传递给数组的第一个元素时,本机端不知道数组的其他元素在哪里。

    需要在Java端使用Structure.toArray()来分配连续内存:

    IJNA.Struct[] array = (IJNA.Struct[]) new IJNA.Struct().toArray(5);
    for (int i = 0; i < array.length; i++) {
            // array[i] = new IJNA.Struct(); <-- no longer needed
    ...
    }
    

    这声明了为所有 5 个结构提供了足够空间的后备内存,并且在内部还实例化了 Java 端对象,每个对象都引用了它们自己的起始内存地址:

    // This is internal to Structure.toArray. 
    // You do not need to implement this yourself
    array[i] = newInstance(getClass(), memory.share(i*size, size));
    

    您仍然可以像现在一样将地址传递给[0] 元素,但不同之处在于,其他元素实际上是本机端期望它们所在的位置,在第一个结构大小的偏移处。

    【讨论】:

    • 谢谢丹尼尔。函数 getStructureArray() 无需重新读取元素即可正常工作。我的问题是函数 setStructureArray()。在 C# 中,我只收到一行(请参阅输出)。
    • 糟糕。正确的答案是“您需要使用 Structure.toArray 连续声明结构内存”。我会更新我的答案。
    • 您好丹尼尔,非常感谢您的提示。我更改了代码,但仍然得到相同的结果。我已经更新了我的问题。
    • 您不需要自己实现低级内存处理,只需使用处理所有内部内容的Structure.toArray()。通过仅实现我向您展示的一行,您错过了write() 步骤。因此,您只是在 Java 端声明这些结构,而不是将它们全部写入本机端。
    • 当您决定使用Memory() 时,您自己承担读取/写入的责任。使用已经这样做的现有代码要好得多。
    【解决方案2】:

    我自己找到了解决方案:

    仅将 MarshalAs 参数添加到 Method 作品中。

    C#

    [DllExport]
    public static void setStructureArray([In, Out, 
        MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] Struct[] 
        structureArray, int length)
    {
        Console.WriteLine("Length: " + structureArray.Length);
        for (int i = 0; i < structureArray.Length; i++)
        {
            Console.WriteLine(structureArray[i].x);
            Console.WriteLine(structureArray[i].y);
            Console.WriteLine(structureArray[i].z);
            Console.WriteLine(structureArray[i].name);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-08-06
      • 1970-01-01
      • 1970-01-01
      • 2015-02-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多