VCL Host

From RemObjects Wiki
Jump to: navigation, search

This is an Article about Hydra for Delphi


(This page is considered "good" on a technical level, but is pending review for grammar and typos)


Hydra host is an application that provides an ability to load (host) and use exposed functionality of a plugin. Host applications is able to use all plugins supported by the Hydra.

VCL is a visual component-based framework, VCL hosts is is one of the most used types of host applications. Also VCL is our oldest supported framework and thus we providing some of features that is available only for this framework.

In this article we will describe how to create a new VCL host application, and talk about what features it provides and how they can be used.

Contents

Getting Started

Hydra host application, basically, is a common application. Our wizards can create this application for every supported platforms, so in result you can have a WinForms, VCL or FireMonkey HD applications.

By default there is not much changes in these applications is done by wizard, the main one is that it adds a module manager component which allows you to interact with plugins. However for some host project it adds additional items, they will be described in the "Review the resulting project" section.

All this allows you easily convert your existing project into the Hydra host project.

Now we can describe how to create and setup a Hydra host project.

Working with Wizard

Creating a Hydra host is a very simple process which is entirely automated by our IDE wizards.

The following screenshots will show you all the steps required to create a new Hydra host application.

To start a wizard that will help you to create a new host go to the menu File -> New -> Other and select the RemObjects Hydra category, in this category select Host Application option:

Visual Plugins VCL 01.png

The New Hydra Host Project wizard will start and you will be presented with a Welcome screen.

HY FMX 12.png

Next page allows you to set destination folder, project name and a platform of a host:

Host VCL 01.png

Wizard allows you to choose between FireMonkey and VCL frameworks, to create a VCL host you will need to select the VCL option.

After everything is set, wizard will show a summary page:

Host VCL 02.png

In the end of the work wizard will create a new project that contains a host application and show a dialog that allows you to choose runtime package settings:

Visual Plugins VCL 13.png

We will describe working with runtime packages later in the article.

After finishing its work wizard will create a new application that is ready to work with Hydra plugin modules.

Review the resulting project

Let us review the resulting project:

  • Project file - wizard will create a *.dpr file and if needed additional project files (like *.dproj). This is a regular VCL project file, wizard doesn't add anything specific for this one.
  • Main file - this file contains a main form definition:

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, uHYModuleManager, uHYIntf;

type
  TMainForm = class(TForm)
    HYModuleManager1: THYModuleManager;
  private
    { Private declarations }
  public
    { Public declarations }
  end;

As you can see, this is peaty much looks like regular VCL application, wizard adds some units into the uses section, these units contain definition of most common items like interfaces or enums.

Also it adds a definition of a module manager, we will describe working with a module manager later in this article.

Using the host

By now you will have a complete project that with a little bit of an additional work can be used to work with plugins. A host application can load any plugin module, regardless of what platform it was developed with, allowing you to mix Delphi and .NET functionality in the same application, further in this article we will describe how to do this.

The host application we've showed is a common VCL application, the main form generated by a wizard is inherited from standard TForm and provides exactly the same design surface, so you can work with the host application like you do with any regular VCL application.

Below we will describe the most common way to use the host, but first let us make a note. One of the major parts of the Hydra framework is custom interfaces. Custom interfaces are user defined interfaces that can be used in the cross-platform environment (for example they can be shared between FireMonkey host and a .NET plugin) to receive access to a data or to call host or plugin methods. When we talk about things like "accessing host methods" we will refer to the custom interfaces, but since this is large topic we can't cover it in this article, so if you feel like you need to use them, please take a look at these two articles:

Also there are two VCL only things that we won't cover in this article:

  • RemObjects SDK Plugins - this is special type of plugin/host combination that allow you to mix Hydra and RemObjects SDK.

Please follow the links to learn more about these features.

Module Manager Members

For host applications, the THYModuleManager is a key component that allows you to perform the task of managing plugin modules. So, let us start by giving a short description of module manager members:

