Segmentation Fault 11的帖子很多,但似乎没有一个回答我的问题:
我之前已经发布了用于简单测试应用程序的代码,但出于完整性考虑而提供。 Windows 10,带有OSX High Sierra 10.13.6和Xcode 9.4.1的Delphi 10.2.3。
主机:
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以及大约半秒到一秒在一切关闭后,问题报告会弹出)。请注意,初始化部分已执行,但终结部分未执行(尽管在Win32模式下运行时已执行)。我已经把谷歌驱动器链接到错误日志。 OSX Problem Report
当我查看堆栈跟踪时,没有提到我的两个文件(DylibTest和libpTesDLL.DYLIB),它们都是RTL /系统单元。我已经用Google搜索Seg Fault 11并搜索了论坛,但似乎没有任何联系。
我将对我如何进行的任何建议表示赞赏?
答案 0 :(得分:3)
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.