问题
I'm using RGiesecke DLLExport library to produce a C# DLL that can be dynamically loaded from Delphi. I have a method like:
[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++;
}
}
I want to be able to return an array from this call, the problem is I won't know the size of the array in advance, this will only be determined at RunTime.
To test I have done the following (also in 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)
{
}
}
A get an error "attemping to read or write to protected memory" which is understandable as I am not allocating memory on the caller. In real use I don't know the size of the array to return so cannot pre-allocate the memory. To put things in there most basic I am trying to simply return an array of unknown size from GetVals.
回答1:
You have to allocate the array in a way that it isn't affected by the GC. You do that with 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);
}
On the Delphi side, you will get a pointer to the first element, and the size. To read it you can increment the pointer arraySize-1 times and put it into a list or a Delphi-managed array:
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.
来源:https://stackoverflow.com/questions/14934016/unmanaged-exports-with-arrays