2

我已成功使用 Roberts UnmanagedExportLibrary.zip从 Delphi 2007 调用 .NET 2/3.5 程序集。

但是,当我使用 VS2010 将 C# 程序集重新编译为面向 .NET 4 时,调用会因 ntdll.dll 中的堆栈溢出异常而崩溃。(ntdll 调用 ntdll)加载 mscorlib/mscoreei 后。

在针对 .NET 4 时,还有其他人可以使用它吗?- 罗伯特的文档似乎暗示这应该有效。

顺便说一句,罗伯特的伟大工作 - 非常有用。

谢谢迈尔斯。

4

1 回答 1

1

数组更加棘手,因为您需要更加注意分配和销毁数组的位置。最干净的方法总是在调用者处分配,将数组传递给被调用者,让它填充数组。在您的上下文中,这种方法看起来像这样:

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;
}

您需要指定需要在向外方向编组的数组。如果您希望以两种方式编组值,那么您将使用In, Out而不是Out. 您还需要使用MarshalAswithUnmanagedType.LPArray来指示如何编组数组。而且您确实需要指定大小参数,以便编组器知道要编组回非托管代码的项目数。

然后在 Delphi 端,你声明这样的函数:

type
  TSample = record
    Name: WideString;
  end;
  PSample = ^TSample;

function func(samples: PSample; var len: Integer): Integer; stdcall; 
  external dllname;

像这样称呼它:

var
  samples: array of TSample;
  i, len: Integer;
....
len := 10;
SetLength(samples, len);
if func(PSample(samples), len)=0 then
  for i := 0 to len-1 do
    Writeln(samples[i].Name);

更新

正如 AlexS [发现][1](请参阅下面的评论),仅在 .net 4 上支持通过引用传递大小参数索引。在早期版本中,您需要按值传递大小参数索引。

我选择在这里通过引用传递它的原因是允许以下协议:

  1. 调用者传入一个值指示数组有多大。
  2. 被调用者传递一个值指示已填充多少元素。

这在 .net 4 上运行良好,但在早期版本中,您需要为步骤 2 使用额外的参数。

参考 https://stackoverflow.com/a/22507948/4339857

于 2014-12-09T06:30:34.840 回答