3
votes

Im trying to make a FMX form in a dll, after about 17 hours (of trying diffrent approches) i got it working, except i get a exception trying to unload the dll. I have no idea how to make it work, maybe someone could help me and point out what im doing wrong?

side note: i cant have a FMX form in my VCL application becouse of the AA drawing, i just need it on my text while drawing on a canvas and while having a FMX form on a VCL application, i dont get that cleartype on text :( im trying to make a some sort of OSD/HUD.

Project showing my problem:

exe unit1.pas

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses
  unitLoadDLL, Winapi.GDIPOBJ;

procedure TForm1.Button1Click(Sender: TObject);
begin
  showme();
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  closeme();
end;

end.

exe unitLoadDll.pas

unit unitLoadDLL;

interface

uses Windows, Dialogs;

type
  TShowme = procedure();
  TCloseme = procedure();

var
  showme : TShowme = nil;
  closeme : TCloseme = nil;
  DllHandle : THandle;

implementation

initialization

  if DllHandle = 0 then begin
    DllHandle := LoadLibrary('C:\Users\Ja\Desktop\dupa\dll\Win32\Debug\Project1.dll');
    if DllHandle > 0 then begin
      @showme := GetProcAddress(DllHandle,'showme');
      @closeme := GetProcAddress(DllHandle,'closeme');
    end
    else begin
      MessageDlg('Select Image functionality is not available', mtInformation, [mbOK], 0);
    end;
  end;

finalization
  if DLLHandle <> 0 then
    FreeLibrary(DLLHandle);
end.

dll project1.dpr

library Project1;


uses
  FMX.Forms,
  System.SysUtils,
  System.Classes,
  Unit1 in 'Unit1.pas' {Form1};

{$R *.res}

procedure showme(); stdcall export;
begin
  TForm1.showme;
end;

procedure closeme(); stdcall export;
begin
  TForm1.closeme;
end;

exports
  showme, closeme;

begin
end.

dll unit1.pas

unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Dialogs;

type
  TForm1 = class(TForm)
    Label1: TLabel;
  private
    { Private declarations }
  public
    class procedure showme();
    class procedure closeme();
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

class procedure TForm1.showme();
begin
  Form1 := TForm1.Create(Application);
  Form1.Show;
end;

class procedure TForm1.closeme();
begin
  Form1.Free;
end;

end.

EDIT (FIX):

All answers ware helpfull, but what i've done is, that the GDI+ was shutdown BEFORE the dll unload... that appear's to be the problem.

new unitLoadDll.pas

unit unitLoadDLL;

interface

uses Windows, Dialogs;

type
  TShowme = procedure();
  TCloseme = procedure();

var
  showme : TShowme = nil;
  closeme : TCloseme = nil;
  DllHandle : THandle;

  function LoadLib : Boolean;
  procedure UnloadLib;

implementation

function LoadLib : Boolean;
begin
  if DllHandle = 0 then begin
    DllHandle := LoadLibrary('C:\Users\Ja\Desktop\dupa\dll\Win32\Debug\Project1.dll');
    if DllHandle > 0 then begin
      @showme := GetProcAddress(DllHandle,'showme');
      @closeme := GetProcAddress(DllHandle,'closeme');
    end
    else begin
      MessageDlg('Select Image functionality is not available', mtInformation, [mbOK], 0);
    end;
  end;
  Result := DllHandle <> 0;
end;

procedure UnloadLib;
begin
  if DLLHandle <> 0 then begin
    FreeLibrary(DLLHandle);
    DllHandle := 0;
  end;
end;

initialization
  LoadLib;

finalization
  UnloadLib;
end.

new unit1.pas

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Winapi.GDIPOBJ;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses
  unitLoadDLL;

procedure TForm1.Button1Click(Sender: TObject);
begin
  showme();
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  closeme();
end;

end.

in unit1.pas i moved the Winapi.GDIPOBJ to "uses" just after interface directive, and it worked...

Thank you all for your answers! See you soon! very soon...

1
What is your underlying problem?David Heffernan
Another similar question which may help. stackoverflow.com/questions/8563938/…Shannon Matthews
It seems to me that you life would be a lot easier if you did this with VCL/Win API rather than trying to force FMX into your VCL app.David Heffernan
This article was posted as a link only answer which may or may not be deleted community.embarcadero.com/blogs/entry/…Stephen Kennedy

1 Answers

0
votes

Does it help if you import sharemem on both sides?

You are not using packages, so both sides probably have an own instance all RTL state, as well as VMT tables (though that is only a problem with certain IS and AS cases). And the memory manager is RTL state :-)