【问题标题】:Delphi FMX OSX Segmentation Fault 11Delphi FMX OSX 分段故障 11
【发布时间】:2018-10-22 23:30:24
【问题描述】:

有很多 Segmentation Fault 11 帖子,但似乎没有一个回答我的问题:

我之前已经发布了简单测试应用程序的代码,但只是为了完整性而提供的。 Windows 10、Delphi 10.2.3 和 OSX High Sierra 10.13.6 和 Xcode 9.4.1。

主持人:

unit uDylibTest1;

interface

uses
  System.SysUtils,
  System.Types,
  System.Diagnostics,
  System.UITypes,
  System.Classes,
  System.Variants,

  FMX.Types,
  FMX.Controls,
  FMX.Forms,
  FMX.Graphics,
  FMX.Dialogs,
  FMX.StdCtrls,
  FMX.Platform,
  FMX.Controls.Presentation;


const
  // Windows DLL Names
  {$IFDEF MSWINDOWS}
  TestDLL = 'pTestDLL.dll';
  {$ENDIF MSWINDOWS}

  // macOS DYLIB Names
  {$IFDEF MACOS}
  //TestDLL = 'libpTestDLL.dylib';
    TestDLL = 'libpTestDLL.dylib';
  {$ENDIF MACOS}

type
  TfDylibTest = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

type
  TuDylibTest = class(TForm)
  private
    { Private declarations }
  public
    { Public declarations }
  end;

{$IFDEF MSWINDOWS}
function say_Hello(Hello: string; out ReturnString: string): boolean; stdcall; external TestDLL Delayed;
{$ENDIF MSWINDOWS}
{$IFDEF MACOS}
function _say_Hello(Hello: string; out ReturnString: string): boolean; cdecl; external TestDLL;
{$ENDIF MACOS}

function local_say_hello(Hello: string): boolean; forward;

var
  fDylibTest: TfDylibTest;

implementation

function local_say_hello(Hello: string): boolean;
begin
  Result := True;
  showmessage(Hello);
end;

{$R *.fmx}
{$R *.Macintosh.fmx MACOS}
{$R *.Windows.fmx MSWINDOWS}

procedure TfDylibTest.Button1Click(Sender: TObject);
var
 b:boolean;
 sType: string;
 sDLLString: string;

begin
  b := False;
  sType := '';
  // Call the DLL Function
  {$IFDEF MSWINDOWS}
  sDLLString := 'The string passed to the Windows DLL';
  b := say_Hello(sDLLString, sType);
  {$ENDIF MSWINDOWS}
  {$IFDEF MACOS}
  sDLLString := 'The string passed to the macOS DYLIB';
  b :=  _say_Hello(sDLLString, sType);
  {$ENDIF MACOS}

  if b then
    showmessage('Returned From: ' + sType + ': TRUE')
  else
    showmessage('Retur was: FALSE');
end;

procedure TfDylibTest.Button2Click(Sender: TObject);
var
  b: boolean;
 iTime2Auth: integer;
 Opened: boolean;
 i: integer;

begin
  Opened := False;

  // test the Waitnofreeze
  iTime2Auth := 0;

  b := False;
  // Call the local Function
  b := local_say_Hello('The string passed to the LOCAL function');

  if b then
    showmessage('Say Hello OK - LOCAL')
  else
    showmessage('Say Hello Failed - LOCAL');
end;

procedure TfDylibTest.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  showmessage('[Test App] onClose');
end;

procedure TfDylibTest.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  canClose := True;
  showmessage('[Test App] onCloseQuery');
end;

procedure TfDylibTest.FormCreate(Sender: TObject);
begin
  showmessage('[Test App] onCreate');
end;

initialization
  showmessage('[Test App] Initialization');  <<<<-- THIS IS EXECUTED

finalization
  showmessage('[Test App] Finalization');    <<<<-- THIS IS NOT EXECUTED

end.

还有 DYLIB:

unit uTestDLL;

interface

uses
  System.Diagnostics,
  System.Classes;

// External functions and procedures
{$IFDEF MSWINDOWS}
function say_Hello(Hello: string; out ReturnString: string): boolean; stdcall; forward;
{$ENDIF MSWINDOWS}
{$IFDEF MACOS}
function _say_Hello(Hello: string; out ReturnString: string): boolean; cdecl; forward;
{$ENDIF MACOS}

exports
  {$IFDEF MSWINDOWS}
  say_Hello;
  {$ENDIF MSWINDOWS}
  {$IFDEF MACOS}
  _say_Hello;
  {$ENDIF MACOS}

Implementation

{$IFDEF MSWINDOWS}
function say_Hello(Hello: string; out ReturnString: string): boolean; stdcall; export;
{$ENDIF MSWINDOWS}
{$IFDEF MACOS}
function _say_Hello(Hello: string; out ReturnString: string): boolean; cdecl; export;
{$ENDIF MACOS}
begin
  {$IFDEF MSWINDOWS}
  ReturnString := 'Windows DLL, Received: ' + Hello;
  {$ENDIF MSWINDOWS}
  {$IFDEF MACOS}
  ReturnString := 'macOS DYLIB, Received: ' + Hello;
  {$ENDIF MACOS}
  Result := True;
end;

end.

基本上,DYLIB 中的函数被传递一个字符串参数,它返回 TRUE 和另一个字符串的 OUT 变量,证明数据进入 DYLIB 函数并返回。

我在调用应用程序中有简单的 showmessage() 语句,用于 onCloseQuery 和 onClose 等,这些都显示,一切正常,直到应用程序关闭(onCloseQuery 执行,onClose 执行,大约半秒到一秒一切都关闭后问题报告弹出)。注意 Initialisation Section IS 被执行,但 Finalization Section 没有被执行(虽然它在 Win32 模式下运行时被执行)。我已将谷歌驱动器链接放入错误日志。 OSX Problem Report

当我查看堆栈跟踪时,没有提到我的两个文件(DylibTest 和 libpTesDLL.DYLIB),它们都是 RTL/系统单元。我用谷歌搜索了 Seg Fault 11 并搜索了论坛,但似乎没有任何关系。

如果有任何关于如何继续的建议,我将不胜感激?

【问题讨论】:

  • 是否部署了相同的代码,PAServer 终端显示了多行:001844AD 处的运行时错误 231。从 Finder 再次运行应用程序并没有收到 231 错误,但仍然收到 seg fault 11。

标签: delphi firemonkey delphi-10.2-tokyo macos-high-sierra


【解决方案1】:

string 不是用于跨库边界互操作的可移植数据类型。请改用PChar,然后您需要决定 WHO 为字符串(库或调用应用程序)分配和释放内存,以及如何分配和释放它们,并以互操作兼容的方式进行。您没有以安全的方式跨库边界传递字符串,因此您的代码崩溃也就不足为奇了。

尝试类似的方法:

主机:

// Windows DLL
{$IFDEF MSWINDOWS}
const
  TestDLL = 'pTestDLL.dll';

function say_Hello(Hello: PChar; out ReturnString: PChar): boolean; stdcall; external TestDLL Delayed;
procedure free_String(Mem: PChar); stdcall; external TestDLL Delayed;
{$ENDIF}

// macOS DYLIB
{$IFDEF MACOS}
const
  TestDLL = 'libpTestDLL.dylib';

function say_Hello(Hello: PChar; out ReturnString: PChar): boolean; cdecl; external TestDLL name '_say_Hello';
function free_String(Mem: PChar); cdecl; external TestDLL name '_free_String';
{$ENDIF}

...

procedure TfDylibTest.Button1Click(Sender: TObject);
var
 b: boolean;
 sType: PChar;
 sDLLString: string;
begin
  b := False;
  sType := nil;

  // Call the DLL Function
  {$IF DEFINED(MSWINDOWS) OR DEFINED(MACOS)}
  sDLLString := 'The string passed to the ' + {$IFDEF MSWINDOWS}'Windows DLL'{$ELSE}'macOS DYLIB'{$ENDIF};
  b := say_Hello(PChar(sDLLString), sType);
  if b then
  begin
    try
      ShowMessage('Returned From: ' + string(sType) + ': TRUE');
    finally
      free_String(sType);
    end;        
    Exit;
  end;
  {$IFEND}

  ShowMessage('Retur was: FALSE');
end;

DYLIB:

unit uTestDLL;

interface

{$IF DEFINED(MSWINDOWS) OR DEFINED(MACOS)}

// External functions and procedures
function say_Hello(Hello: PChar; out ReturnString: PChar): boolean; {$IFDEF MSWINDOWS}stdcall{$ELSE}cdecl{$ENDIF}; forward;
procedure free_String(S: PChar); {$IFDEF MSWINDOWS}stdcall{$ELSE}cdecl{$ENDIF}; forward;

exports
  say_Hello{$IFNDEF MSWINDOWS} name '_say_Hello'{$ENDIF},
  free_String{$IFNDEF MSWINDOWS} name '_free_String'{$ENDIF};

{$IFEND}

implementation

uses
  System.Diagnostics,
  System.Classes;

{$IF DEFINED(MSWINDOWS) OR DEFINED(MACOS)}

function say_Hello(Hello: PChar; out ReturnString: PChar): boolean; {$IFDEF MSWINDOWS}stdcall{$ELSE}cdecl{$ENDIF}; export;
var
  s: string;
begin
  try
    s := 'FROM: ' + {$IFDEF MSWINDOWS}'Windows DLL'{$ELSE}'macOS DYLIB'{$ENDIF} + ', Received: ' + string(Hello);
    ReturnString := StrAlloc(Length(s));
    try
      StrPCopy(ReturnString, s);
    except
      StrDispose(ReturnString);
      throw;
    end;
    Result := True;
  except
    Result := False;
  end;
end;

procedure free_String(S: PChar); {$IFDEF MSWINDOWS}stdcall{$ELSE}cdecl{$ENDIF}; export;
begin
  StrDispose(S);
end;

{$IFEND}

end.

或者:

主机:

// Windows DLL
{$IFDEF MSWINDOWS}
const
  TestDLL = 'pTestDLL.dll';

function say_Hello(Hello: PChar): PChar; stdcall; external TestDLL Delayed;
procedure free_String(Mem: PChar); stdcall; external TestDLL Delayed;
{$ENDIF}

// macOS DYLIB
{$IFDEF MACOS}
const
  TestDLL = 'libpTestDLL.dylib';

function say_Hello(Hello: PChar): PChar; cdecl; external TestDLL name '_say_Hello';
function free_String(Mem: PChar); cdecl; external TestDLL name '_free_String';
{$ENDIF}

...

procedure TfDylibTest.Button1Click(Sender: TObject);
var
 sType: PChar;
 sDLLString: string;
begin
  sType := nil;

  // Call the DLL Function
  {$IF DEFINED(MSWINDOWS) OR DEFINED(MACOS)}
  sDLLString := 'The string passed to the ' + {$IFDEF MSWINDOWS}'Windows DLL'{$ELSE}'macOS DYLIB'{$ENDIF};
  sType := say_Hello(PChar(sDLLString));
  if sType <> nil then
  begin
    try
      ShowMessage('Returned From: ' + string(sType) + ': TRUE');
    finally
      free_String(sType);
    end;        
    Exit;
  end;
  {$IFEND}

  ShowMessage('Retur was: FALSE');
end;

DYLIB:

unit uTestDLL;

interface

{$IF DEFINED(MSWINDOWS) OR DEFINED(MACOS)}

// External functions and procedures
function say_Hello(Hello: PChar): PChar; {$IFDEF MSWINDOWS}stdcall{$ELSE}cdecl{$ENDIF}; forward;
procedure free_String(S: PChar); {$IFDEF MSWINDOWS}stdcall{$ELSE}cdecl{$ENDIF}; forward;

exports
  say_Hello{$IFNDEF MSWINDOWS} name '_say_Hello'{$ENDIF},
  free_String{$IFNDEF MSWINDOWS} name '_free_String'{$ENDIF};

{$IFEND}

implementation

uses
  System.Diagnostics,
  System.Classes;

{$IF DEFINED(MSWINDOWS) OR DEFINED(MACOS)}

function say_Hello(Hello: PChar): PChar; {$IFDEF MSWINDOWS}stdcall{$ELSE}cdecl{$ENDIF}; export;
var
  s: string;
begin
  try
    s := 'FROM: ' + {$IFDEF MSWINDOWS}'Windows DLL'{$ELSE}'macOS DYLIB'{$ENDIF} + ', Received: ' + string(Hello);
    Result := StrAlloc(Length(s));
    try
      StrPCopy(Result, s);
    except
      StrDispose(Result);
      throw;
    end;
  except
    Result := nil;
  end;
end;

procedure free_String(S: PChar); {$IFDEF MSWINDOWS}stdcall{$ELSE}cdecl{$ENDIF}; export;
begin
  StrDispose(S);
end;

{$IFEND}

end.

【讨论】:

  • 由于某种原因,@Remy Lebeau 在我保存评论时不断被删除。谢谢,有道理。我要移植的更大的 DYLIB 具有 pChar 返回而不是字符串。对于测试,我认为字符串可以,显然不是。我会执行您的建议并返回结果。
  • @Remy.Lebeau 按预期工作。谢谢你(特别是花时间给我几个选择,我很感激)。使用 pChar 代替字符串可能节省了我追逐鬼魂的时间。
  • 公平地说,当向/从 DYLIB 传递参数时,可接受的类型是:pChar、布尔值和整数?
  • @Cirrus22 你应该只传递可移植的 POD 类型,而不是特定于供应商的类型
  • @Remy.Lebeau 只是 hanks,POD 类型肯定是我的 int、bool、pChar 的超集,我当然可以将参数/返回值保留到那个小组。我的想象力越少,出现意外错误的可能性就越小。再次感谢,感谢您在此论坛和其他论坛上投入的时间。
猜你喜欢
  • 2016-01-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-19
  • 1970-01-01
相关资源
最近更新 更多