A programação da estrutura do plug-in requer um contêiner de plug-in para controlar a operação de cada DLL e organizar cada subsistema dividido em um arquivo de biblioteca DLL. Cada programa DLL precisa reservar funções de interface para o contêiner. Geralmente, as funções de interface incluem: funções que iniciam a chamada da biblioteca DLL e funções que fecham a biblioteca DLL. Através da função de interface, o contêiner do plug-in pode passar parâmetros ao módulo DLL para obter controle dinâmico. Explicarei os detalhes específicos da implementação e fornecerei o código de resposta abaixo.
Talvez seja necessário primeiro entender a estrutura do UNIT e a estrutura dos projetos no DELPHI. Este artigo não discute em profundidade os detalhes teóricos da programação de DLL, mas apenas demonstra alguns códigos práticos que eu estava estudando no livro "DELPHI In-Deep Programming" de Liu Yi.
Também estou no estágio introdutório do DELPHI e sinto que há algumas coisas que vale a pena discutir neste desenvolvimento de DLL, por isso estou escrevendo este artigo e espero que você possa dar sugestões generosas sobre o que não estou fazendo bem.
Introdução ao programa de exemplo
Para facilitar a leitura, utilizarei parte do código do programa de um sistema MIS para demonstrar alguns métodos de programação de plug-ins. O programa de exemplo é um aplicativo DBMS de estrutura C/S típico. Nosso foco estará nas instruções de controle do programa de estrutura (doravante denominado Hall) e no controle de resposta do programa plug-in dll.
1. Estrutura do programa
O contêiner de plug-in Hall é criado usando um projeto independente. A janela principal do Hall é equivalente ao formulário do contêiner MDI no programa MDI. As funções de interface na DLL serão explicitamente chamadas no Hall.
Cada programa plug-in usa seu próprio projeto de forma independente. Diferente dos projetos comuns, o projeto DLL cria o Dll Wizard e os arquivos gerados pela compilação correspondente possuem o sufixo DLL.
=550) window.open('/upload/20080315181507424.jpg');" src="http://files.VeVB.COm/upload/20080315181507424.jpg" onload="if(this.width>'550')this.width='550';if(this.height>'1000')this.height='1000';" border=0>
2. Design de interface
No programa de exemplo Narcissus, reservamos duas funções de interface:
MostrarDLLForm
Esta função passa o identificador do aplicativo para a janela filho da DLL e o programa DLL criará dinamicamente uma instância do formulário DLL. Você também pode passar alguma lógica de negócios para a subjanela DLL na forma de parâmetros, como o nome do formulário, o nome do usuário conectado no momento, etc. Use esta função para criar uma instância de formulário DLL quando ela for chamada pela primeira vez.
FreeDLLForm
Esta função exibirá a liberação da instância da janela DLL e chamará o método FreeDLLForm de cada formulário DLL para liberar a instância criada ao sair do aplicativo, caso contrário causará um erro somente leitura de memória. Da mesma forma, você também pode passar alguma lógica de negócios que precisa ser feita ao liberar o formulário para o formulário DLL na forma de parâmetros.
3. Método de depuração
O programa de formulário DLL não pode ser executado diretamente e precisa de um contêiner de plug-in para chamá-lo. Portanto, precisamos primeiro implementar um programa Hall básico e, em seguida, salvar Hall.exe em um diretório fixo. Faça as seguintes configurações para cada projeto DLL:
1) Abra o projeto DLL
2) Selecione o menu Parâmetros de execução
3) Navegue até nosso contêiner Hall.exe na janela pop-up
Desta forma, o programa Hall será chamado automaticamente ao depurar o programa DLL, e o programa DLL será depurado usando a interface de chamada reservada no Hall.
Implementação básica do programa plug-in
O método de design do programa DLL não é muito diferente do WINAPP comum, exceto que todas as janelas são salvas na biblioteca DLL como um "recurso" especial e precisam ser chamadas manualmente, ao contrário do WINAPP, onde os projetos são criados automaticamente. O método de declaração de funções de interface é muito simples
1) Declare a função na seção Implementação da Unidade
2) Adicione a marca stdcall no final da instrução de declaração da função
3) Antes da instrução inicial do código do projeto (Project View Source), use a instrução exports para declarar a interface da função
Para tornar o código conciso, eu pessoalmente gosto de adicionar uma unidade Unit (Arquivo Novo - Unidade) independentemente no projeto e, em seguida, definir todos os corpos de função a serem gerados nesta unidade. Unidade do formulário referenciado. Chamei esta unidade de UnitEntrance, inicializei a janela a ser exibida na função ShowDLLForm e chamei o método Show para exibi-la. HALL passará o nome do usuário logado como parâmetro. o que se reflete na inicialização da interface superior.
O código é o seguinte
unidade UnidadeEscritórioEntrada;
interface
usa
Windows, Mensagens, SysUtils, Variantes, Classes, Gráficos, Controles, Formulários;
função ShowDLLForm(AHandle: THandle; ACaption: string; AUserID: string):boolean;stdcall;
função FreeDLLForm(AHandle: THandle; ACaption: string; AUserID: string):boolean;stdcall;
implementação
usa UnitOfficialMainForm; //Muda para unidade de MAINFORM;
var
DLL_Form:TFormOfficialMain; //Muda para o nome de MAINFORM
//------------------------------------------------------
//Nome: ShowDLLForm
//Func: O plug-in DLL chama a função de entrada
//Para: AHandle identificador de programa anexado; Atítulo da legenda deste formulário;
//Rtrn: N/A
//Autenticação: CST
//Data: 3/6/2005
//------------------------------------------------------
função ShowDLLForm(AHandle: THandle; ACaption: string; AUserID: string):boolean;
começar
resultado:=verdadeiro;
tentar
Application.Handle:=AHandle; //Anexado ao contêiner principal do programa
DLL_Form:=TFormOfficialMain.Create(Application); //Muda para o NOME do MAINFORM
tentar
com DLL_Form faça
começar
Legenda := ACaption;
StatusBar.Panels.Items[0].Text := AUserID;
//Configurar UI
Mostrar ;
fim;
exceto
em e:exceção do
começar
dll_form.Free;
fim;
fim;
exceto
resultado:=falso;
fim;
fim;
//------------------------------------------------------
//Nome: FreeDLLForm
//Func: O plug-in DLL chama a função de exportação
//Para: AHandle identificador de programa anexado
//Rtrn: verdadeiro/falso
//Autenticação: CST
//Data: 11/06/2005
//------------------------------------------------------
função FreeDLLForm(AHandle: THandle; ACaption: string; AUserID: string):boolean;
começar
Application.Handle:=AHandle; //Anexado ao contêiner principal do programa
if DLL_Form.Showing then DLL_Form.Close; //Se a janela for aberta e fechada primeiro, o acionamento de FORM.CLOSEQUERY pode cancelar o processo de fechamento
se não for DLL_Form.Showing então
começar
DLL_Form.Free;
resultado:=verdadeiro;
end //Ainda aberto, indicando CLOSEQUERY.CANCLOSE=FALSE
outro
começar
resultado:=falso;
fim;
fim;
fim.
O código do arquivo do projeto DLL é o seguinte:
oficial da biblioteca;
{ Observação importante sobre gerenciamento de memória DLL: ShareMem deve ser o
primeira unidade na cláusula USES da sua biblioteca E do seu projeto (selecione
Project-View Source) Cláusula USES se sua DLL exportar algum procedimento ou
funções que passam strings como parâmetros ou resultados de funções.
aplica-se a todas as strings passadas de e para sua DLL - mesmo aquelas que
estão aninhados em registros e classes. ShareMem é a unidade de interface para a qual.
o gerenciador de memória compartilhada BORLNDMM.DLL, que deve ser implantado junto
com sua DLL. Para evitar o uso de BORLNDMM.DLL, passe informações de string.
usando parâmetros PChar ou ShortString }.
usa
SysUtils,
Aulas,
UnitOfficialDetailForm em 'UnitOfficialDetailForm.pas' {FormOfficialDetail},
UnitOfficialMainForm em 'UnitOfficialMainForm.pas' {FormOfficialMain},
UnitOfficeEntrance em 'UnitOfficeEntrance.pas',
UnitOfficialClass em '../../Public/Library/UnitOfficialClass.pas',
UnitMyDataAdatper em '../../Public/Library/UnitMyDataAdatper.pas',
UnitMyHeaders em '../../Public/Library/UnitMyHeaders.pas';
{$R *.res}
exporta ShowDLLForm,FreeDLLForm; //função de interface
começar
fim.
Depois que o programa plug-in chamar a janela DLL, a instância da janela permanecerá no topo da janela HALL, portanto não há necessidade de se preocupar com a oclusão.
Implementação de programas de contêiner
1. Introdução de funções de interface
Existem duas maneiras de chamar funções na biblioteca DLL: explícita e implícita. A chamada explícita é mais flexível, por isso usamos a chamada explícita. No Delphi, você precisa declarar o tipo de função para a função de interface e, em seguida, instanciar uma instância do tipo de função. A instância é na verdade um ponteiro para a função. Por meio do ponteiro, podemos acessar a função, passar parâmetros e obter. o valor de retorno. Adicione a declaração da classe de função na seção Interface do arquivo da unidade:
tipo
//Define o tipo de função de interface, a função de interface vem da interface DLL
TShowDLLForm = Função(AHandle:THandle; ACaption: String; AUserID:string):Boolean;stdcall;
TFreeDLLForm = Função(AHandle:THandle; ACaption: String; AUserID:string):boolean;stdcall;
As funções da biblioteca de chamada de exibição requerem as seguintes etapas:
1) Carregar arquivo de biblioteca DLL
2) Obtenha o endereço da função
3) Executar função
4) Libere a biblioteca DLL
A seguir discutiremos essas etapas em detalhes.
2. Carregue o arquivo da biblioteca DLL
A biblioteca DLL pode ser carregada na memória chamando a função da API LoadLibrary. Não discutiremos aqui o impacto da DLL no gerenciamento de memória. O parâmetro de LoadLibrary é o caminho do endereço do arquivo DLL. Se o carregamento for bem-sucedido, uma variável do tipo CARDINAL será retornada como o identificador da biblioteca DLL se o arquivo de destino não existir ou outros motivos causarem o carregamento da DLL; arquivo falhar, um 0 será retornado.
3. Instanciar funções de interface
A função API para obter o ponteiro da função da interface é GetProcAddress (identificador do arquivo de biblioteca, nome da função). Se a função for encontrada, o ponteiro da função será retornado. Se falhar, NIL será retornado.
Defina uma variável de ponteiro de função usando o tipo de função definido acima e, em seguida, use o operador @ para obter o endereço da função, para que você possa usar a variável de ponteiro para acessar a função. O código principal é o seguinte:
…
var
ShowDLLForm: TShowDLLForm; //instância da função de interface DLL
FreeDLLForm: TFreeDLLForm;
começar
tentar
começar
APlugin.ProcAddr := LoadLibrary(PChar(sPath));
APlugin.FuncFreeAddr := GetProcAddress(APlugin.ProcAddr,'FreeDLLForm');
APlugin.FuncAddr := GetProcAddress(APlugin.ProcAddr ,'ShowDLLForm');
@ShowDLLForm:=APlugin.FuncAddr;
@FreeDLLForm:=APlugin.FuncFreeAddr;
se ShowDllForm(Self.Handle, APlugin.Caption, APlugin.UserID) então
Resultado:=Verdadeiro
…
4. Um método de implementação específico
Para gerenciar plug-ins de forma estruturada e facilitar futuras expansões do sistema, podemos combinar as informações DLL disponíveis registradas no banco de dados e, em seguida, acessar dinamicamente o programa DLL consultando os registros do banco de dados.
1) Design da tabela do módulo do sistema
Para sistemas MIS, você pode usar condições DBS existentes para estabelecer uma tabela de módulos do sistema para registrar arquivos DLL e informações relacionadas mapeadas para módulos do sistema.
Tipo de função do nome do campo
Índice de AutoIDINT
modAlias módulo alias VARCHAR
modName nome do módulo VARCHAR
modWndClass forma identificador exclusivo VARCHAR
caminho da DLL do modFile VARCHAR
modMemo nota TEXTO
・Os aliases dos módulos são usados para unificar as regras de nomenclatura durante a fase de design da programação, especialmente para referência dos membros da equipe durante o desenvolvimento da equipe.
・O nome do módulo será passado como parâmetro ACAPTION para a função SHOWDLLFORM como título da janela DLL.
・O identificador exclusivo do formulário é o CLASSNAME da janela principal do submódulo DLL, que é usado para determinar a janela a ser controlada em tempo de execução.
・O caminho da DLL salva o nome do arquivo DLL, que será convertido em um caminho absoluto no programa.
2) Estrutura de dados de informações do plug-in
Definir uma interface de dados que registre informações relacionadas ao plug-in pode controlar centralmente os plug-ins DLL. Adicione o seguinte código à seção Interface:
tipo
//Define a classe de informação do plug-in
TMyPlugins = classe
Legenda:String; //título do formulário DLL
DllFileName:String; //caminho do arquivo DLL
WndClass:String; //Identificação do formulário
ID do usuário:string; //Nome de usuário
ProcAddr:THandle; //Identificador da biblioteca carregado por LOADLIBRARY
FuncAddr:Pointer; //ponteiro de função SHOWDLLFORM
FuncFreeAddr:Pointer; //ponteiro de função FREEDLLFORM
fim;
…
Crie uma instância de TMyPlugins para cada plug-in. Os métodos de inicialização para essas instâncias serão discutidos abaixo.
3) Função de carregamento de plug-ins
Neste exemplo, a janela DLL é carregada e exibida no evento que aciona a abertura da janela filho no HALL. Após o evento do botão ser acionado, primeiro determine se a DLL foi carregada de acordo com a instância da estrutura do plug-in. Caso tenha sido carregada, controle a exibição ou fechamento da janela, caso não esteja carregada, acesse a tabela de dados e; atribua os campos à estrutura do plug-in e execute o carregamento.
O código parcial é o seguinte
…
//------------------------------------------------------
//Nome: OpenPlugin
//Func: Processo de controle de informações do plug-in: Inicialização==》Definir permissões==》Carregar janela DLL
//Para: valor da permissão iFuncValue;
//Rtrn: N/A
//Autenticação: CST
//Data: 2/6/2005
//------------------------------------------------------
procedimento TFormHall.OpenPlugin(AFromActn: TAction ;APlugin:TMyPlugins; sAlias:string; sUserID:string);
var hWndPlugin:HWnd;
começar
//Determina se a janela do plug-in foi carregada hWndPlugin:=FindWindow(PChar(APlugin.WndClass),nil);
if hWndPlugin <> 0 then //A janela do plug-in foi carregada
começar
se não for IsWindowVisible(hWndPlugin) então
começar
AFromActn.Checked := Verdadeiro;
ShowWindow(hWndPlugin,SW_SHOWDEFAULT);
fim
outro
começar
AFromActn.checked := Falso;
ShowWindow(hWndPlugin,SW_HIDE);
fim;
Sair; //Sai do processo de criação do plug-in
fim;
//Inicializa a instância da classe do plug-in
se não for InitializeMyPlugins(APlugin,sAlias) então
começar
showmessage('Erro ao inicializar classe de plug-in.');
saída;
fim;
//Obtém o valor da permissão atual
APlugin.UserID := sUserID;
//Carrega janela DLL
se não for LoadShowPluginForm(APlugin) então
começar
showmessage('Erro ao carregar o plug-in do centro.');
saída;
fim;
fim;
//------------------------------------------------------
//Nome: InitializeMyPlugins
//Func: Inicializar instância MYPLUGIN (Legenda | DllFileName | IsLoaded)
//Para: APlugin-TMyPlugins
//Rtrn: N/A
//Autenticação: CST
//Data: 2/6/2005
//------------------------------------------------------
função TFormHall.InitializeMyPlugins(APlugin:TMyPlugins; sAlias:String):Boolean;
var
strSQL:string;
meuDA:TMyDataAdapter;
começar
Resultado:=Falso;
meuDA:=TMyDataAdapter.Create;
strSQL:='SELECT * FROM SystemModuleList WHERE modAlias='+QuotedStr(sAlias);
tentar
meuDA.RetrieveData(strSQL);
exceto
em E:Exceção fazer
começar
resultado:=falso;
meuDA.Free;
saída;
fim;
fim;
tentar
começar
com myDA.MyDataSet faça
começar
se não estiver vazio então
começar
APlugin.Caption:= FieldByName('modName').Value;
APlugin.DllFileName := FieldByName('modFile').Value;
APlugin.WndClass := FieldByName('modWndClass').Value;
resultado:=Verdadeiro;
fim;
Fechar;
fim; //fim de com...fazer...
fim; //fim da tentativa
exceto
em E:Exceção fazer
começar
Resultado:=Falso;
meuDA.Free;
Saída;
fim; //fim da exceção
fim; //fim da tentativa...exceto
meuDA.Free;
fim;
//------------------------------------------------------
//Nome: LoadShowPluginForm
//Func: Carrega o plug-in DLL e exibe a janela
//Para: APlugin-TMyPlugins
//Rtrn: verdadeiro criado com sucesso
//Autenticação: CST
//Data: 2/6/2005
//------------------------------------------------------
função TFormHall.LoadShowPluginForm (const APlugin:TMyPlugins):boolean;
var
ShowDLLForm: TShowDLLForm; //instância da função de interface DLL
FreeDLLForm: TFreeDLLForm;
sPath:string; //Caminho completo do arquivo DLL
começar
tentar
começar
sPath:=ExtractFilepath(Application.ExeName)+ 'plugins/' + APlugin.DllFileName;
APlugin.ProcAddr := LoadLibrary(PChar(sPath));
APlugin.FuncFreeAddr := GetProcAddress(APlugin.ProcAddr,'FreeDLLForm');
APlugin.FuncAddr := GetProcAddress(APlugin.ProcAddr ,'ShowDLLForm');
@ShowDLLForm:=APlugin.FuncAddr;
@FreeDLLForm:=APlugin.FuncFreeAddr;
se ShowDllForm(Self.Handle, APlugin.Caption, APlugin.UserID) então
Resultado:=Verdadeiro
outro
Resultado:=Falso;
fim;
exceto
em E:Exceção fazer
começar
Resultado:=Falso;
ShowMessage('Erro ao carregar o módulo plug-in, verifique se os arquivos no diretório PLUGINS estão completos.');
fim;
fim;
fim;
…
4) Controle de janela DLL
Como ilustra o código em 3), a abertura e o fechamento da janela da DLL ocorre apenas na camada de apresentação. Fechar a janela na verdade não libera a janela da DLL. Apenas chama a função da API FindWindow para obter o identificador do formulário de acordo com a janela. identificador (ou seja, Form.name). Uso O parâmetro nCmdShow da função SHOWWINDOW controla a exibição/ocultação da janela.
Na verdade, este é um ponto fraco na implementação do meu programa. Se o método Self.close for usado na janela DLL, causará um erro de memória. Não há como resolvê-lo devido aos recursos limitados. o último recurso. Portanto, o botão fechar da janela principal de cada programa DLL deve estar oculto. :-P
5) Liberação da biblioteca DLL
Ao encerrar o programa, a biblioteca DLL deve ser liberada uma por uma de acordo com a instância de informações do plug-in. A função para liberar a biblioteca DLL é a seguinte:
procedimento TFormHall.ClosePlugin(aPLG:TMyPlugins);
var
FreeDLLForm:TFreeDLLForm;
começar
se aPLG.ProcAddr = 0 então saia;
se aPLG.FuncFreeAddr = nil então saia;
@FreeDLLForm:=aPLG.FuncFreeAddr;
se não for FreeDLLForm(Application.Handle,'','') então
showMessage('erro');
fim;
resumo
O efeito de execução deste programa de exemplo é o seguinte:
=550) window.open('/upload/20080315181507979.jpg');" src="http://files.VeVB.COm/upload/20080315181507979.jpg" onload="if(this.width>'550')this.width='550';if(this.height>'1000')this.height='1000';" border=0>
Entre os métodos acima, porque há muitos problemas não resolvidos devido à capacidade limitada, adotei alguns métodos de encobrimento que não parecem razoáveis. Espero que todos possam projetar uma solução melhor depois de tentar um pouco. ótima maneira de aprender mais.