【问题标题】:Unmanaged Exports with Arrays带数组的非托管导出
【发布时间】:2013-02-18 10:23:02
【问题描述】:

我正在使用 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)
                    {

                    }
                }

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

【问题讨论】:

    标签: c# unmanaged loadlibrary


    【解决方案1】:

    您必须以不受 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.
    

    【讨论】:

      猜你喜欢
      • 2015-03-23
      • 1970-01-01
      • 2023-03-11
      • 2011-03-13
      • 1970-01-01
      • 2013-06-12
      • 1970-01-01
      • 2016-09-15
      • 2010-11-15
      相关资源
      最近更新 更多