英文:
Marshal.StructureToPtr Java equivalent in JNA
问题
c# code:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct RSSlapInfo
{
public int fingerType;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public RSPoint[] fingerPosition;
public int imageQuality;
public int rotation;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public int[] reserved;
};
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct RSSlapInfoArray
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public RSSlapInfo[] RSSlapInfoA;
};
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct RSPoint
{
public int x;
public int y;
};
Java code:
@Structure.FieldOrder({"fingerType", "fingerPosition", "imageQuality", "rotation", "reserved"})
public static class RSSlapInfo extends Structure
{
public RSSlapInfo() {}
public int fingerType;
public RSPoint[] fingerPosition = new RSPoint[4];
public int imageQuality;
public int rotation;
public int[] reserved = new int[3];
}
@Structure.FieldOrder({"RSSlapInfoA"})
public static class RSSlapInfoArray extends Structure implements Structure.ByValue
{
public RSSlapInfoArray() {
}
public RSSlapInfo RSSlapInfoA[] = new RSSlapInfo[4];
}
@Structure.FieldOrder({"x", "y"})
public static class RSPoint extends Structure
{
public RSPoint(){
}
public int x;
public int y;
}
You are facing difficulty in converting the C# code snippet to Java, particularly the part where you initialize and convert the slapInfoArray
before making a native call.
英文:
I am working on a project where the only lib I have to communicate to is a .dll file and the resource I have is a C# working project with source code; I am using JNA to access native codes and I currently run into Invalid memory access when I call the method;
c# code:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct RSSlapInfo
{
public int fingerType;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public RSPoint[] fingerPosition;
public int imageQuality;
public int rotation;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public int[] reserved;
};
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct RSSlapInfoArray
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public RSSlapInfo[] RSSlapInfoA;
};
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct RSPoint
{
public int x;
public int y;
};
RSSlapInfoArray slapInfoA = new RSSlapInfoArray();
IntPtr slapInfoArray;
int _size = Marshal.SizeOf(typeof(RSSlapInfoArray));
slapInfoArray = Marshal.AllocHGlobal(_size);
Marshal.StructureToPtr(slapInfoA, slapInfoArray, true);
On debug mode, I notice slapInfoArray
value changes after the above call
My Java code:
public static class RSSlapInfo extends Structure
{
public RSSlapInfo(){}
public int fingerType;
public RSPoint[] fingerPosition = new RSPoint[4];
public int imageQuality;
public int rotation;
public int[] reserved =new int[3];
}
@Structure.FieldOrder({"RSSlapInfoA"})
public static class RSSlapInfoArray extends Structure implements Structure.ByValue
{
public RSSlapInfoArray() {
}
public RSSlapInfo RSSlapInfoA[] = new RSSlapInfo[4];
}
@Structure.FieldOrder({"x","y"})
public static class RSPoint extends Structure
{
public RSPoint(){
}
public int x;
public int y;
}
Currently stuck on the right way to convert this C# part of the code before I make the native call with slapInfoArray
RSSlapInfoArray slapInfoA = new RSSlapInfoArray();
IntPtr slapInfoArray;
int _size = Marshal.SizeOf(typeof(RSSlapInfoArray));
slapInfoArray = Marshal.AllocHGlobal(_size);
Marshal.StructureToPtr(slapInfoA, slapInfoArray, true);
答案1
得分: 1
Marshal.StructureToPtr()
的文档为你提供了关于你需要做的强烈提示:
从托管对象封送数据到不受管控的内存块。
structure
一个保存要封送数据的托管对象。此对象必须是结构体或格式化类的实例。
ptr
指向不受管控的内存块的指针,在调用该方法之前必须对此进行分配。
你已经有了结构参数,但你需要为第二个参数分配所需的内存块。这可以通过 JNA 的 Memory
类来完成。
更复杂的是,你需要传递的实际上是包含其他结构体数组的结构体,并且必须在连续的内存中分配。你在 RSSlapInfoArray
包装中所做的方式实际上根本没有分配任何内存;它只是在 Java 端声明了一个具有空指针的数组,这些空指针将传递到本机端。
你需要做的第一件事是将你的 RSSlapInfoArray
映射更正为连续分配结构体数组。将
public RSSlapInfo RSSlapInfoA[] = new RSSlapInfo[4]; // 不分配任何内存
更改为
public RSSlapInfo[] RSSlapInfoA = (RSSlapInfo[]) new RSSlapInfo().toArray(4); // 连续分配
你应该对 RSPoint
数组执行类似的操作:
public RSPoint[] fingerPosition = new RSPoint().toArray(4);
关于你的结构体映射的另一个注意事项:你已经覆盖了 Structure
类的构造函数,但没有实现它。你应该删除你不需要的构造函数,或者在构造函数内部调用 super()
。
假设你已经在加载 DLL 的接口中映射了 StructureToPtr
,类似于这样:
void StructureToPtr(Structure structure, Pointer ptr, boolean fDeleteOld);
那么你问题中最后5行的 JNA 代码应该是:
// 创建带有内联数组的托管结构体
RSSlapInfoArray slapInfoA = new RSSlapInfoArray();
// 为它分配一个内存块
Memory slapInfoArray = new Memory(slapInfoA.size());
// 调用 StructureToPtr
YourInterface.INSTANCE.StructureToPtr(slapInfoA, slapInfoArray, true);
英文:
The documentation for Marshal.StructureToPtr()
gives you a strong hint about what you need to do:
> Marshals data from a managed object to an unmanaged block of memory.
>
> structure
A managed object that holds the data to be marshaled. This object must be a structure or an instance of a formatted class.
>
> ptr
A pointer to an unmanaged block of memory, which must be allocated before this method is called.
You have the structure parameter but you need to allocate the block of memory required for the second parameter. This can be done with JNA's Memory
class.
Complicating this, the structure you need to pass actually holds an array of other structures, and must be allocated in contiguous memory. The way you've done that in your RSSlapInfoArray
wrapping doesn't actually allocate any memory at all; it just declares a java-side array with null pointers that would be passed to the native side.
The first thing you need to do is correct your RSSlapInfoArray
mapping to allocate the structure array contiguously. Change
public RSSlapInfo RSSlapInfoA[] = new RSSlapInfo[4]; // does not allocate anything
to
public RSSlapInfo[] RSSlapInfoA = (RSSlapInfo[]) new RSSlapInfo().toArray(4); // contiguous allocation
You should do similarly for the RSPoint
array:
public RSPoint[] fingerPosition = new RSPoint().toArray(4);
Another note on your structure mappings: you have overridden the Structure
class constructor without implementing it. You should either delete your (unneeded) constructors or call super()
inside the constructors.
Assuming you've mapped StructureToPtr in an interface loading the DLL something similar this:
void StructureToPtr(Structure structure, Pointer ptr, boolean fDeleteOld);
Then the JNA code for your last 5 lines in your question should be
// Create the managed structure with the array inline
RSSlapInfoArray slapInfoA = new RSSlapInfoArray();
// Allocate a block of memory for it
Memory slapInfoArray = new Memory(slapInfoA.size());
// Call StructureToPtr
YourInterface.INSTANCE.StructureToPtr(slapInfoA, slapInfoArray, true);
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论