2

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から未知のサイズの配列を単に返すことを試みています。

4

1 に答える 1

4

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.
于 2013-02-22T13:20:21.290 に答える