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 new host go to 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 VCL option.

After everything is set, wizard will show 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 dialog that allows you to choose runtime package settings:

Visual Plugins VCL 13.png

We will describe work with runtime package 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 unit into the uses section, these unit contains definition of most common items like interfaces or enums.

Also it adds a definition of a module manager, we will describe work with 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.

As we've showed host application is a common VCL application, 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 a most common way to use host, but first let us make a note. One of the major part of the Hydra framework is a custom interfaces. Custom interfaces is a 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 will 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 is 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 modules filenames and returns a number of modules that was found.
  • CreateInstance - creates an instance of a plugin with 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 an equivalent to setting instance to nil. You need to release all the references to a plugin instance before you can unload module or shutdown your application.
  • LoadModule - loads a module with 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 plugin module by using reference to a module, index or file name. Also there is UnloadModules method that allows you to unload all modules.
  • FindModule - searches for a loaded module by specified file name, returns nil if nothing is found. Also there is ModuleByFileName that does same search, but will throw an exception if 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 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 a number of plugins descriptors in all modules.
  • Modules - returns a reference to a loaded module by specified index.
  • ModuleCount - returns a 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 i 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 couple of methods that allows you to access to the modules and descriptors. So let's find out what is this.

Every plugin that was loaded by the module manager is represented by a special class that describes this module. All modules is 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 plugin description.
  • UserData - string that sores custom data.
  • RequiredPrivilege - string that defines privileges that 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 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 couple base hosting interfaces to be able to deal with plugins, and provides couple of ways for your 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 a 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 a 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 implements a 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 does not require any special setup, you need to perform couple steps in a 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 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 type of a module and will use 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 search pattern.
  • ModuleManager.CreateVisualPlugin('VisualPluginName', fVisualPlugin, Panel1); - this method will create an instance of plugin with 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 plugin 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 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 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 appears 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 with a VCL plugins, if this is your case you need to select the first option. If you will use this host with the .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