从C#传递结构数组德尔福 [英] Passing array of struct from c# to delphi
问题描述
我使用的罗伯特·捷托管输出1.2.6 在 VS2010 我的目标是从 C#(.NET 3.5)传递结构的数组为德尔福(D7)。
我必须承认,我不是那么熟悉德尔福
我已经阅读过的这个帖子,但建议的答案没'对我来说将不起作用:
当在Delphi中CPU调试窗口调用 FUNC
打开,如果我继续在应用程序退出无一例外,没有期望的结果<。 / p>
下面是我试过的代码:
C#平台86
使用系统;
使用System.Collections.Generic;
使用System.Linq的;
使用System.Text;使用System.Runtime.InteropServices
;
使用RGiesecke.DllExport;
命名空间ArrayTest中
{
公共类的Class1
{
公共结构示例
{
[的MarshalAs(UnmanagedType.BStr) ]
公共字符串名称;
}
[DLLEXPORT]
公共静态INT FUNC(
[手续,的MarshalAs(UnmanagedType.LPArray,SizeParamIndex = 1)]
样品[]样本,
裁判INT的len $ b $二)
{
// len个持有// len个被分配一个已项的数目上的输入
中的阵列的长度赋值
//使用返回值来指示成功或失败
的for(int i = 0; I< LEN,我++)
样本[I] .Name点=富 + i.ToString();
返回0;
}
}
}
Delphi7中
程序DelphiApp;
{$ APPTYPE CONSOLE}
使用
SysUtils单元,
的ActiveX;
型
TSAMPLE =记录
名称:WideString的;
端;
PSample = ^ TSAMPLE;
函数func(样本:PSample; VAR LEN:整数):整数; STDCALL;
外部ArrayTest.dll';
程序的Test2;
变量
瓶尿样:TSAMPLE的阵列;
我,LEN:整数;
开始
LEN:= 10;
SetLength函数(样本,LEN);
如果FUNC(PSample(样本),LEN)= 0,那么
为I:= 0到LEN-1做
Writeln(样本[I] .Name点);
端;
开始
的Test2();
端。
如前所述,调试器打开CPU-窗口,如果我继续应用程序退出无一例外或错误信息。
。如果我运行它没有dbugger时,Windows告诉我的应用无法正常运行任何更多的应用程序关闭。
我缺少什么?
更新
修改后的代码:
[DLLEXPORT]
公共静态INT FUNC(
[手续,的MarshalAs(UnmanagedType.LPArray,SizeParamIndex = 1)]
样品[]样本,
裁判INT LEN
)
{
Console.WriteLine(返回0);
返回0;
}
程序的Test2;
变量
瓶尿样:TSAMPLE的阵列;
我,LEN:整数;
开始
LEN:= 10;
SetLength函数(样本,LEN);
如果FUNC(PSample(样本),LEN)= 0,那么
为I:= 0到LEN-1做
Writeln('D7:'我);
端;
就算我不访问两侧的阵列,其行为仍然是相同的。
控制台输出:返回0
好像我发现了问题:
如果使用.NET 4.0或更高版本的代码运行正常。如果你使用.NET 3.5或降低len个参数必须按值传递。
请参阅MSDN的文档的 SizeParamIndex V3.5 :
< BLOCKQUOTE>
包含大小必须是按值传递的整数参数。
块引用>
请参阅MSDN-文档 SizeParamIndex V4.0 :
在阵列为C风格的数组通过,封送不能
确定数组的大小。因此,要传递一个托管数组
到非托管函数或方法时,必须提供两个参数:
的阵列,通过引用或值来定义。
数组大小,引用或值。
<定义/ UL>
块引用>
代码与.NET 3.5的工作:
C#
[DLLEXPORT]
公共静态INT FUNC(
[手续,的MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)]
样品[]样本,
INT LEN,
裁判INT outLen
)
{
// LEN持有的长度上输入
//数组outLen被分配已经分配值
//使用返回值来指示成功和所需的数组大小项的数目(大于= 0)或失败(小于0)
INT requiredSize = 20;
如果(requiredSize< LEN)
{
LEN = requiredSize;
}
为(outLen = 0; outLen< LEN; outLen ++)
{
样本[outLen] .Name点=富+ outLen.ToString();
}
返回requiredSize;
}
Delphi7中
函数func(样本:PSample; LEN:整数; VAR outLen:整数):整数; STDCALL;
外部ArrayTest.dll';
程序的Test2;
变量
瓶尿样:TSAMPLE的阵列;
我,LEN:整数;
开始
LEN:= 0;
//查询所需要的数组大小
I:= FUNC(PSample(样本),LEN,LEN);
如果我大于0,然后
开始
LEN:= I;
SetLength函数(样本,LEN);
如果FUNC(PSample(样本),LEN,LEN)> = 0,那么
为I:= 0到LEN-1做
Writeln(样本[I] .Name点);
端;
端;
结论:
中的代码张贴在我的问题和大卫·赫弗南张贴这里只适用于.NET> = 4.0!
如果你必须使用.NET<!= 3.5,你必须通过引用通过值传递ARRAYSIZE而不是I am using Robert Giesecke Unmanaged Exports 1.2.6 in VS2010 and my goal is to pass an array of structs from c# (.NET 3.5) to delphi (D7). I have to admit, that I'm not that familiar with delphi.
I've already read this post, but the suggested answer didn't work for me: When calling
func
in delphi the CPU-debugging-window opens and if I continue the app exits without exception and without the desired result.Here is the code I tried:
C# platform x86
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.InteropServices; using RGiesecke.DllExport; namespace ArrayTest { public class Class1 { public struct Sample { [MarshalAs(UnmanagedType.BStr)] public string Name; } [DllExport] public static int func( [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] Sample[] samples, ref int len ) { // len holds the length of the array on input // len is assigned the number of items that have been assigned values // use the return value to indicate success or failure for (int i = 0; i < len; i++) samples[i].Name = "foo: " + i.ToString(); return 0; } } }
Delphi7
program DelphiApp; {$APPTYPE CONSOLE} uses SysUtils, ActiveX; type TSample = record Name: WideString; end; PSample = ^TSample; function func(samples: PSample; var len: Integer): Integer; stdcall; external 'ArrayTest.dll'; procedure Test2; var samples: array of TSample; i, len: Integer; begin len := 10; SetLength(samples, len); if func(PSample(samples), len)=0 then for i := 0 to len-1 do Writeln(samples[i].Name); end; begin Test2(); end.
As mentioned earlier, the debugger opens the CPU-Window and if I continue the app exits without exception or error message. If I run it without dbugger, Windows tells me the app isn't working any more and the app closes.
What am I missing?
Update
Modified code:
[DllExport] public static int func( [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] Sample[] samples, ref int len ) { Console.WriteLine("return 0"); return 0; } procedure Test2; var samples: array of TSample; i, len: Integer; begin len := 10; SetLength(samples, len); if func(PSample(samples), len)=0 then for i := 0 to len-1 do Writeln('D7: ', i); end;
Even if I don't access the array on either side, the behaviour still is the same.
Console-output:
return 0
解决方案Seems like I found the issue:
The code runs fine if .NET 4.0 or higher is used. If you use .NET 3.5 or lower the len-parameter has to be passed by value.
See MSDN-documentation SizeParamIndex v3.5:
The parameter containing the size must be an integer that is passed by value.
See MSDN-documentation SizeParamIndex v4.0:
When arrays are passed as C-style arrays, the marshaler cannot determine the size of the array. Therefore, to pass an managed array to an unmanaged function or method, you must provide two arguments:
The array, defined by reference or value.
The array size, defined by reference or value.
Code working with .NET 3.5:
C#
[DllExport] public static int func( [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] Sample[] samples, int len, ref int outLen ) { // len holds the length of the array on input // outLen is assigned the number of items that have been assigned values // use the return value to indicates success and the required array size (>=0) or failure (<0) int requiredSize = 20; if (requiredSize < len) { len = requiredSize; } for (outLen = 0; outLen < len; outLen++) { samples[outLen].Name = "foo: " + outLen.ToString(); } return requiredSize; }
Delphi7
function func(samples: PSample; len: Integer; var outLen: Integer): Integer; stdcall; external 'ArrayTest.dll'; procedure Test2; var samples: array of TSample; i, len: Integer; begin len := 0; // query the required array size i := func(PSample(samples), len, len); if i>0 then begin len := i; SetLength(samples, len); if func(PSample(samples), len, len)>=0 then for i := 0 to len-1 do Writeln(samples[i].Name); end; end;
Conclusion:
The code posted in my question and posted by David Heffernan here only works with .NET >= 4.0! If you have to use .NET <= 3.5 you must pass the arraysize by value and not by reference!
这篇关于从C#传递结构数组德尔福的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!