WinForms Host (.NET)

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

This article describes a WinForms host application and features it provides.

Getting Started

A Hydra host application is basically a common WinForms application.

The main component added by the Host application template is the module manager component that allows to interact with plugins. However, for some host projects it adds additional items, which will be described in the "Review the resulting project" section.

New Application Template

Hydra provides Host application template for all supported langugages, including C#, VB.NET, Hydrogene, Oxygene and Silver for .NET:

The Hydra Host Application template will create a new application that is ready to work with Hydra plugin modules.

Choose Hydra Host Application (.NET Core) or Hydra Host Application (.NET Framework) template.

The resulting project

As The host application is a common Windows Forms Application, so the resulting application is very close to the one created by a regular template:

  • Program.cs - The entry point of the application; contains code that runs the application and shows its main form.
  • Main.cs - Main form of the application, a regular WinForms class.

The template also adds a new component to the main form, the module manager - a key component that allows to manage plugins.

One more thing: in the AssemblyInfo.cs the following assembly atttribute should be set:

[assembly: ComVisible(true)]

This attribute allows plugins to access the interfaces exposed by the host application.

Using the host

The host application can load any plugin module, regardless of what platform it was developed with, allowing to mix Delphi and .NET functionality in the same application.

The host application is a common WinForms application, the main form generated by the project template is inherited from the standard System.Windows.Forms.Form class and provides exactly the same design surface, so one can work with the host application like any regular WinForms application.

Below the most common way to use the host described.

But first a quick look at the custom interfaces conception is needed. Custom interfaces are one of the major parts of the Hydra framework. They are user-defined interfaces that can be used in the cross-platform environment (they can, for example, be shared between a FireMonkey host and a .NET plugin) to receive access to data or to call host or plugin methods. The following article covers this topic more detailed: Passing Interfaces between Host and Plugins.

For host applications, the ModuleManager component is a key component that allows to perform the task of managing plugin modules.

Module Manager Members

The ModuleManager component provides the following methods:

Methods:

  • GetModuleFileNames - Uses the specified search path to populate a list with module filenames and returns the number of modules that were found.
  • CreateInstance - Creates an instance of a plugin with a specified name. CreateInstance returns a reference to a IHYCrossPlatformPlugin. It is also possible to use the CreateVisualPlugin or CreateNonVisualPlugin methods to create a specific plugin kinds.
  • ReleaseInstance - Releases an instance that was created by the CreateInstance method. This is equivalent to setting the instance to null. All references to a plugin instance should be released before unloading its module or shutting down the host application.
  • LoadModule - Loads a module with a specified file name, and optionally creates a new AppDomain for .NET plugins (for Delphi hosts). Returns an index of the new module. This method will automatically define the plugin module type and will call on the module manager methods to load the specific module.
  • LoadModules - Provides the 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. The UnloadModules method allows to unload all modules.
  • FindModule - Searches for a loaded module by a specified file name, returns null if nothing is found. ModuleByFileName does the same search, but will throw an exception if the module wasn't found.
  • FindPluginDescriptor - Searches for a plugin descriptor by the specified name, returns null if nothing is found. PluginDescriptorByName does the same search, but will throw an exception if the descriptor wasn't found.

Events that are raised during the module loading process:

  • ModuleLoading
  • ModuleLoaded

Modules and Descriptors

As mentioned above, the module manager provides a couple of members that allows to access the modules and descriptors.

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 LoadedModule class and allow to get access to module data.

LoadedModule allows to access the following data:

  • FileName - File name of the loaded module
  • ModuleController - Provides a reference plugin's module controller (IHYCrossPlatformModuleController).
  • Plugins - Allows to access the list of plugin descriptors. Unlike the similar module manager property, it allows to get descriptors for this specific module only.

Plugin descriptors are used to describe a plugin that is stored in the module.

PluginDescriptor provides access to a plugin description metadata:

  • Name - Name of the plugin. Can be used to create an plugin instance.
  • MajorVersion and MinorVersion - User-specified version of the plugin.
  • Description - Holds the plugin description.
  • UserData - String that stores custom plugin metadata.
  • RequiredPrivilege - String that defines privileges required to create this plugin.
  • CheckPluginInterface - Method that allows to check if the plugin implements a specified interface.

Please note that MajorVersion, MinorVersion, Description and UserData are optional fields. Their default values are empty strings.

Custom Interfaces

Please refer to this article for more details: Passing interfaces between Host and Plugins). This setion will bprovide only a brief overview of custom interfaces feature.

Basically the host object is an object that implements the IHYCrossPlatformHost interface. This interface doesn't have any methods, it just shows that the object is a host.

In most cases this interface is implemented by application main form, f.e. this:

public partial class Main : Form, IHYCrossPlatformHost
{
    public Main()
    {
        InitializeComponent();
    }
}

It is also possible to have a dedicated host object. The hosat instance should be provided to to plugin instances. There are a couple of ways to do this, the first is to assign it to the module manager's Host property:

moduleManager.Host = this; //in case when your main form is a host object

This will allow module manager to assign it to every module that is being loaded and to every plugin instance created. The second way is to assign it individually to plugin and module controller instances:

var module = moduleManager.LoadModule("Plugin.dll");
module.ModuleController.Host = this;

...

var instance = moduleManager.CreateInstance("MyPlugin");
instance.Host = this;

Plugin Loading

While plugins do not require any special setup, several steps should be performed to allow loading plugins by the host application:

public partial class Main : Form, IHYCrossPlatformHost
{
    public Main()
    {
        InitializeComponent();
    }

    private IHYCrossPlatformPlugin _instance = null;

    private void Main_Load(object sender, EventArgs e)
    {
        moduleManager.LoadModule("Plugin.dll");
        _instance = moduleManager.CreateInstance("MyPlugin");
        hostPanel1.HostPlugin(_instance as IBasePlugin);
    }
}

This code sample shows several points of interest:

  • IHYCrossPlatformPlugin _instance - This is a reference to an instance of the plugin. It can be used to perform any specific actions with a plugin. If a specific plugin interface is needed an explicit cast is required:
if (_instance is IHYCrossPlatformNonVisualPlugin)
{
    (_instance as IHYCrossPlatformNonVisualPlugin).Start();
}
  • moduleManager.LoadModule("Plugin.dll"); - This method call loads a plugin module with a specified path. Module manager will automatically detect the type of the module and will use the appropriate method.
  • _instance = moduleManager.CreateInstance("MyPlugin"); - This method call creates an instance of the plugin with the specified name and assigns this instance to the *_instance* class field defined above.
  • hostPanel1.HostPlugin(Instance as IBasePlugin); - This method is used for visual plugins. It displays the plugin content in a special container named HostPanel.