DllExport:将数组从Delphi传递到C#

DllExport:将数组从Delphi传递到C#,c#,arrays,delphi,marshalling,dllexport,C#,Arrays,Delphi,Marshalling,Dllexport,我正在使用RGiesecke的“非托管导出”包从C#创建一个dll,该dll可以从Delphi应用程序调用 具体来说,我希望传递一个结构的数组 我在C#中所做的是 然后可以从Delphi调用,执行如下操作: unit MyUnit interface type TVector = array[X..Y] of single; TVectorCollection = array of TVector; procedure TDoExternalStuff(const vectors :

我正在使用RGiesecke的“非托管导出”包从C#创建一个dll,该dll可以从Delphi应用程序调用

具体来说,我希望传递一个结构的数组

我在C#中所做的是

然后可以从Delphi调用,执行如下操作:

unit MyUnit
interface
type
  TVector = array[X..Y] of single;
  TVectorCollection = array of TVector;
  procedure TDoExternalStuff(const vectors : TVectorCollection; count : integer; stdcall;
  procedure DoSomeWork;

implementation

procedure DoSomeWork;
var
  vectors : array of TVector;
  fDoExternalStuff : TDoExternalStuff;
  Handle: THandle;
begin
  // omitted: create and fill vectors
  Handle := LoadLibrary('MyExport.dll');
  @fDoExternalStuff := GetProcAddress(Handle, 'DoStuff');
  fDoExternalStuff(vectors, Length(vectors)); 
end;

end.
[DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)]
public static extern void CopyMemory(IntPtr dest, IntPtr src, uint count);

[DllExport]
public static void DoStuff( 
    [In] 
    int arrayCount, 
    [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] 
    IntPtr[] arrays, 
    [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] 
    int[] subArrayCount, 
)
{
    MyVector[][] input = new MyVector[arrayCount];
    for (int i = 0; i < arrayCount; i++)
    {
        input[i] = new MyVector[subArrayCount[i]];
        GCHandle gch = GCHandle.Alloc(input[i], GCHandleType.Pinned);
        try
        {
            CopyMemory(
                gch.AddrOfPinnedObject(), 
                arrays[i], 
                (uint)(subArrayCount[i]*Marshal.SizeOf(typeof(MyVector))
            );
        }
        finally
        {
            gch.Free();
        }
    }
}
type  
  TVectorDoubleArray = array of array of TVector;
  TIntegerArray = array of Integer;

procedure DoStuff(
  arrays: TVectorDoubleArray; 
  arrayCount: Integer;
  subArrayCount: TIntegerArray
); stdcall; external dllname;

....

procedure CallDoStuff(const arrays: TVectorDoubleArray);
var
  i: Integer;
  subArrayCount: TIntegerArray;
begin
  SetLength(subArrayCount, Length(arrays));
  for i := 0 to high(subArrayCount) do
    subArrayCount[i] := Length(arrays[i]);
  DoStuff(Length(Arrays), arrays, subArrayCount);
end;
然而,我真正需要做的是传递一个TVector数组。包含TVector数组的结构数组也可以。但是写作

[DllExport]
public static void DoStuff([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] 
  MyVector[][] vectors, int count)
{
  // Do stuff
}
不适用于Delphi

...
TVectorCollection = array of array of TVector;
...

procedure DoSomeWork;
var
  vectors : array of array of TVector;
  fDoExternalStuff : TDoExternalStuff;
  Handle: THandle;
begin
  // omitted: create and fill vectors
  Handle := LoadLibrary('MyExport.dll');
  @fDoExternalStuff := GetProcAddress(Handle, 'DoStuff');
  fDoExternalStuff(vectors, Length(vectors)); //external error 
end;
如果是这样的话,我也会有点惊讶,因为我没有在任何地方指定锯齿数组中单个元素的长度

我是否有办法设置DllExport函数,以便能够封送这种类型的元素

我是否有办法设置DllExport函数,以便能够封送这种类型的元素

否,p/invoke编组从不下降到子数组中。您将不得不手动封送

就我个人而言,我会传递一个指向子数组第一个元素的指针数组,以及一个包含子数组长度的数组

在C#侧,它将如下所示:

unit MyUnit
interface
type
  TVector = array[X..Y] of single;
  TVectorCollection = array of TVector;
  procedure TDoExternalStuff(const vectors : TVectorCollection; count : integer; stdcall;
  procedure DoSomeWork;

implementation

procedure DoSomeWork;
var
  vectors : array of TVector;
  fDoExternalStuff : TDoExternalStuff;
  Handle: THandle;
begin
  // omitted: create and fill vectors
  Handle := LoadLibrary('MyExport.dll');
  @fDoExternalStuff := GetProcAddress(Handle, 'DoStuff');
  fDoExternalStuff(vectors, Length(vectors)); 
end;

end.
[DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)]
public static extern void CopyMemory(IntPtr dest, IntPtr src, uint count);

[DllExport]
public static void DoStuff( 
    [In] 
    int arrayCount, 
    [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] 
    IntPtr[] arrays, 
    [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] 
    int[] subArrayCount, 
)
{
    MyVector[][] input = new MyVector[arrayCount];
    for (int i = 0; i < arrayCount; i++)
    {
        input[i] = new MyVector[subArrayCount[i]];
        GCHandle gch = GCHandle.Alloc(input[i], GCHandleType.Pinned);
        try
        {
            CopyMemory(
                gch.AddrOfPinnedObject(), 
                arrays[i], 
                (uint)(subArrayCount[i]*Marshal.SizeOf(typeof(MyVector))
            );
        }
        finally
        {
            gch.Free();
        }
    }
}
type  
  TVectorDoubleArray = array of array of TVector;
  TIntegerArray = array of Integer;

procedure DoStuff(
  arrays: TVectorDoubleArray; 
  arrayCount: Integer;
  subArrayCount: TIntegerArray
); stdcall; external dllname;

....

procedure CallDoStuff(const arrays: TVectorDoubleArray);
var
  i: Integer;
  subArrayCount: TIntegerArray;
begin
  SetLength(subArrayCount, Length(arrays));
  for i := 0 to high(subArrayCount) do
    subArrayCount[i] := Length(arrays[i]);
  DoStuff(Length(Arrays), arrays, subArrayCount);
end;

尽管可以使用动态数组
TVectorCollection
声明导入函数,但要注意这一点。在C#端,这被视为原始指针。所以你现在的情况很好,但是如果你朝相反的方向走,它就会坏掉。谢谢!这似乎是一个粗糙的解决方案。但如果只可能制造出一种粗溶液,那就这样吧。但是如何在一个方法调用中传递这两个列表呢?你会把它放在一个结构中吗?如果是的话,你会如何处理它?或者放在一个结构中,或者作为两个参数。您目前使用两个参数。用
int[]子阵列计数
替换
int计数
。如果您在执行此操作时遇到问题,请告诉我,我将提供帮助。@yms它不是多维数组。这是一个参差不齐的数组。大卫,我不太确定我是否会按照你的方法将
int count
更改为
int[]subraycount
。编写
publicstaticvoiddostuff([marshallas(UnmanagedType.LPArray,SizeParamIndex=1)]MyVector[]vectors,int[]subraycount)
似乎没有帮助——从Delphi调用时,我仍然会遇到一个外部错误,我将调用方法的签名从Delphi更改为
fDoExternalStuff(vectors,subraycount)
我可能误解了什么?它将是这样的:
公共静态void DoStuff([In]IntPtr[]向量,[In]int[]子数组计数)
向量的每个元素都是指向子数组第一个元素的指针。使用
Marshal.Copy
复制到.net数组中。