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.
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:
- MenuItem – Assinatura para se registrar em um item de menu do HOST;
- ToolbarItem – Assinatura para se registrar em um item da Toolbar;
- 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)
….