FireMonkey Sample

Since FireMonkey is a cross-platform framework, it's not bound to Windows and it doesn't directly use things like the Windows message loop or handles. Being a GPU powered framework, FireMonkey uses GDI+, Direct2D/Direct3D or OpenGL to render the GUI, meaning that by default, FireMonkey and the VCL will not get along well, and you won't be able to add a FireMonkey control to a VCL application or vice versa, or even share FireMonkey and VCL forms within the same application.

The FireMonkey sample demonstrates how Hydra can mix FMX and VCL in the same application.

To build this sample you will require Hydra version 4 or higher and Delphi XE2 or higher.

Examine the Plugins

The sample contains two plugin modules, one for VCL and one for FireMonkey. Each plugin module contains two plugins, one visual and one non visual.

You may notice that the non-visual plugins provide event handlers for a set of events that can be called by the host application:

  • OnStart
  • OnStop
  • OnPause
  • OnResume

These events can be triggered from the host by calling the corresponding methods (Start, Stop, etc).

If you need more information on how to create plugins in Delphi, please refer to these articles:

Examine the Hosts

There are also two host applications, one for VCL and one for FireMonkey.

For more information on how to create a host in Delphi, please refer to these articles:

Let's take a look at the major parts:

procedure TMainForm.FormCreate(Sender: TObject);
begin
  ModuleManager.LoadModules('*Plugin*.dll');
  ListPlugins;
end;

Note how we load the plugins, @@@call to a ModuleManager.LoadModules('*Plugin*.dll') - will load all dll files that matches the search pattern@@@.

After the plugin is loaded, we can create an instance per user request:

procedure TMainForm.CreateButtonClick(Sender: TObject);
begin
  if Instance <> nil then
    ModuleManager.ReleaseInstance(Instance);

  if PluginBox.ItemIndex >= 0 then begin
    ModuleManager.CreateInstance(PluginBox.Text, Instance);
    ShowPlugin;
  end;
end;

First, we call the ModuleManager.ReleaseInstance(Instance). This method releases the previous instance of the plugin. Now we can create a new one with ModuleManager.CreateInstance(PluginBox.Text, Instance). This method will create an instance of type@@@. So to work with this instance, we need to convert it to either a visual or a non-visual plugin.

  TMainForm = class(TForm)
  [...]
    function GetNonVisualPlugin: IHYNonVisualPlugin;
    function GetVisualPlugin: IHYVisualPlugin;
  public
    property VisualPlugin: IHYVisualPlugin read GetVisualPlugin;
    property NonVisualPlugin: IHYNonVisualPlugin read GetNonVisualPlugin;
  end;

function TMainForm.GetNonVisualPlugin: IHYNonVisualPlugin;
begin
  Supports(Instance, IHYNonVisualPlugin, Result);
end;

function TMainForm.GetVisualPlugin: IHYVisualPlugin;
begin
  Supports(Instance, IHYVisualPlugin, Result);
end;

The Supports method will check if the instance implements the specified interface, and if it does, a reference to the plugin will be assigned to Result.

The last interesting part is the ShowPlugin method, it performs GUI setup to enable/disable buttons for non-visual plugins, and displays content for visual plugins. Let's take a look:

procedure TMainForm.ShowPlugin;
begin
  StartButton.Visible := NonVisualPlugin <> nil;
  PauseButton.Visible := StartButton.Visible;
  StopButton.Visible := StartButton.Visible;
  ResumeButton.Visible := StartButton.Visible;
  Label1.Visible := StartButton.Visible;

  if VisualPlugin <> nil then
    VisualPlugin.ShowParented(PluginContainer);
end;

Please note how we show the visual plugin: By calling the ShowParented method, we can show the plugin content @@@of a descendant (in case of VCL) or descendant (in case of FMX)@@@.

Putting It All Together

Now that we've examined all major parts of the sample, we can build both plugin and host. And after we launch the sample, we should get the following result:

Concepts Covered