将浮点数组编组到 C#
Marshalling float Array to c#
我正在尝试将包含浮点数组的结构从 C++ DLL 编组到 C#。
我从以下代码创建了 C++ DLL:
//MarshalTest.h
namespace mTest{
typedef struct {
float data[3];
int otherStuff;
} dataStruct;
extern "C" __declspec(dllexport) dataStruct getData();
}
//MarshalTest.cpp
#include "MarshallTest.h"
using namespace std;
namespace mTest{
dataStruct getData(){
dataStruct d = {{ 16, 2, 77 }, 5};
return d;
}
}
我使用以下代码使 getData-Function 在 C# 中可用:
public unsafe struct dataStruct{
public fixed byte data[3];
public int otherStuff;
public unsafe float[] Data{
get{
fixed (byte* ptr = data){
IntPtr ptr2 = (IntPtr)ptr;
float[] array = new float[3];
Marshal.Copy(ptr2, array, 0, 3);
return array;
}
}
set{
fixed (byte* ptr = data){
//not needed
}
}
}
}
[DllImport("MarshallTest", CallingConvention = CallingConvention.Cdecl)]
private static extern dataStruct getData ();
在 C# 中打印数据[]时,我得到以下输出:
1.175494E-38
1.610935E-32
8.255635E-20
我做错了什么?
我不确定你为什么要做所有不安全的事情和指针操作。如果你正确地定义了你的结构,它非常简单:
[StructLayout(LayoutKind.Sequential)]
public struct dataStruct
{
[MarshalAs(UnmanagedType.LPArray, SizeConst = 3)]
public float[] data;
public int otherStuff;
}
您只需使用有关您的类型的一些附加信息来指示互操作编组,然后您就可以在不使用不安全构造和指针的情况下获取非托管数据。在 C# 中使用指针的理由非常非常少。
你的可能不起作用,因为你使用 byte[]
数组作为第一个参数,所以当从非托管端复制数据时,你会覆盖大量数据(也许你认为使用 fixed
把它们变成指针?)。类型必须匹配,以便结构匹配并正确复制数据。此外,由于 C# 会自动重新安排结构以最适合内存,因此您可能不会按照您的想法放置数据。这就是上面示例顶部的 StructLayout
所做的,它告诉编译器按顺序保持结构的定义。
您必须使用正确的类型:
public unsafe struct dataStruct2
{
public fixed float data[3];
public int otherStuff;
public unsafe float[] Data
{
get
{
fixed (float* ptr = data)
{
float[] array = new float[3];
Marshal.Copy((IntPtr)ptr, array, 0, 3);
return array;
}
}
}
}
请注意,对于小数组,您甚至可以使用:
public struct dataStruct
{
public float data1;
public float data2;
public float data3;
public int otherStuff;
public float[] Data
{
get
{
return new[] { data1, data2, data3 };
}
}
}
不使用不安全代码。
我正在尝试将包含浮点数组的结构从 C++ DLL 编组到 C#。
我从以下代码创建了 C++ DLL:
//MarshalTest.h
namespace mTest{
typedef struct {
float data[3];
int otherStuff;
} dataStruct;
extern "C" __declspec(dllexport) dataStruct getData();
}
//MarshalTest.cpp
#include "MarshallTest.h"
using namespace std;
namespace mTest{
dataStruct getData(){
dataStruct d = {{ 16, 2, 77 }, 5};
return d;
}
}
我使用以下代码使 getData-Function 在 C# 中可用:
public unsafe struct dataStruct{
public fixed byte data[3];
public int otherStuff;
public unsafe float[] Data{
get{
fixed (byte* ptr = data){
IntPtr ptr2 = (IntPtr)ptr;
float[] array = new float[3];
Marshal.Copy(ptr2, array, 0, 3);
return array;
}
}
set{
fixed (byte* ptr = data){
//not needed
}
}
}
}
[DllImport("MarshallTest", CallingConvention = CallingConvention.Cdecl)]
private static extern dataStruct getData ();
在 C# 中打印数据[]时,我得到以下输出: 1.175494E-38 1.610935E-32 8.255635E-20
我做错了什么?
我不确定你为什么要做所有不安全的事情和指针操作。如果你正确地定义了你的结构,它非常简单:
[StructLayout(LayoutKind.Sequential)]
public struct dataStruct
{
[MarshalAs(UnmanagedType.LPArray, SizeConst = 3)]
public float[] data;
public int otherStuff;
}
您只需使用有关您的类型的一些附加信息来指示互操作编组,然后您就可以在不使用不安全构造和指针的情况下获取非托管数据。在 C# 中使用指针的理由非常非常少。
你的可能不起作用,因为你使用 byte[]
数组作为第一个参数,所以当从非托管端复制数据时,你会覆盖大量数据(也许你认为使用 fixed
把它们变成指针?)。类型必须匹配,以便结构匹配并正确复制数据。此外,由于 C# 会自动重新安排结构以最适合内存,因此您可能不会按照您的想法放置数据。这就是上面示例顶部的 StructLayout
所做的,它告诉编译器按顺序保持结构的定义。
您必须使用正确的类型:
public unsafe struct dataStruct2
{
public fixed float data[3];
public int otherStuff;
public unsafe float[] Data
{
get
{
fixed (float* ptr = data)
{
float[] array = new float[3];
Marshal.Copy((IntPtr)ptr, array, 0, 3);
return array;
}
}
}
}
请注意,对于小数组,您甚至可以使用:
public struct dataStruct
{
public float data1;
public float data2;
public float data3;
public int otherStuff;
public float[] Data
{
get
{
return new[] { data1, data2, data3 };
}
}
}
不使用不安全代码。