使用 JNA 和 DLLExport 将结构数组从 Java 传输到 C#
Transmit Structure Array from Java to C# using JNA and DLLExport
我正在尝试使用 JNA 从 C# to/from 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 界面
public 接口 IJNA 扩展库 {
IJNA 实例 = (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 参数添加到方法中。
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 中,数组是“扁平”的,由单个连续内存块组成。
您已在 Java 端定义数组,如下所示:
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]
元素,但不同之处在于其他元素实际上位于本机端期望它们所在的位置,在第一个结构的偏移量处尺寸。
我自己找到了解决方案:
仅将 MarshalAs 参数添加到方法中。
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);
}
}
我正在尝试使用 JNA 从 C# to/from 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 界面
public 接口 IJNA 扩展库 { IJNA 实例 = (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 参数添加到方法中。
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 中,数组是“扁平”的,由单个连续内存块组成。
您已在 Java 端定义数组,如下所示:
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]
元素,但不同之处在于其他元素实际上位于本机端期望它们所在的位置,在第一个结构的偏移量处尺寸。
我自己找到了解决方案:
仅将 MarshalAs 参数添加到方法中。
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);
}
}