【问题标题】:How do I properly pass a Delphi Record to a C# struct with Interop?如何使用 Interop 将 Delphi Record 正确传递给 C# 结构?
【发布时间】:2010-07-12 18:46:48
【问题描述】:

在 Delphi 中,我有这样的结构:

  TCustomerInfo = Packed Record
    CustomerNo: Integer;
    FirstName: String[50];
    LastName: String[50];
  End;

使用这样的虚拟进程:

procedure GetCustomer(CustomerNo: Integer; var CustomerInfo: TCustomerInfo);
begin
  CustomerInfo.CustomerNo := 19901;
  CustomerInfo.FirstName := 'JOHN';
  CustomerInfo.LastName := 'DOE';
end;

在 C# 中我有这个:

 [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi, Pack=1)]
 struct CUSTOMER_INFO
 {
  public Int32 CustomerNo;
  [MarshalAs(UnmanagedType.ByValTStr, SizeConst=50)]
  public string FirstName;
  [MarshalAs(UnmanagedType.ByValTStr, SizeConst=50)]
  public string LastName;
 }

使用这样的导入 Delphi 函数:

 [DllImport("Exceline.dll")]
 extern static void GetCustomer(Int32 CustomerNo, ref CUSTOMER_INFO CustomerInfo);

这个想法是确保所有内存分配和存储都由 C# 应用程序处理。

我的问题是,从 GetCustomer 返回时,没有任何内容分配给我的 C# 结构:-/

【问题讨论】:

    标签: c# delphi interop delphi-7


    【解决方案1】:

    我终于想出了一个避免所有 Alloc/FreeHGlobal 的解决方案,但如果这真的是关于垃圾收集器的要点,那就是另一回事了。

    解决方案是首先使用 FillChar 清除 TCustomer 结构,然后使用 Move 过程复制数据。

    delphi 记录如下所示:

      TCustomer = packed record
        CustomerNo: Integer;
        FirstName: array [1..50] of Char;
        LastName: array [1..50] of Char;
      end;
    

    然后我用一个过程将字符串复制到结构中:

    procedure StrToBuf(Str: String; var buf);
    begin
      Move(Pointer(str)^, buf, Length(str));
    end;
    

    在一个过程中或多或少像这样:

    procedure LoadCustomerFromQuery(var Query: TQuery; var Customer: TCustomer); stdcall;
    begin
    
      FillChar(Customer, SizeOf(Customer), 0);
    
      StrToBuf(Query.FieldByName('FNAVN').AsString, Customer.FirstName);
      StrToBuf(Query.FieldByName('ENAVN').AsString, Customer.LastName);
    
      Customer.CustomerNo := Query.FieldByName('KUNDENR').AsInteger;
    
    end;
    

    最后,C# 结构看起来像这样:

    [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi, Pack=1)]
    public struct TCustomer
    {
        public Int32 CustomerNo;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
        public string FirstName;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
        public string LastName;
    }
    

    【讨论】:

    • StrToBuf 在 unicode Delphi 中应该有 sizeof (char)。
    【解决方案2】:
    extern static void GetCustomer(Int32 CustomerNo, IntPtr CustomerInfo);
    ...
    var info = default(CUSTOMER_INFO);
    var ptr = Marshal.AllocHGlobal(Marshal.SizeOf(info));
    Marshal.StructureToPtr(info, ptr, false);
    GetCustomer(n, ptr);
    Marshal.PtrToStructure(ptr, info);
    Marshal.FreeHGlobal(ptr);
    

    【讨论】:

    • 好的,谢谢,我越来越接近这个了,但我得到一个错误:Marshal.PtrToStructure(ptr, custInf) => 结构不能是值类。
    • 此外,C# 的 ByValTStr 似乎不是 Delphi 的 String[50] 的正确选择。我假设 Delphi 使用标准的 byte-len 前缀 Pascal 字符串,但我不允许在 MarshalAs-attribute 中使用 AnsiBStr。
    • 改用info = (CUSTOMER_INFO)Marshal.PtrToStructure(ptr, typeof(CUSTOMER_INFO));。至于 Delphi 字符串,它们是 AnsiString 的别名,我认为它只是一个字符数组,每个 1 个字节。我不确定长度是否有前缀 - 有没有机会转储你得到的东西?
    • A string[50] 本质上是一个 51 个字节的数组,其中第一个字节是长度字节。 (如果您希望它以零终止,则必须手动执行此操作。)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-01
    • 2017-03-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多