【问题标题】:How to get array from C++/C to C# using DLL如何使用 DLL 从 C++/C 获取数组到 C#
【发布时间】:2021-12-24 19:29:35
【问题描述】:

我对 C# 和 C/C++ 非常陌生。我用 C 编写了一个代码,将 2 个变量存储在一个数组中并返回该数组。使用 P/invoke 方法,我创建了这个 C 函数的 DLL 库并将该库导入 C#。在调用数组时,我无法从 C# 中获取数组。 需要你的帮助。这是C代码

#include <stdio.h>

extern __declspec(dllexport) int* sum();
int* sum()
{
    int a = 50, b = 80, sum, neg;
    sum = a + b;
    neg = b - a;
    int arr[2] = { sum, neg };
    return arr;
}

现在 C# 代码是...

using System;
using System.Runtime.InteropServices;

namespace ConsoleApplication
{
    class Program
    {
        [DllImport("Project2.dll")]
        unsafe public static extern int* sum();
        static void Main(string[] args)
        {
            unsafe
            {
                int* array2 = sum();

                Console.WriteLine("Value: " + array2[0]);
            }
            
        }
    }
}

请告诉我如何获取数组。

【问题讨论】:

标签: c# dll pinvoke marshalling dllimport


【解决方案1】:

返回数组有多种方式,但在所有情况下都必须解决两个问题:

  • 能够告诉 .NET 数组中的项目数;
  • 对本机和 .NET 大小使用通用内存分配器。选择的分配器是 COM 分配器,因为它是 known to .NET;

以下是使用 .NET 安全代码的两个示例:

本机代码 1:

extern __declspec(dllexport) SAFEARRAY * sumSafeArray()
{
    int a = 50, b = 80, sum, neg;
    sum = a + b;
    neg = b - a;
    int arr[2] = { sum, neg };

    // SAFEARRAY is a OLE/COM automation type well known to Windows and .NET
    // (note it implicitly uses the COM allocator too)
    SAFEARRAY* psa = SafeArrayCreateVector(VT_I4, 0, 2);
    void* data;
    SafeArrayAccessData(psa, &data);
    CopyMemory(data, arr, sizeof(arr));
    SafeArrayUnaccessData(psa);
    return psa;
}

托管代码 1:

[DllImport("mydll")]
[return: MarshalAs(UnmanagedType.SafeArray)]
private static extern int[] sumSafeArray();

foreach (var i in sumSafeArray())
{
    Console.WriteLine(i);
}

本机代码 2:

extern __declspec(dllexport) void sumArray(int** out, int* count)
{
    int a = 50, b = 80, sum, neg;
    sum = a + b;
    neg = b - a;
    int arr[2] = { sum, neg };

    // always use COM allocator
    *out = (int*)CoTaskMemAlloc(sizeof(arr));
    CopyMemory(*out, arr, sizeof(arr));
    *count = sizeof(arr) / sizeof(int);
}

托管代码 2:

[DllImport("mydll")]
private static extern void sumArray(out IntPtr array, out int size);

sumArray(out var ptr, out var size);
var ints = new int[size];
Marshal.Copy(ptr, ints, 0, size);
Marshal.FreeCoTaskMem(ptr);
foreach (var i in ints)
{
    Console.WriteLine(i);
}

还有一个使用 .NET 不安全代码的示例:

本机代码:

extern __declspec(dllexport) int* sum(int* count)
{
    int a = 50, b = 80, sum, neg;
    sum = a + b;
    neg = b - a;
    int arr[2] = { sum, neg };

    // always use COM allocator
    int* out = (int*)CoTaskMemAlloc(sizeof(arr));
    CopyMemory(out, arr, sizeof(arr));
    *count = sizeof(arr) / sizeof(int);
    return out;
}

托管代码:

[DllImport("mydll")]
private unsafe static extern int* sum(out int count);

var ints = sum(out var count);
for (var i = 0; i < count; i++)
{
    Console.WriteLine(*ints);
    ints++;
}

您也可以使用另一个分配器(new/malloc/custom 等),但在这种情况下,您需要向 .NET 公开/导出相应的“free”方法,并在需要时自行释放。

【讨论】:

  • 很好的答案,但您缺少finallys 的Marshal.FreeCoTaskMem
  • @Charlieface - 我没有遗漏任何东西,代码没有处理任何可能的错误。
  • catch 块不一定需要处理任何错误,调用代码可以做到这一点。但是您必须能够安全地退出代码而不会发生内存泄漏,为此您需要一个finally 块。这相当于using 块:没有错误处理,只是确保正确处理事情。或者换一种说法:如果你不关心确保安全的内存释放,为什么要关心你使用哪个分配器?只需从任何地方分配并让它泄漏
  • @Simon Mourier 很好的答案,但我使用 C 语言编写本机代码。每当我将您的代码放在 C 语言中时,它都会向我显示此错误。 “ALT 需要 C++ 编译(使用 .CPP 后缀)atlbase.h”但我不能使用 C++ 后缀,因为我的函数是用 C 语言编写的,而且我对 C++/C 编程非常陌生。
  • @sulemanAlikazmi - 我的代码(请注意,我在刚刚删除的答案中留下了一个外部“C”)不使用任何 C++,也不使用 ATL。它只需要“windows.h”来编译。看起来您的代码正在使用 ATL,但我不能说。
猜你喜欢
  • 2021-04-12
  • 2018-11-07
  • 2019-10-16
  • 1970-01-01
  • 2018-02-24
  • 1970-01-01
  • 1970-01-01
  • 2016-08-20
  • 1970-01-01
相关资源
最近更新 更多