C# 使用Delphi';用C语言构造数组和字符串#

C# 使用Delphi';用C语言构造数组和字符串#,c#,delphi,pinvoke,C#,Delphi,Pinvoke,我一直试图通过以下方式调用在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

我一直试图通过以下方式调用在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#


为了赞美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);
}
私有静态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);//带前缀的长度值
for(int i=0;i
这可以直接发送到Delphi,在那里它可以正确地作为字符串读取


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

感谢您的回答和编辑。我将尝试这个解决方案。据我所知,我需要创建新的Delphi库,该库将公开使用字符串类型的函数,并用PAnsiChar替换它们?对吗?对,对。魔鬼在于细节,但在高层次上你所说的是正确的。若你们这样做的话,我可能也会避开开放数组,显式地接收指向第一个元素和元素数的指针。
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);
}