Delphi – Criando uma interface Plugin (Parte 1)

Introdução
Tomando emprestado o WIKI “plugin” é um componente computacional que adiciona recursos a um programa existente. Quando um programa suporta “plugins” ele permite ser customizado para responder a necessidades não previstas no projeto original.

Uma interface de “plugin” deve prever a possibilidade de um conjunto de código ou janela permitir ser inserida em partes do programa principal.

Em um primeiro instante – é comum encontrar “plugins” que publicam alguma função ou procedimento a ser chamado pelo aplicativo principal. Este modelo limita as funcionalidade dos “plugins” que em geral ficam mais estáticos a serem um item de menu ou uma ou outra funcionalidade.

O “plugin” que irá assinar algum serviço do aplicativo

A idéia que motiva esta publicação é a construção de um modelo de aplicativo principal HOST que publica serviços implementados em seu código e ficam disponíveis para que os “plugins” possam assinar estes serviços.

Então o HOST se torna um “publisher” é o plugin um “subscriber”, onde o plugin que solicita a assinatura de um determinado serviço do HOST.

Com a mecânica em que o “plugin” assina aos serviços do aplicativo servidor permitirá que o “plugin” tome a decisão sobre qual tipo de serviço ele quer assinar no aplicativo principal . Um exemplo é um “plugin” assinar  para ser um item do menu –  em outros casos poderá assinar funcionalidade de uma “aba” de janela ou adicionar “frames” a alguma interface do usuário – entregando mais poder de escolha ao “plugin”.

Segurança x Flexibilidade

Ainda que “flexibilidade” seja o principal objetivo, segurança segue o mesmo caminho da flexibilidade. Há que se questionar o quanto um HOST pode ser seguro o bastante para não permitir um assinante malicioso. Não tenho resposta para a questão, já que um “plugin” maldoso poderia assinar a serviços para aplicar táticas maliciosas – questão que precisa ser avaliada.

diagrama

Uma interface para publicar serviços

Considerando que o programa principal tem um formulário:

TMeuMenu = class(TForm)

end;

o que precisamos fazer é dotar o formulário principal de uma interface que permita publicar os seus serviços:

[code lang=”pascal”]
IPluginApplication = interface
[‘{6ED989EA-E8B5-4435-A0BC-33685CFE7EEB}’]
procedure RegisterMenuItem(const AParentMenuItemName, ACaption: string; ADoExecute: IPluginMenuItem);
procedure RegisterToolbarItem(const AParentItemName, ACaption: string; ADoExecute: IPluginToolbarItem);
procedure RegisterAttributeControl(const AType,ASubType: Int64; ADoExecute: IPluginControl);
end;
[/code]

Com isto o formulário passa a implementar a interface de serviço de “plugins” da seguinte forma:

TMeuMenu = class(TForm, IPluginApplication)
….
end;

 

Registrando o Plugin para o Aplicativo Principal

Antes de carregar um “plugin” o aplicativo host precisa conhece-lo (uma lista de plugins registrados). Uma forma simplista, é guardar uma lista de “plugins” disponíveis em um arquivo INI do host. A lista é necessária para que o aplicativo possa fazer carga dos seus “plugins”. Uma vez feito a carga do plugin, estes  irão assinar os serviços a que desejam interagir com o host.

O framework utiliza a implementação de DLLs para troca de código com o aplicativo principal, publicando duas chamadas – uma para carga inicial da DLL e outra para quando o aplicativo principal esta encerrando o uso do plugin:

[code]
function LoadPlugin(AAplication: IPluginApplication): IPluginItems;
procedure UnloadPlugin;

exports LoadPlugin, UnloadPlugin;
[/code]

Carregando o Plugin
Sabedor destas duas entradas disponíveis no “plugin”, resta escrever os métodos de carga do plugin (ver TPluginManager nos fontes):

[code lang=”pascal”]
function LoadPluginService(APlugin: string;
AAppliction: IPluginApplication): Integer;
var
F: function(APApplication: IPluginApplication): IPluginItems;
H: THandle;
begin
result := -1;
H := LoadLibrary(PWideChar(APlugin));
if H > 0 then
begin
try
@F := GetProcAddress(H, ‘LoadPlugin’);
if assigned(F) then
result := LPluginManager.FPlugins.Add(H, F(AAppliction))
else
raise Exception.Create(‘Não carregou o plugin’);
except
FreeLibrary(H);
end;
end;
end;
[/code]

Note que a função “LoadPlugin” do plugin espera que seja enviado um IPluginApplication e retorna uma lista de plugins existente na mesma DLL, já que uma DLL pode conter 1 ou mais plugins.

O IPluginApplication, representa a interface do HOST que publica os serviços que os plugins poderão assinar no HOST – Internamente a DLL irá montar uma lista de plugins disponíveis. Cada plugin da DLL se encarrega de assinar os serviços do HOST.

Estendendo os serviços do HOST

A interface que publica os serviços no HOST é o IPluginApplication que já possui três serviços básicos para a assinatura sendo eles:

  1. MenuItem – Assinatura para se registrar em um item de menu do HOST;
  2. ToolbarItem – Assinatura para se registrar em um item da Toolbar;
  3. AttributeControl – Uma assinatura a utilizar o plugin como um “control” ou atributo de um janela.

Caso deseje implementar outros serviços para o HOST, estendendo suas funcionalidades é possível estender a interface IPluginApplication e implementa-la no HOST:

[code]
IMyPluginApplication  =  interface(IPluginAppliction)
procedure RegisterXXX(….);
end;

// no host
TMyMainMenu = class(TForm, IMyPluginApplication )

end;
[/code]

 

Parte 2 – Construindo o Plugin

(No final o código estará disponível no GIT)
….

3 comentários

  1. genial amigo .. há tempos penso sobre essa funcionalidade num sistema … aguardando a parte 2   Abçs e parabéns pelo artigo

  2. Gostei do artigo! Esse seu conceito de plug in deixou mais claro pra mim o conceito de módulos! Há tempos pesquiso sobre isso e pouco se tem sobre os conceitos na internet!!! Forte abraço!

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *