Non-Visual Plugins (Island)

In this article we will describe how to create a new non-visual plugin, and talk about what features it provides and how they can be used.

Getting Started

Before we start to create our first plugin, let's look at what a Hydra plugin project involves.

A Hydra plugin project consists of three parts:

  • Plugin Module - A Class Library project that acts as a container for a module controller and can hold a set of plugins.
  • Module Controller - An entry point of a module. The module controller supplies the host with information about stored plugins and provides methods that allow the host to instantiate and work with plugins.
  • Plugins - Special classes stored inside the plugin module.

Module Controller and simple plugin will be created automatically.

Review the project

Now let's review the project. It has three parts:

Plugin Module

The plugin module is a Class Library project that will produce an assembly file that can be used by any of the supported host platforms.

Module Controller

The module controller is a class that acts as an entry point for the plugin module. The host will instantiate this class on module load to get information about the plugins.

The template generates the following code for the module controller:

type
  [ComObject]
  ModuleController1 = public class(ModuleController, IUnknown, IDispatch, IHYCrossPlatformInterface, IHYCrossPlatformModuleController, IHYCrossPlatformModule)
  end;
[ComObject]
public class ModuleController1 : ModuleController, IUnknown, IDispatch, IHYCrossPlatformInterface, IHYCrossPlatformModuleController, IHYCrossPlatformModule
{
}
@ComObject
public class ModuleController1 : ModuleController, IUnknown, IDispatch, IHYCrossPlatformInterface, IHYCrossPlatformModuleController, IHYCrossPlatformModule {
}
@ComObject
public class ModuleController1 extends ModuleController implements IUnknown, IDispatch, IHYCrossPlatformInterface, IHYCrossPlatformModuleController, IHYCrossPlatformModule {
}

The module controller is inherited from ModuleController.

NonVisual Plugins

Non-Visual plugins are components that can be used from a host application and don't provide a GUI. The code generated by the template looks like this:

type
  [ComObject, Used, Plugin, NonVisualPlugin]
  IslandPlugin = public class(NonVisualPlugin, IUnknown, IDispatch, IHYCrossPlatformInterface, IHYCrossPlatformPlugin, IHYCrossPlatformNonVisualPlugin)
  public

    method DoStart; override;
    begin
    end;

    method DoStop; override;
    begin
    end;

    method DoPause; override;
    begin
    end;

    method DoResume; override;
    begin
    end;
    
  end;
[ComObject, Used, Plugin, NonVisualPlugin]
public class IslandPlugin : NonVisualPlugin, IUnknown, IDispatch, IHYCrossPlatformInterface, IHYCrossPlatformPlugin, IHYCrossPlatformNonVisualPlugin)
{
    public override void DoStart()
    {
    }

    public override void DoStop()
    {
    }

    public override void DoPause()
    {
    }

    public override void DoResume()
    {
    }   
}
@ComObject, @Used, @Plugin, @NonVisualPlugin
public class IslandPlugin : NonVisualPlugin, IUnknown, IDispatch, IHYCrossPlatformInterface, IHYCrossPlatformPlugin, IHYCrossPlatformNonVisualPlugin) {

    public override func DoStart() {
    }

    public override func DoStop() {
    }

    public override func DoPause() {
    }

    public override func DoResume() {
    }   
}
@ComObject, @Used, @Plugin, @NonVisualPlugin
public class IslandPlugin extends NonVisualPlugin implements IUnknown, IDispatch, IHYCrossPlatformInterface, IHYCrossPlatformPlugin, IHYCrossPlatformNonVisualPlugin) {

    @Override
    public void DoStart() {
    }

    @Override
    public void DoStop() {
    }

    @Override
    public void DoPause() {
    }

    @Override
    public void DoResume() {
    }   
}

Please do not remove the any attributes, they are required for correct functionality.

As in the module controller you can set the meta-data manually:

[ComObject, Used, Plugin(Name := 'SamplePlugin', Description := 'This is sample plugin', UserData := 'Data'), NonVisualPlugin]
IslandPlugin = public class(NonVisualPlugin, IUnknown, IDispatch, IHYCrossPlatformInterface, IHYCrossPlatformPlugin, IHYCrossPlatformNonVisualPlugin)
end;
[ComObject, Used, Plugin(Name = "SamplePlugin", Description = "This is sample plugin", UserData := "Data"), NonVisualPlugin]
public class IslandPlugin : NonVisualPlugin, IUnknown, IDispatch, IHYCrossPlatformInterface, IHYCrossPlatformPlugin, IHYCrossPlatformNonVisualPlugin
{
}
@ComObject, @Used, @Plugin(Name = "SamplePlugin", Description = "This is sample plugin", UserData := "Data"), @NonVisualPlugin
public class IslandPlugin : NonVisualPlugin, IUnknown, IDispatch, IHYCrossPlatformInterface, IHYCrossPlatformPlugin, IHYCrossPlatformNonVisualPlugin {
}
@ComObject, @Used, @Plugin(Name = "SamplePlugin", Description = "This is sample plugin", UserData := "Data"), @NonVisualPlugin
public class IslandPlugin extends NonVisualPlugin implements IUnknown, IDispatch, IHYCrossPlatformInterface, IHYCrossPlatformPlugin, IHYCrossPlatformNonVisualPlugin {
}

Using the Plugin

By now you will have a complete project that can be loaded by all supported host platforms without any additional work.

Both the module controller and the non-visual plugin provide a set of internal methods that are used by the Hydra framework to perform specific tasks, however, they also provide some useful methods and properties that can be used in your own application.

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

Before we start to describe the non-visual plugin itself, let's look at how to use the module controller.

How to use Module Controller

While the main purpose of the module controller is to provide information about the plugin to the host, it can also be useful for some specific task.

The host will instantiate the module controller only once on a module load, so it can be used to initialize some global data or allow the host to gain access to global methods via custom interfaces.

By default the module controller holds SmallImages and LargeImages properties. These image lists will be passed to the host application, so you can use them to store shared images.

The ModuleController also has an event called HostChanged. This is an important event if you need to be able to access host methods or data. Since the controller is initialized on module load, it doesn't have access to the Host property at the time its constructor is executed. When the host assigns its reference to the module controller, the HostChanged event is fired so you can safely get access to the host. One thing that you need to consider is that this event can be called with a nil/null host reference (when the host unloads a module), so you need to check this before accessing host members.

The last thing that is available to the module controller is the Host property. This property holds the reference to the host object, and can be used to access host methods or data.

How to use the NonVisual Plugin

Like the module controller, the NonVisualPlugin provides access to the Host property and the HostChanged event, so everything that was said above for the module controller also applies to the non-visual plugin.

The non-visual plugin provides a set of methods that can be called by the hostapplication:

  • DoStart
  • DoStop
  • DoPause
  • DoResume

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