Methods:

  • GetModuleFileNames - uses the specified search path to populate a list with module filenames and returns a number of modules that were found.
  • CreateInstance - creates an instance of a plugin with a specified name. CreateInstance returns a reference to an IInterface, however if you want you can use CreateVisualPlugin or CreateNonVisualPlugin methods to create a specific plugin.
  • ReleaseInstance - releases an instance that was created by the CreateInstance method, this is equivalent to setting instance to nil. You need to release all the references to a plugin instance before you can unload a module or shutdown your application.
  • LoadModule - loads a module with a specified file name, optionally can create a new AppDomain for .NET plugins or use a global one. Returns an index of a new module. This method will automatically define a plugin module type and will call on the module manager methods to load specific module, however you can do this manually by using methods such as LoadUnmanagedModule, LoadFiremonkeyModule etc.
  • LoadModules - provides an ability to load a set of modules with different methods like: by search path, from list of file names, etc.
  • UnloadModule - unloads a plugin module by using a reference to the module, index or file name. Also there is UnloadModules method that allows you to unload all modules.
  • FindModule - searches for a loaded module by a specified file name, returns nil if nothing is found. Also there is ModuleByFileName that does the same search, but will throw an exception if the module wasn't found.
  • FindPluginDescriptor - searches for a plugin descriptor by specified name, returns nil if nothing is found. Also there is PluginDescriptorByName that does the same search, but will throw an exception if descriptor wasn't found.

Properties:

  • PluginDescriptors - returns a reference to a plugin descriptor by specified index.
  • PluginDescriptorCount - returns the number of plugin descriptors in all modules.
  • Modules - returns a reference to a loaded module by specified index.
  • ModuleCount - returns the number of loaded modules.
  • ModulesToLoad and AutoLoad - first is a string list that allows you to define a list of modules that can be loaded, second defines whether they will be loaded automatically after module manager is created.

Just a description, we will give details on these two later:

  • CustomImplementer - allows you to set a reference to an IInterface that will receive calls from plugins.
  • ResolveInterfacesToOwner - defines whether host will resolve interfaces to his owner.

Events:

Set of events that reacts on various load/unload events:

  • OnBeforeLoadModule
  • OnAfterLoadModule
  • OnBeforeUnloadModule
  • OnAfterUnloadModule
  • OnLoadModuleError

Events that react on instance creation:

  • OnBeforeCreateInstance
  • OnAfterCreateInstance

