代码之家  ›  专栏  ›  技术社区  ›  Thomas Kjørnes

如何通过interop将delphi记录正确传递给C结构?

  •  1
  • Thomas Kjørnes  · 技术社区  · 14 年前

    在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结构:-/

    2 回复  |  直到 14 年前
        1
  •  2
  •   Thomas Kjørnes    14 年前

    我终于想出了一个解决方案,它可以避免所有的alloc/freehglobal,但是如果这真的是关于垃圾收集器的bulletproff,那就另当别论了。

    解决方案是首先用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;
    }
    
        2
  •  1
  •   Mark H    14 年前
    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);