【问题标题】:Strings between FPC & DelphiFPC 和 Delphi 之间的字符串
【发布时间】:2014-02-25 20:03:36
【问题描述】:

我在 Delphi 中从 FPC DLL 获取 String 的长度时遇到问题。这很奇怪,因为我可以从 DLL 中取回 String,但我无法获得它的长度。

德尔福:

program Project2;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils;

function Test(const S: String): Integer; cdecl; external 'c:\Project1.dll';

var
    A: String;
begin 
    A := 'test';
    WriteLn(Test(A)); // 1 ?
    ReadLn;
end.

FPC:

library project1;

{$mode ObjFPC}{$H+}

uses
  Classes;

function Test(const A: String): Integer; cdecl; export;
begin
 Result := Length(A);
end;

exports Test;

end.

【问题讨论】:

    标签: delphi delphi-xe5 fpc


    【解决方案1】:

    String 在 Delphi 2009+ 中是 UnicodeString,在早期版本中是 AnsiString

    FPC 中的String 始终为AnsiString,它从不映射到UnicodeString。而且 AFAIK,FPC 的字符串类型无论如何都不是与 Delphi 的字符串类型二进制兼容的。所以你不能将 Delphi AnsiString 传递给 FPC AnsiString,反之亦然,UnicodeString 也是如此。

    无论如何,您都不应该在 DLL 边界上传递 String 值,尤其是在涉及多个编译器时,尤其是因为您没有使用 FPC 的 Delphi 模式。您需要重新设计您的 DLL 以使其更具可移植性,例如:

    FPC:

    library project1;
    
    {$mode ObjFPC}
    {$H+}
    
    uses
      Classes;
    
    function TestA(const A: PAnsiChar): Integer; cdecl; export;
    begin
     Result := Length(A);
    end;
    
    function TestW(const A: PWideChar): Integer; cdecl; export;
    begin
     Result := Length(A);
    end;
    
    exports TestA, TestW;
    
    end.
    

    德尔福:

    program Project2;
    
    {$APPTYPE CONSOLE}
    
    {$R *.res}
    
    uses
      System.SysUtils;
    
    function Test(const S: PChar): Integer; cdecl; external 'Project1.dll' name {$IFDEF UNICODE}'TestW'{$ELSE}'TestA'{$ENDIF};
    
    var
      A: String;
    begin 
      A := 'test';
      WriteLn(Test(PChar(A)));
      ReadLn;
    end.
    

    【讨论】:

    • 没有执行转换,也没有性能损失。将UnicodeString 转换为PWideChar 仅检索指向UnicodeString 字符数据的指针,或者如果UnicodeString 为空,则检索指向静态#0 字符的指针。
    • FPC 中的字符串始终是 AnsiString 以及 Delphi 中的 AnsiString 或 UnicodeString 似乎并不重要。最好强调一下 FPC.AnsiString 不等于 Delphi.AnsiString。
    • @RemyLebeau 我仍然在 Delphi 中对一个函数进行了基准测试。使用 PWideChar 而不是 String 类型调用它会增加一些延迟。所以很明显,这种方法并非没有缺点。
    • 切换到PChars 不带长度指示器将引入潜在的缓冲区溢出漏洞。
    • @DavidHeffernan:在此问题的示例中PChar(String) 保证空终止符。但是IN GENERAL,不能保证一个空终止符,例如:var Buffer: array[0..5] of Char; Buffer is filled without a null terminator; SomeFuncThatTakesAPChar(@Buffer[0]); 如果函数需要一个空终止符并且没有给定一个,则可能会发生缓冲区溢出。传递缓冲区长度将避免这种情况:SomeFuncThatTakesAPChar(@Buffer[0], Length(Buffer))。这就是我要说的。它不适用于本问题中的示例,但可以适用于其他情况。
    【解决方案2】:

    您不能在此模块边界上使用string。 Delphi 类型与 FPC 类型完全不同。的确,它们具有相同的名称,但这并不意味着它们是同一类型。

    事实上,即使两个模块都是用同一个编译器编译的,它们也会是不同的类型,分配在不同的堆上并且对互操作无效。在 Delphi 中,您可以使用 Sharemem 和完全相同的编译器版本,但这非常受限制。

    使用互操作友好的类型,例如 PWideChar 用于 UTF-16 或 PAnsiChar 用于 UTF-8。这样你的库就不会受到限制,并且可以与任何东西互操作。

    【讨论】:

    • @Ritsaert 我就是这么想的。
    • @Ritsaert 我什至从来没有说过现在的类型是一样的。事实上我说他们是不同的句号。您只能通过强制相同的编译器和共享内存管理器使它们相同。我看不出问题出在哪里。
    • @DavidHeffernan 解决方案是{$mode delphiunicode}。而且我可以拥有原生字符串互操作。
    • @user 不,你不能。我在回答中所说的是准确的。
    • @Ritsaert 类型总是不同的。他们从来都不一样。我看不出假设一个永远不会发生的场景有什么意义。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-29
    • 1970-01-01
    • 2021-11-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多