Following events used only for Silverlight plugins (you can read more about them in the this article:

  • OnGetAutomationObject
  • OnNotifySilverlightError

Modules and Descriptors

As you can see module manager provides a couple of methods that allows you to access the modules and descriptors. So let's find out what this is.

Every plugin that was loaded by the module manager is represented by a special class that describes this module. All modules are descendants of the THYModule and allows you to get access to module data.

THYModule allows you to access to following data:

  • FileName - file name of the loaded module.
  • ModuleController - holds reference to an instance of a plugin's module controller (IHYCrossPlatformModuleController).
  • Handle - handle of a module, can be zero if module doesn't have a handle.

Plugin descriptors is used to describe a plugin that is stored in the module. In most cases you will use THYPluginDescriptor class to deal with the descriptors.

THYPluginDescriptor provides access to a set of data that describes plugin:

  • PluginType - defines type of the module (visual, non visual, etc.).
  • Name - holds name of the plugin, you can use it to create an instance of a plugin.
  • MajorVersion and MinorVersion - holds user specified version of a plugin.
  • Description - holds a string that allows you to read the plugin description.
  • UserData - string that stores custom data.
  • RequiredPrivilege - string that defines privileges required to create this plugin.
  • CheckPluginType - method that allows you to check if plugin is derived from a specified type.
  • CheckPluginInterface - method that allows you to check if plugin implements a specified interface.

Please note that MajorVersion, MinorVersion, Description and UserData is optional fields, they can be defined on the plugin side if you need such info, by default they are empty.

Custom Interfaces

We won't dig deep into details of custom interfaces usage (please refer to this article - Passing interfaces between Host and Plugins), but we will describe how host deals with them.

The THYModuleManager itself implements a couple of base hosting interfaces to be able to deal with plugins, and provides a couple of ways for you to expose your own methods or properties to plugins.

First let us review how host reacts when plugin tries to get access to it:

  1. First it checks if it already implements requested interfaces, this is for general interfaces like IHYCrossPlatformHost.
  2. If plugin request for different interface, then if ResolveInterfacesToOwner is enabled it will request its owner to resolve an interface.
  3. If interface is still not resolved and CustomImplementer is set, it will forward the call to it.
  4. Otherwise it will return an error code to a plugin.

By default ResolveInterfacesToOwner is enabled so most common way to implement your custom interfaces is to use an owner object (in most cases this is TForm) and declare interfaces there, for example:

TMainForm = class(TForm, IMyCustomInterface)
  ModuleManager: THYModuleManager;
[..]
private
  procedure MyInterfaceMethod; safecall;
[..]

In some cases (like when you don't have a parent form), you may use a custom implementor which is a bit more complex. Module manager requires that CustomImplementor object implements IInterface, so we can call a QueryInterface method on this object, but if you need this object to also implement an interface then it must also implement IDispatch. For this purpose you can use THYFakeIDispatch class that can be used as a base class for an object that implements custom interfaces, for example:

uses
 uHYInterfaceHelpers;

[..]
TMyImplementor = class (THYFakeIDispatch, IMyCustomInterface)
private
  procedure MyInterfaceMethod; safecall;
end;
[..]

  ModuleManager.ResolveInterfacesToOwner := false;
  ModuleManager.CustomImplementer := TMyImplementor.Create;


Usage Example

While plugins do not require any special setup, you need to perform a couple of steps in the host application to be able to load your plugins, for example:

TMainForm = class(TForm)
  [..]
  private
    { Private declarations }
    fVisualPlugin: IHYVisualPlugin;
  end;

[..]
procedure TMainForm.FormCreate(Sender: TObject);
begin 
  ModuleManager.LoadModule('ModuleName.dll');
  ModuleManager.CreateVisualPlugin('VisualPluginName', fVisualPlugin, Panel1);
end;

procedure TMainForm.FormDestroy(Sender: TObject);
begin
  ModuleManager.ReleaseInstance(fVisualPlugin);
  ModuleManager.UnloadModules;
end;

Let's review this example step-by-step:

  • fVisualPlugin: IHYVisualPlugin; - this is a reference to an instance of a plugin. You can use it to perform any specific actions with a plugin. Also you need to keep this reference alive until you use a plugin and release it when the plugin is no longer needed.
  • ModuleManager.LoadModule('ModuleName.dll'); - this method allows you to load a plugin module with a specified path. Module manager will automatically detect the type of a module and will use the appropriate method. Module manager also provides a couple of methods for module loading, such as loading from a list of file names or loading using a search pattern.
  • ModuleManager.CreateVisualPlugin('VisualPluginName', fVisualPlugin, Panel1); - this method will create an instance of a plugin with the specified name and assign this instance to a fVisualPlugin variable that we defined above. Also this method can show plugin content in a container that in this example represented by Panel1. Please note that this method is used only for creating an instance of a visual plugin, for non visual plugins you can use ModuleManager.CreateNonVisualPlugin.
  • ModuleManager.ReleaseInstance(fVisualPlugin); - releases an instance of a plugin. Please note that you must release an instance of a plugin before the plugin module is unloaded.
  • ModuleManager.UnloadModules; - this method unloads all modules that was previously loaded by a module manager. Module manager also provides methods for unloading a specific plugin by its file name or index. Please note that there is no need to call UnloadModules on application end since module manager will do this operation on its own destruction.

This is it, with just a few lines of code, a host is able to load, create and show your visual plugin.

Runtime Packages

Hydra provides an easy way to enable runtime packages support for your project by using Project Package Settings dialog. You saw this dialog appear when you've created your project, but you also can run this manually from Hydra -> Hydra Package Settings menu:

Visual Plugins VCL 14.png

It allows you to select one of the four options:

  • Build with Hydra Core Packages - will enable runtime packages and set them to standard vcl and rtl and also add Hydra core packages (version specific) to the list.
  • Build with Hydra and RemObjects SDK Core Packages - same as first option but also will add a RemObjects SDK core package to the list.
  • Build with Custom Package List - allows you to manually enter a set of required packages.
  • Don't Build with Packages - turns runtime package support off.

The VCL host applications only requires runtime packages when they use VCL plugins, if this is your case you need to select the first option. If you will use this host only with .NET or FireMonkey plugins then you can build without runtime packages.

Other Articles

Product Articles Data Abstract RemObjects SDK  


Product: RemObjects Hydra
Current version: Hydra 4.0

GlossaryArchitectureArticlesLibrarySamples