【发布时间】:2011-12-25 00:13:45
【问题描述】:
我需要使用 PInvoke 将我拥有的点列表从 C dll 返回到 C# 应用程序。这些是 3 维 [x,y,z] 中的点。点数因型号而异。在 C 中,我处理这个结构的链接列表。但我不知道如何将其传递给 C#。
在我看来,我必须返回一个灵活的二维数组,可能在一个结构中。
对如何做到这一点有任何建议吗?关于如何在 C 中返回它以及如何在 C# 中访问它的两个想法都受到高度赞赏。
【问题讨论】:
我需要使用 PInvoke 将我拥有的点列表从 C dll 返回到 C# 应用程序。这些是 3 维 [x,y,z] 中的点。点数因型号而异。在 C 中,我处理这个结构的链接列表。但我不知道如何将其传递给 C#。
在我看来,我必须返回一个灵活的二维数组,可能在一个结构中。
对如何做到这一点有任何建议吗?关于如何在 C 中返回它以及如何在 C# 中访问它的两个想法都受到高度赞赏。
【问题讨论】:
结构的链接列表可以传回,但处理起来会很麻烦,因为您必须编写代码来循环指针,读取和复制数据本机内存到托管内存空间。我会推荐一个简单的结构数组。
如果你有一个像下面这样的 C 结构(假设是 32 位整数)...
struct Point
{
int x;
int y;
int z;
}
...那么您将在 C# 中以几乎相同的方式表示它:
[StructLayout(LayoutKind.Sequential]
struct Point
{
public int x;
public int y;
public int z;
}
现在要传回一个数组,最简单的方法是让您的本机代码分配该数组并将其作为一个指针传回,以及另一个指定元素大小的指针。
您的 C 原型可能如下所示:
// Return value would represent an error code
// (in case something goes wrong or the caller
// passes some invalid pointer, e.g. a NULL).
// Caller must pass in a valid pointer-to-pointer to
// capture the array and a pointer to capture the size
// in elements.
int GetPoints(Point ** array, int * arraySizeInElements);
P/Invoke 声明将是这样的:
[DllImport("YourLib.dll")]
static extern int GetPoints(
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] out Point[] array,
out int arraySizeInElements);
MarshalAs 属性指定应使用第二个参数中指定的大小对数组进行封送处理(您可以在 MSDN,"Default Marshaling for Arrays" 上阅读更多相关信息)。
如果您使用这种方法,请注意您必须使用CoTaskMemAlloc 分配本机缓冲区,因为这是 .NET 封送处理程序所期望的。否则,您的应用程序会出现内存泄漏和/或其他错误。
这是我在验证答案时编译的简单示例的 sn-p:
struct Point
{
int x;
int y;
int z;
};
extern "C"
int GetPoints(Point ** array, int * arraySizeInElements)
{
// Always return 3 items for this simple example.
*arraySizeInElements = 3;
// MUST use CoTaskMemAlloc to allocate (from ole32.dll)
int bytesToAlloc = sizeof(Point) * (*arraySizeInElements);
Point * a = static_cast<Point *>(CoTaskMemAlloc(bytesToAlloc));
*array = a;
Point p1 = { 1, 2, 3 };
a[0] = p1;
Point p2 = { 4, 5, 6 };
a[1] = p2;
Point p3 = { 7, 8, 9 };
a[2] = p3;
return 0;
}
然后托管调用者可以非常简单地处理数据(在本示例中,我将所有互操作代码放在名为 NativeMethods 的静态类中):
NativeMethods.Point[] points;
int size;
int result = NativeMethods.GetPoints(out points, out size);
if (result == 0)
{
Console.WriteLine("{0} points returned.", size);
foreach (NativeMethods.Point point in points)
{
Console.WriteLine("({0}, {1}, {2})", point.x, point.y, point.z);
}
}
【讨论】: