c# - 如何从 C# 调用设置 int 数组值的 C++ 函数?

标签 c# c++ .net

我在 .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();

最佳答案

您有两个选择。一种是依赖 .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 类型,编码器实际上作弊并将指针传递给您的实际数组。或者至少这是记录在案的行为。

如果这对您来说是一个性能问题(并且这是一个真实的、可衡量的问题 - 否则不要这样做),您可以使用不安全代码直接将指针传递给 .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,但应明确指定契约。

The main difference is that in the first case, the C(++) code never has access to any of the managed memory of yours - it always works on copies of the data. By adding the [In, Out] attributes, you're telling the marshaller to not only copy the data in, but also to copy it out after the call is done - otherwise your table array would never change.

On the other hand, the second variant passes a pointer to the actual array you're using on the C# side - this may or may not be faster, depending on way too many things, and it's generally slightly more unsafe (be very careful about specifying the correct length of the array, for example).

另一个编辑:

还有一个选项可以在 P/Invoke 方法声明中使用 IntPtr 表,这让您更加灵活。最简单的例子就是固定数组,这等同于 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 代码一样“不安全” .事实是,由于您正在与 native 代码交互,所以您总是(有点)不安全 - native 代码中的错误很容易破坏您的整个应用程序。

关于c# - 如何从 C# 调用设置 int 数组值的 C++ 函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30754481/

相关文章:

c# - OrderedDictionary 和正则表达式

c++ - 这段代码是如何工作的?

c++ - 是否有 CoreBluetooth for OSX 的替代品

c++ - Rcpp : Magick++ 中的编译器和链接器标志

.net - 为什么 DataContractSerializer 绕过初始值设定项?

c# - 当用户使用数据时,在 SQL 中锁定行的正确方法是什么?

c# - 适用于特定环境(非部署)的 Windows Azure WADPerformanceCounters

c# - 求csv文件中每一列的最大长度

c# - IronPython 有时导入模块但不导入其他模块

c# - List<T>.AddRange 实现次优