【问题标题】:Using Delphi's stuct arrays and strings in C#在 C# 中使用 Delphi 结构数组和字符串
【发布时间】:2012-05-14 13:52:35
【问题描述】:

我一直在尝试通过以下方式调用在 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(我已经尝试过使用其他字符串参数)。有人知道如何解决这个问题吗?

【问题讨论】:

    标签: c# delphi pinvoke


    【解决方案1】:

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

    开放数组

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

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

    【讨论】:

    • 感谢您的回答和编辑。我会尝试这个解决方案。据我了解,我需要创建新的 Delphi 库来公开使用字符串类型的函数并用 PAnsiChar 替换它们?对吗?
    • 是的,这是正确的。魔鬼在细节中,但在高层次上,你所说的是正确的。如果你这样做,我可能也会避开开放数组并明确接收指向第一个元素和元素数量的指针。
    【解决方案2】:

    为了恭维大卫的回答,您可以编组到 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,然后将其转换回指针。这可以补偿长度前缀。

    【讨论】:

      猜你喜欢
      • 2021-11-24
      • 2023-04-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-15
      • 2018-06-30
      • 2016-03-06
      相关资源
      最近更新 更多