I would like to load a DLL (from an VCL application but that should't be important [that is not true as both VCL and FMX contain a message loop]) and display a FireMonkey modal form created in that DLL. Showing the form works OK but I have problems cleaning after it ...
I can only find only articles on that topic from years 2011/2012 and they mostly refer to XE2. These solutions sadly don't work anymore. (Or I'm doing something wrong.)
All sample files are here: https://github.com/gabr42/GpDelphiCode/tree/master/FMX%20from%20DLL
My DLL just exports ShowMainForm
.
library FMXDLL;
uses
System.SysUtils,
System.Classes,
FMXMain in 'FMXMain.pas' {FormMain};
{$R *.res}
exports
ShowMainForm;
begin
end.
ShowMainForm
initializes GDI+ and then shows the form. Afterwards, it tries to clean up but fails at that.
uses
Winapi.GDIPAPI,
Winapi.GDIPOBJ;
procedure InitGDIP;
begin
// Initialize StartupInput structure
StartupInput.DebugEventCallback := nil;
StartupInput.SuppressBackgroundThread := False;
StartupInput.SuppressExternalCodecs := False;
StartupInput.GdiplusVersion := 1;
GdiplusStartup(gdiplusToken, @StartupInput, nil);
end;
procedure FreeGDIP;
begin
if Assigned(GenericSansSerifFontFamily) then
GenericSansSerifFontFamily.Free;
if Assigned(GenericSerifFontFamily) then
GenericSerifFontFamily.Free;
if Assigned(GenericMonospaceFontFamily) then
GenericMonospaceFontFamily.Free;
if Assigned(GenericTypographicStringFormatBuffer) then
GenericTypographicStringFormatBuffer.free;
if Assigned(GenericDefaultStringFormatBuffer) then
GenericDefaultStringFormatBuffer.Free;
GdiplusShutdown(gdiplusToken);
end;
procedure ShowMainForm; stdcall;
var
FormMain: TFormMain;
begin
InitGDIP;
Application.Title := 'DLL Form';
FormMain := TFormMain.Create(Application);
FormMain.ShowModal;
FormMain.Free;
Application.Terminate;
Application.ProcessMessages;
FreeGDIP;
end;
Form contains a button which closes the form.
procedure TFormMain.btnCloseClick(Sender: TObject);
begin
Close;
end;
Host application loads this DLL when its main form is created
procedure TFormHost.FormCreate(Sender: TObject);
begin
FLibHandle := LoadLibrary('FMXDLL');
if FLibHandle = 0 then begin
ShowMessage('Cannot load FMXDLL.DLL');
Application.Terminate;
end
else begin
FShowMain := GetProcAddress(FLibHandle, 'ShowMainForm');
if not assigned(FShowMain) then begin
ShowMessage('Missing export: ShowMainForm');
Application.Terminate;
end;
end;
end;
It has a button which shows FireMonkey form.
procedure TFormHost.Button1Click(Sender: TObject);
begin
FShowMain();
end;
DLL is unloaded when form is destroyed.
procedure TFormHost.FormDestroy(Sender: TObject);
begin
if FLibHandle <> 0 then begin
FreeLibrary(FLibHandle);
FLibHandle := 0;
end;
end;
This is observed behaviour (Delphi 10.1 Berlin running on Windows 10 Creators Edition):
- I start my host program. An icon with name "DLL Host" appears in the taskbar. [OK]
- When I click the button, FireMonkey form appears. [OK].
- This new form also has a taskbar button with a name "DLL Form". [OK]
- When I click Close button on the FireMonkey form, it closes. [OK]
- However, its taskbar button is still visible on screen! [Definitely NOT OK!]
- I can click and close FireMonkey form multiple times. It will always show correctly but its taskbar button will never disappear.
- When I close my VCL form, it disappears from the taskbar. [OK]
- The FireMonkey form is, however, still visible and program hangs. [Definitely NOT OK!] Stack shows that the code is somewhere inside
d3d11.dll
, if that can be trusted.
I tried different ways of creating and destroying the FMX form but nothing seems to be working correctly.