Writing a FireMonkey DLL for use with a VCL application.

by Oct 11, 2011

VCL App calling a FireMonkey DLL

We have had a lot of questions on the road show tour about how to mix Fire Monkey and VCL. Although this is not officially supported, a number of blogs have started to appear with ways to do this.

One way that is appropriate to add additional functionality to your VCL application is by writing a DLL library that contains your FireMonkey forms and initializing that DLL from your VCL application.

Step 1) Create a new Dynamic-Link Library project.
Step 2) Write the procedure you want to make available and add it to the exports clause in the project source, the example below is for a project with a function called SelectPicture.

library SelectImage;

{ Important note about DLL memory management: ShareMem must be the
first unit in your library's USES clause AND your project's (select
Project-View Source) USES clause if your DLL exports any procedures or
functions that pass strings as parameters or function results. This
applies to all strings passed to and from your DLL–even those that
are nested in records and classes. ShareMem is the interface unit to
the BORLNDMM.DLL shared memory manager, which must be deployed along
with your DLL. To avoid using BORLNDMM.DLL, pass string information
using PChar or ShortString parameters. }

uses
FMX.Forms,
System.SysUtils,
System.Classes,
formImages in '..\formImages.pas' {frmImages}; // This is our FireMonkey form

{$R *.res}

exports
SelectPicture;

begin
end.

Step 3) Write the function being exported from the DLL. (SelectPicture).

function SelectPicture(AFolder : ShortString): ShortString; export;
…..
function SelectPicture(AFolder: ShortString): ShortString;
var
frm: TfrmImages; // This is the FireMonkey form added into the DLL
begin
frm := TfrmImages.Create(Application);
try
frm.LoadImages(AFolder);
if (frm.ShowModal = mrOK) then
Result := frm.FCurrentImage
else
Result := 'None Selected';
finally
frm.Free;
end;
end;

Now the DLL is written, you need to link the DLL into you VCL application, to do this there a few important steps.

Step 4) The following code is a unit written in the VCL application that will enable you to load in the FireMonkey DLL Dynamically.

unit unitLoadDLL;

interface

uses Windows, Dialogs;

type
TSelectImage = function(AFolder : ShortString): ShortString;

var
SelectImage : TSelectImage = nil;
DllHandle : THandle;

implementation

initialization

if DllHandle = 0 then begin
DllHandle := LoadLibrary('..\..\..\DLL\Win32\Debug\SelectImage.dll'); // loads the DLL if it exists
if DllHandle > 0 then begin //succesfully loaded the DLL
@SelectImage := GetProcAddress(DllHandle,'SelectPicture');//map the functiuon in the DLL
end
else begin
MessageDlg('Select Image functionality is not available', mtInformation, [mbOK], 0);
end;
end;

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

Step 5) Once the DLL is loading when you run the application, you are then able to call it using the SelectImage variable. Firstly add to the uses the following two uses.

uses unitLoadDLL, Winapi.GDIPOBJ;

The second uses declaration is a bit of a surprise initially, but once you understand that GDI+ can only be called from a main application and not from a DLL, it starts to make sense.

Also see http://msdn.microsoft.com/en-us/library/ms534077(v=vs.85).aspx

If you forget to add in the Winapi.GDIPOBJ call you will get an Access Violation as the Delphi RTL enforces that GdiPlusStartup is explicitly skipped if it is called from a Library to confirm with the GDI+ recommendations.

If you want to use it, it is necessary for developers to start up GDI+ from their host application instead, hence the addition of Winapi.CDIPOBJ.

Step 6) Finally, reference the function that you have created

procedure TForm1.Button1Click(Sender: TObject);
begin
if not OpenDialog1.Execute then
Exit

if DllHandle > 0 then
lblImage.Caption := SelectImage(OpenDialog1.FileName)
else
MessageDlg('DLL Not loaded',mtInformation,[mbOK],0);
end;