繁体   English   中英

使用 JNA 和 DLLExport 将结构体数组从 Java 传输到 C#

[英]Transmit Structure Array from Java to C# using JNA and DLLExport

我正在尝试使用 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);
    }
}

Output:

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

Output:

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

在 C 中,arrays 是“扁平”的,由一个连续的 memory 块组成。

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

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

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

您需要在 Java 端使用Structure.toArray()来分配连续的 memory:

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

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

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

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

我自己找到了解决方案:

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM