我在.dll中的C ++中有以下函数:
extern "C"
{
__declspec(dllexport) void ColorRamps_getColorRampTable(int n, int *table);
}
它接收一个包含n个元素的数组并设置数组值。
如何从C#中调用此内容?
我明白我必须首先DllImport函数(?):
[DllImport("ColorRamps.dll")]
static extern void ColorRamps_getColorRampTable(int n, int[] table);
public static void getColorRampTable(int[] table)
{
int n = table.Length;
ColorRamps_getColorRampTable(n, table);
}
这是对的吗?
当我调用getColorRampTable(int [] table)时,我必须固定数组吗? 我该怎么做?
我试过了:
int[] table = new int[164];
GCHandle handle = GCHandle.Alloc(table, GCHandleType.Pinned);
getColorRampTable(table);
handle.Free();
答案 0 :(得分:2)
您有两种选择。一种是依赖.NET编组,另一种是使用不安全的代码。
对你的情况来说,两者都非常简单。编组人员:
[DllImport("ColorRamps.dll")]
static extern void ColorRamps_getColorRampTable(int n, [In, Out] int[] table);
public static void getColorRampTable(int[] table)
{
int n = table.Length;
ColorRamps_getColorRampTable(n, table);
}
这有一点点开销,因为编组器首先将阵列复制到非托管内存,然后再将其复制回阵列。 编辑:正如Xanatos正确指出的那样,因为int[]
是一个blittable类型,marshaller实际上作弊并将指针传递给你的实际数组。或者至少是记录在案的行为。
如果这对您来说是一个性能问题(并且它是一个真实的,经过测量的问题 - 除非这样做),您可以使用不安全的代码直接将指针传递给.NET数组:
[DllImport("ColorRamps.dll")]
static extern void ColorRamps_getColorRampTable(int n, int* table);
public unsafe static void getColorRampTable(int[] table)
{
int n = table.Length;
fixed (int* pTable = &table)
{
ColorRamps_getColorRampTable(n, pTable);
}
}
该类必须使用unsafe
关键字进行标记,您必须在项目设置中启用不安全的代码。
编辑:
如上所述,这并不适用于int[]
案例。我会把它留在这里,因为对于非blittable类型它是正确的,但int[]
是 blittable,并且编组器应该直接传递指针,同时为你处理钉扎。尽管如此,您仍应使用[In, Out]
属性 - 即使默认情况下它通常表现为In / Out,也应明确指定合同。
主要区别在于,在第一种情况下,C(++)代码永远不能访问您的任何托管内存 - 它始终适用于数据的副本。通过添加
[In, Out]
属性,您可以告诉编组人员不仅要复制数据,还要在调用完成后将其复制出来 - 否则您的table
数组将永远不会更改。另一方面,第二个变体传递指向您在C#端使用的实际数组的指针 - 这可能会也可能不会更快,这取决于太多的东西,而且它是通常稍微不安全(例如,在指定正确的数组长度时要非常小心)。
另一个编辑:
还可以选择在P / Invoke方法声明中使用IntPtr table
,这样可以提供额外的灵活性。最简单的例子就是固定数组,这相当于fixed
情况:
var hTable = GCHandle.Alloc(table, GCHandleType.Pinned);
try
{
ColorRamps_getColorRampTable(n, hTable.AddrOfPinnedObject());
}
finally
{
hTable.Free();
}
甚至允许您非常轻松地明确管理复制:
var pTable = Marshal.AllocHGlobal(sizeof(int) * table.Length);
try
{
Marshal.Copy(table, 0, pTable, table.Length);
ColorRamps_getColorRampTable(n, pTable);
Marshal.Copy(pTable, table, 0, table.Length);
}
finally
{
Marshal.FreeHGlobal(pTable);
}
如果您出于某种原因无法使用unsafe
代码,这是一个不错的解决方法,但请记住它只是"不安全"在大多数情况下为unsafe
代码。事实是,既然您正在与本机代码交互,那么总是(有些)不安全 - 本机代码中的错误很容易破坏整个应用程序。