How can I display FireMonkey modal form from a DLL?

Viewed 976

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.

1 Answers
Related