当前位置: 首页 > 知识库问答 >
问题:

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

张岳
2023-03-14

我正试图用JNA从C向Java发送和接收结构数组。在Java中,从C#接收可以很好地工作,但传输到C#只会给出一行。我认为问题是“pointerByReference.setPointer(数组[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扩展Library{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 Main

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

共有2个答案

贺季
2023-03-14

我自己找到了解决方案

只有将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);
    }
}
郎弘壮
2023-03-14

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

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

 类似资料:
  • 问题内容: 我在C 中有一个方法,该方法将双精度数组作为参数。我从Java调用此方法,需要传递一个双精度数组。C 例程读取和修改数组的值,而我需要Java中的那些更新后的值。我该怎么做呢? 例如,使用C ++例程: 和Java代码: 我猜不能像上面的调用那样对myMethod进行调用…还是可以吗?而在Swig中进行这项工作所需的是什么。如果我无法进行上述调用,如何将我的值获取到C ++代码? 问题

  • 我目前正在做一个项目,需要开发一个原生DLL(用C++)来访问Java应用程序。我选择了JNA来做桥接工作,我面临着从Java向C++函数传递正确的int值的问题。 简单地说,我有一个函数在C++中接受一个int值作为参数:(代码被剥离,方法被重命名以保持机密性) 是一种简单的方法,它使用将任何数据类型的值转换为。执行情况如下: 是从我在代码中使用的实际结构重命名的。是的数组。和都是共享内存中的全

  • 问题内容: 我有一个字符串数组一样,我想这个数组发送到 ç 使用 JNI 。我找不到任何明确的解决方案。我试图将此字符串视为成功,但没有成功。 有没有办法做到这一点? 问题答案: 您可以编写一个简单的函数,该函数接受一个对象,将每个对象强制转换为jstring,然后对其进行调用。 像这样:

  • 装饰器定义了一组可以从父组件传递的参数。例如,我们可以修改HelloComponent组件,以便可以由父提供。 我们现在可以使用我们的组件:

  • 问题内容: C部分: 开始部分: 我可以通过这种方式获取数组的第一个元素。但是如何获得带有n个元素的整个数组呢?以及如何安全地释放它们? 问题答案: 首先,即使您正在使用Go,添加cgo时也不再存在任何“安全”。由您决定何时以及如何释放内存,就像使用C进行编程一样。 在go中使用C数组的最简单方法是通过数组将其转换为切片: 最大大小的数组实际上并未分配,但是Go需要恒定大小的数组,并且要足够大。该

  • 我编写了以下类来包装win32事件对象的创建 我在Windows7机器上使用JNA3.3,当我试图创建这个类的实例时,我得到了以下堆栈跟踪。 线程“main”java.lang.UnsatifiedLinkError中出现异常:查找函数“CreateEvent”时出错:找不到指定的过程。