假设我有一个带有原型的 C++ 函数
int someFunction(const float ** raws)
如何使用float[][]
C# 中的参数调用此函数?可能不使用不安全的代码。
假设我有一个带有原型的 C++ 函数
int someFunction(const float ** raws)
如何使用float[][]
C# 中的参数调用此函数?可能不使用不安全的代码。
据我所知,您必须自己完成大部分工作。Interop 将帮助您编组顶级数组,但您的工作将是固定所有嵌套数组,然后在完成后取消固定它们。此代码显示了一种方法:
using System;
using System.Runtime.InteropServices;
namespace ManagedClient
{
class Program
{
[DllImport("UnmanagedDll.dll", CallingConvention = CallingConvention.StdCall)]
private static extern int UseFloats([MarshalAs(UnmanagedType.LPArray)] IntPtr[] raws);
static void Main(string[] args)
{
float[][] data =
{
new[] { 0.0f, 0.1f, 0.2f, 0.3f, 0.4f },
new[] { 1.0f, 1.1f, 1.2f, 1.3f },
new[] { 2.0f },
new[] { 3.0f, 3.1f }
};
var handles = new GCHandle[data.Length];
var pointers = new IntPtr[data.Length];
try
{
for (int i = 0; i < data.Length; ++i)
{
var h = GCHandle.Alloc(data[i], GCHandleType.Pinned);
handles[i] = h;
pointers[i] = h.AddrOfPinnedObject();
}
UseFloats(pointers);
}
finally
{
for (int i = 0; i < handles.Length; ++i)
{
if (handles[i].IsAllocated)
{
handles[i].Free();
}
}
}
}
}
}
这将构建一个指针数组,每个指针都指向float
输入数组中的数据。(这是您的 C 函数期望数据到达的格式。)您的 C 代码如何知道每个子数组的长度取决于您。在我的测试代码中,我只是对其进行了硬编码以匹配传入的 C# 代码:
__declspec(dllexport) int __stdcall UseFloats(const float ** raws)
{
printf("%f %f %f %f %f\n", raws[0][0], raws[0][1], raws[0][2], raws[0][3], raws[0][4]);
printf("%f %f %f %f\n", raws[1][0], raws[1][1], raws[1][2], raws[1][3], raws[0][4]);
printf("%f\n", raws[2][0]);
printf("%f %f\n", raws[3][0], raws[3][1]);
return 0;
}
实际上,您可能想要做一些事情来告诉非托管代码每个子数组的长度 - 您选择的非托管函数签名不会让被调用的代码以任何方式知道每个子数组的长度。(大概您使用 float** 的原因是因为您想要锯齿状数组。如果不是,并且每个子数组的长度完全相同,那么在这里使用矩形数组而不是指针,它也会使编组更容易。)
Ian 已经回答了您的问题,我只想建议在 C++ 端使用 SAFEARRAY。
SAFEARRAY 是 C/C++ 中数组定义模糊问题的 COM 答案,它们是包含底层元素数量和大小的结构,更适合 .Net 数组。使用它们将允许从 C# 自动编组数组。
SAFEARRAY 在 C++ 中使用起来很痛苦,但 ATL 有一些很好的包装器可以让事情变得更容易。如果可能的话,更改 C++ 函数以使用它,或者考虑编写一个 C++ 包装器以与使用它们的 .Net 互操作,并使用 C++ 编写编组代码。