问题
There are a number of Segmentation Fault 11 posts, but none seem to answer my question:
I've posted the code for the simple test application previously, but is provided for completeness. Windows 10, Delphi 10.2.3 with OSX High Sierra 10.13.6 and Xcode 9.4.1.
The HOST:
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.
And the 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.
Basically a function in a DYLIB is passed a string parameter, it returns TRUE with an OUT variable of another string which proves that the data went into the DYLIB function and returned out.
I have simple showmessage() statements in the calling application for things like onCloseQuery and onClose etc, these all show, everything WORKS as it should until the application closes(onCloseQuery executed, onClose executed, and about half a second to a second after everything shuts down the problem report pops up). Note that the Initialisation Section IS executed, but the Finalization Section is NOT executed (although it is executed when running in Win32 mode). I've put the google drive link to the error log. OSX Problem Report
When I look at the stack trace, there are none of my two files (DylibTest and libpTesDLL.DYLIB) mentioned, it's all RTL/system units. I've googled Seg Fault 11 and searched the forum, but nothing seems related.
I'd appreciate any advice with how I could proceed?
回答1:
string
is NOT a portable data type for interop across library boundaries. Use PChar
instead, and then you need to decide WHO allocates and frees memory for strings - the library or the calling app - and HOW they are allocated and freed, and do so in an interop compatible manner. You are not passing strings across the library boundary in a safe manner, so it is no wonder that you are crashing your code.
Try something more like this instead:
HOST:
// 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.
Alternatively:
HOST:
// 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.
来源:https://stackoverflow.com/questions/52939045/delphi-fmx-osx-segmentation-fault-11