在C#中使用Delphi的stuct数组和字符串

我一直在尝试以下列方式调用在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’类型的字段’Name’:无效的托管/非托管类型组合(字符串字段必须与LPStr,LPWStr,BStr或ByValTStr配对)。

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

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

打开数组

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

Delphi字符串

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


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

delphi

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

为了赞美David的答案,你可以编组一个Delphi字符串,但它很难看。 在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,然后将其转换回指针。 这会补偿长度前缀。