将数组从 c# (Mono) 编组到 c++
Marshal arrays from c# (Mono) to c++
我正在尝试(非常)有限地成功将数组传递给我创建的 dll。我在 C# 中工作,并在 C++ 中执行一些机器学习操作。这里的问题是我正在使用 Mono 而不是 .Net 框架(用于 Unity)。我关注了多个 "how-to" link,例如 this and this。
第一个 link 实际上非常有用,我可以将一个数组从 c# 传递到具有已知边界的 c++,而垃圾收集器不会消耗该数组。但是,单声道不支持 SAFEARRAY(仅在 .Net 中)
我的 C# 代码的相关部分是:
[DllImport("Classification.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void DebugStruct([In] IntPtr ptest_struct_with_arrays);
internal struct LabeledDataManaged
{
//[MarshalAs(UnmanagedType.ByValArray,SizeConst = 720)]
public float[] floatArray;
//[MarshalAs(UnmanagedType.ByValArray, SizeConst = 45)]
public uint[] uintArray;
}
static unsafe void TestMarshalMono(float[] testArr, uint[] labelsArr)
{
LabeledDataManaged labledData = new LabeledDataManaged();
labledData.floatArray = testArr;
labledData.uintArray = labelsArr;
int iSizeOfTestStructWithArrays = Marshal.SizeOf(labledData);
IntPtr pStruct = Marshal.AllocHGlobal(iSizeOfTestStructWithArrays);
Marshal.StructureToPtr(labledData, pStruct, false);
DebugStruct(pStruct );
}
我的问题:
调试 dll 时,我在 C++ 端看到垃圾。我也尝试过从 IntPtr 切换到 HandleRef,但我不明白在 C++ 端需要更改什么才能使其工作。顺便说一句,任何传递数组而不被 GC 弄乱的方式都可以,无论它是否包含在结构中。
我正在联系所有 Whosebug 天才来解决这个恼人的问题。
I define the same struct on the c++ side using std::vector<float>
and std::vector<unsigned int>
.
重要的细节,需要在问题中。但不,那永远行不通。 C++ 语言类型没有互操作故事,class 实现细节在编译器实现之间完全不同。或者就此而言,在同一个编译器中差异太大。像迭代器调试这样的标准辅助改变了 C++ 标准库对象的布局。
Pinvoke 互操作基于 C 语言类型。您的 C# 声明现在匹配
typedef struct LabeledData {
float* floatArray;
unsigned int* uintArray;
}
毫无疑问,您可以看出这个结构的大问题。没有任何合适的方法可以实际使用这些数组。你不知道它们有多大。您可以使用 UnmanagedType.ByValArray 但等效的 C++ 声明需要如下所示:
typedef struct LabeledData {
float floatArray[42];
unsigned int uintArray[666];
}
你肯定看到了这个结构的大问题,数组长度是固定的。当您考虑 std::vector<> 时,您肯定不喜欢固定数组长度。
嗯,这就是 .NET 喜欢 COM 互操作类型的原因。 SafeArray 是安全的,因为它还存储数组长度。并且有一个定义明确的方法来分配和销毁它们,这是原始数组的另一个大问题。没有他们,你能做的最好的事情是:
typedef struct LabeledData {
size_t floatArraySize;
float* floatArray;
size_t uintArraySize;
unsigned int* uintArray;
}
并在 C# 端匹配它:
internal struct LabelData {
public int floatArraySize;
public IntPtr floatArray;
public int uintArraySize;
public IntPtr uintArray;
}
您需要 Marshal.AllocHGlobal() 来分配数组,您已经知道该怎么做。并且担心谁以及何时调用 Marshal.FreeHGlobal()。由于您可能仍想保留 std::vector,因此您需要复制数组以便在调用 pinvoke 后释放内存。
当然,数组数据复制非常非常难看。通过完全不使用结构而是使用四个函数来获得成功。像 CreateLabeledData、DestroyLabeledData、SetLabeledDataFloats、SetLabeledDataIntegers。以及相应的 C++ class 和实现这些功能的工厂方法。
我正在尝试(非常)有限地成功将数组传递给我创建的 dll。我在 C# 中工作,并在 C++ 中执行一些机器学习操作。这里的问题是我正在使用 Mono 而不是 .Net 框架(用于 Unity)。我关注了多个 "how-to" link,例如 this and this。
第一个 link 实际上非常有用,我可以将一个数组从 c# 传递到具有已知边界的 c++,而垃圾收集器不会消耗该数组。但是,单声道不支持 SAFEARRAY(仅在 .Net 中)
我的 C# 代码的相关部分是:
[DllImport("Classification.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void DebugStruct([In] IntPtr ptest_struct_with_arrays);
internal struct LabeledDataManaged
{
//[MarshalAs(UnmanagedType.ByValArray,SizeConst = 720)]
public float[] floatArray;
//[MarshalAs(UnmanagedType.ByValArray, SizeConst = 45)]
public uint[] uintArray;
}
static unsafe void TestMarshalMono(float[] testArr, uint[] labelsArr)
{
LabeledDataManaged labledData = new LabeledDataManaged();
labledData.floatArray = testArr;
labledData.uintArray = labelsArr;
int iSizeOfTestStructWithArrays = Marshal.SizeOf(labledData);
IntPtr pStruct = Marshal.AllocHGlobal(iSizeOfTestStructWithArrays);
Marshal.StructureToPtr(labledData, pStruct, false);
DebugStruct(pStruct );
}
我的问题: 调试 dll 时,我在 C++ 端看到垃圾。我也尝试过从 IntPtr 切换到 HandleRef,但我不明白在 C++ 端需要更改什么才能使其工作。顺便说一句,任何传递数组而不被 GC 弄乱的方式都可以,无论它是否包含在结构中。
我正在联系所有 Whosebug 天才来解决这个恼人的问题。
I define the same struct on the c++ side using
std::vector<float>
andstd::vector<unsigned int>
.
重要的细节,需要在问题中。但不,那永远行不通。 C++ 语言类型没有互操作故事,class 实现细节在编译器实现之间完全不同。或者就此而言,在同一个编译器中差异太大。像迭代器调试这样的标准辅助改变了 C++ 标准库对象的布局。
Pinvoke 互操作基于 C 语言类型。您的 C# 声明现在匹配
typedef struct LabeledData {
float* floatArray;
unsigned int* uintArray;
}
毫无疑问,您可以看出这个结构的大问题。没有任何合适的方法可以实际使用这些数组。你不知道它们有多大。您可以使用 UnmanagedType.ByValArray 但等效的 C++ 声明需要如下所示:
typedef struct LabeledData {
float floatArray[42];
unsigned int uintArray[666];
}
你肯定看到了这个结构的大问题,数组长度是固定的。当您考虑 std::vector<> 时,您肯定不喜欢固定数组长度。
嗯,这就是 .NET 喜欢 COM 互操作类型的原因。 SafeArray 是安全的,因为它还存储数组长度。并且有一个定义明确的方法来分配和销毁它们,这是原始数组的另一个大问题。没有他们,你能做的最好的事情是:
typedef struct LabeledData {
size_t floatArraySize;
float* floatArray;
size_t uintArraySize;
unsigned int* uintArray;
}
并在 C# 端匹配它:
internal struct LabelData {
public int floatArraySize;
public IntPtr floatArray;
public int uintArraySize;
public IntPtr uintArray;
}
您需要 Marshal.AllocHGlobal() 来分配数组,您已经知道该怎么做。并且担心谁以及何时调用 Marshal.FreeHGlobal()。由于您可能仍想保留 std::vector,因此您需要复制数组以便在调用 pinvoke 后释放内存。
当然,数组数据复制非常非常难看。通过完全不使用结构而是使用四个函数来获得成功。像 CreateLabeledData、DestroyLabeledData、SetLabeledDataFloats、SetLabeledDataIntegers。以及相应的 C++ class 和实现这些功能的工厂方法。