c# - 使用数组进行非托管导出

标签 c# unmanaged loadlibrary

我正在使用 RGiesecke DLLExport 库来生成可以从 Delphi 动态加载的 C# DLL。我有一个类似的方法:

[DllExport("GetVals", CallingConvention = System.Runtime.InteropServices.CallingConvention.StdCall)]
  static void GetVals([In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] int[] valueList, int len)
  {
      valueList = new int[3];

      int[] arrList = new int[] { 1, 2, 3 };
      int idx = 0;

      foreach (int s in arrList)
      {
          valueList[idx] = s;
          idx++;              
      }
  }

我希望能够从此调用返回一个数组,问题是我不会提前知道数组的大小,这只能在运行时确定。

为了测试我已经完成了以下操作(也在 C# 中)

IntPtr hLibrary = NativeWinAPI.LoadLibrary(DLLFileName);
                IntPtr pointerToFunction1 = NativeWinAPI.GetProcAddress(hLibrary, "GetVals");
                if (pointerToFunction1 != IntPtr.Zero)
                {
                    GetVals getfunction = (GetVals)Marshal.GetDelegateForFunctionPointer(pointerToFunction, typeof(GetVals));
                    int[] valList= null;
                    int fCnt = 3;
                    getfunction(valList, fCnt);
                    if (valList != null)
                    {

                    }
                }

出现错误“尝试读取或写入 protected 内存”,这是可以理解的,因为我没有在调用者上分配内存。在实际使用中,我不知道要返回的数组的大小,因此无法预分配内存。为了将最基本的内容放在那里,我尝试简单地从 GetVals 返回一个未知大小的数组。

最佳答案

您必须以不受 GC 影响的方式分配数组。 您可以使用 Marshal.AllocHGlobal 来做到这一点:

[DllExport]
static void GetVals(out IntPtr unmanagedArray, out int length)
{
    var valueList = new[]
    {
        1, 2, 3
    };

    length = valueList.Length;

    unmanagedArray = Marshal.AllocHGlobal(valueList.Length * Marshal.SizeOf(typeof(int)));
    Marshal.Copy(valueList, 0, unmanagedArray, length);
}

在 Delphi 方面,您将获得指向第一个元素的指针及其大小。 要读取它,您可以将指针 arraySize-1 增加一次并将其放入列表或 Delphi 管理的数组中:

uses
  SysUtils,
  Windows;

  procedure  GetVals(out unmanagedArray : PInteger; out arraySize : Integer);
    stdcall;
    external 'YourCSharpLib';

  function GetValsAsArray : TArray<integer>;
  var
    unmanagedArray, currentLocation : PInteger;
    arraySize, index : Integer;
  begin
    GetVals(unmanagedArray, arraySize);
    try
      SetLength(result, arraySize);
      if arraySize = 0 then
        exit;

      currentLocation := unmanagedArray;

      for index := 0 to arraySize - 1 do
      begin
        result[index] := currentLocation^;
        inc(currentLocation);
      end;
    finally
      LocalFree(Cardinal(unmanagedArray));
    end;
  end;

var
  valuesFromCSharp : TArray<integer>;
  index : Integer;
begin
  valuesFromCSharp := GetValsAsArray();

  for index := low(valuesFromCSharp) to high(valuesFromCSharp) do
    Writeln(valuesFromCSharp[index]);
end.

关于c# - 使用数组进行非托管导出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14934016/

相关文章:

c# - Ajax.beginform 提交时会在 asp.net mvc 的数据库中创建多个条目

c# - C# 使用的 native C++ 库的内存分配器

c++ - C/C++ 动态链接如何在不同平台上工作?

c# - 从 C# 动态调用非托管 VB COM dll 时遇到问题?

c# - 使用 UTF8 编码将 C# 字符串写入预分配的非托管缓冲区

dll - LoadLibrary 失败并出现模块未找到错误 - 可能存在依赖性问题

c++ - Matlab:dll 的 C++ 头文件

c# - 将 ASP.net MVC2 升级到 MVC4 Razor View 智能感知不起作用

c# - 标记枚举与常规枚举的 HashSet

c# - 这个简单的条件运算符会在编译时优化吗? (。网)