DllExport:将数组从Delphi传递到C#
我正在使用RGiesecke的“非托管导出”包从C#创建一个dll,该dll可以从Delphi应用程序调用 具体来说,我希望传递一个结构的数组 我在C#中所做的是 然后可以从Delphi调用,执行如下操作: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 :
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数组中。