4

我一直在尝试通过以下方式调用在 Delphi 中创建的方法:

 function _Func1(arrParams: array of TParams): Integer;stdcall;    

 type 
   TParams = record
   Type: int;
   Name: string;
   Amount : Real;
 end;

我的代码是:

[DllImport("some.dll", EntryPoint = "_Func1", CallingConvention = CallingConvention.StdCall)]
public static extern int Func(
  [MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.Struct)] TParams[] arrParams)

结构是:

[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct TParams
{
  public int Type;
  [MarshalAs(UnmanagedType.AnsiBStr)]
  public string Name;
  public double Amount;
}

当我调用此方法时,我收到错误消息:无法编组“TParams”类型的字段“名称”:托管/非托管类型组合无效(字符串字段必须与 LPStr、LPWStr、BStr 或 ByValTStr 配对)。

然而,这些组合都不起作用,因为 Delphi 的字符串以它的长度为前缀,而且它肯定是 Ansi(我已经尝试过使用其他字符串参数)。有谁知道如何解决这个问题?

4

2 回答 2

7

这有两个主要问题,使用开放数组和使用 Delphi string

开放阵列

Delphi 开放数组是通过传递指向数组的第一个元素的指针和指定最后一项的索引的额外参数来实现的,high在 Delphi 术语中。有关更多信息,请参阅此答案

德尔福字符串

C# 编组器无法与 Delphi 字符串互操作。Delphi 字符串是私有类型,只能在 Delphi 模块内部使用。相反,您应该使用以 null 结尾的字符串,PAnsiChar.


把它们放在一起,你可以这样写:

德尔福

type 
  TParams = record
    _Type: Integer;//Type is a reserved word in Delphi
    Name: PAnsiChar;
    Amount: Double;
  end;

function Func(const arrParams: array of TParams): Integer; stdcall;

C#

[StructLayoutAttribute(LayoutKind.Sequential)]
public struct TParams
{
  public int Type;
  public string Name;
  public double Amount;
}

[DllImport("some.dll")]
public static extern int Func(TParams[] arrParams, int high);

TParams[] params = new TParams[len];
...populate params
int retval = Func(params, params.Length-1);
于 2012-05-14T14:10:36.747 回答
1

为了恭维大卫的回答,您可以编组到德尔福字符串,但这很丑陋。在 C# 中,您必须将结构中的所有字符串替换为IntPtr.

private static IntPtr AllocDelphiString(string str)
{
    byte[] unicodeData = Encoding.Unicode.GetBytes(str);
    int bufferSize = unicodeData.Length + 6;

    IntPtr hMem = Marshal.AllocHGlobal(bufferSize);

    Marshal.WriteInt32(hMem, 0, unicodeData.Length); // prepended length value

    for (int i = 0; i < unicodeData.Length; i++)
        Marshal.WriteByte(hMem, i + 4, unicodeData[i]);

    Marshal.WriteInt16(hMem, bufferSize - 2, 0); // null-terminate

    return new IntPtr(hMem.ToInt64() + 4);
}

这可以直接发送到 Delphi,在那里它会被正确地读取为字符串。

请记住,您必须在完成后释放该字符串。但是,GlobalFree()不能直接在指向字符串的指针上调用,因为它不指向分配的开始。您必须将该指针转换为 long,然后减去 4,然后将其转换回指针。这补偿了长度前缀。

于 2012-10-03T14:05:34.227 回答