Ir para conteúdo
  • Cadastre-se

dev botao

Adaptação das units OpenSSL para carregamento Dinâmico


Ver Solução Respondido por Daniel Simoes,
  • Este tópico foi criado há 3165 dias atrás.
  • Talvez seja melhor você criar um NOVO TÓPICO do que postar uma resposta aqui.

Recommended Posts

Postado (editado)

Pessoal do ACBr, boa tarde a todos.

Tenho visto aqui no fórum diversos questionamentos sobre o carregamento das DLLs do OpenSSL que agora por motivos do funcionamento automático/simultâneo, passaram no Trunk2 a serem requisitos também para o carregamento de qualquer aplicativo que tenha a dependência destes no projeto. OK

Estive nos últimos dias estudando as units do OpenSSL, e percebi que é possível realizar o carregamento dinâmico destas, de forma que não fique com essa dependência travada no aplicativo.

Aí vem a pergunta: porque precisaria disso se ao utilizar OpenSSL vai precisar necessariamente das DLLs...?

A resposta é simples: no exemplo de nosso software, é composto de vários aplicativos, e a maioria deles utiliza algum componente do ACBr. Então, por exemplo se eu colocar um componente TACBrNFe em um projeto, simplesmente para poder carregar arquivos XMLs (ler arquivos XMLs e ver as propriedades dele), assim já terei que ter as DLLs do OpenSSL corretamente disponíveis ao abrir esse EXE, pois o ACBrNFe faz menção ao ACBrDFeSSL, que faz menção ao ACBrDFeOpenSSL, que depende diretamente dessas DLLs. E em alguns casos o aplicativo não faz uso necessariamente das funções OpenSSL, apenas usa outros recursos dos componentes do ACBr que possuem o OpenSSL.

Blz, para muitos isso pode não ser problema, mas no nosso caso, e como já pude ver aqui no fórum, é o caso também de outros, seria interessante que essa dependência não fosse tão travada a ponto de carregar as DLLs no início do carregamento do EXE, até mesmo por uma questão de acelerar a abertura do programa (claro que a carga das funções da DLL são muito rápidas, mas é mais um passo que o programa tem que fazer ao iniciar).

Então pensando nisso, passei a estudar a fundo essas units:

- ACBrDFe\ACBrDFeOpenSSL.pas

- ACBrOpenSSL\libeay32.pas

- ACBrOpenSSL\libxmlsec.pas

- ACBrOpenSSL\libxml2.pas

- ACBrOpenSSL\libxslt.pas

O carregamento das funções das DLLs de modo geral estão utilizando o padrão "external" que faz o link com a função já no início. Além disso existe a carga de outras variáveis no initialization da unit.

O que eu fiz:

Utilizei o recurso de carregamento atrasado (delayed) em todas as units e alterei o initialization das units trocando-a para uma procedure que é chamada através da unit principal apenas ao criar o objeto OpenSSL (ACBrDFeOpenSSL).

Dessa forma, a dependência das DLLs só ocorre quando realmente as funções do OpenSSL são chamadas dentro dos componentes.

Exemplo, se eu criar um componente ACBrNFe (deixando o SSLLib no padrão Capicom) e apenas usá-lo para abrir XMLs, as DLLs do OpenSSL nunca serão chamadas.

Por isso, deixo aqui como sugestão as units alteradas para contribuir com o projeto, se caso o pessoal do ACBr considerar válido:

ACBrDFeOpenSSL.pas

libeay32.pas

libxml2.pas

libxmlsec.pas

libxslt.pas

Muito obrigado.

PS: Lembrando que esse recurso iria tornar mais prático o carregamento apenas para quem utiliza o Delphi 2010 em diante, pois foi implementado nele o suporte ao "delayed"

Além disso os recursos estão com diretivas de compilação para compatibilizar corretamente com todas as versões do Delphi

Editado por dalpiaze
Postado (editado)

Juliomar, boa tarde,

Exato, porém o delayed estava implementado apenas na libeay32.pas

O que fiz foi implementá-lo em todas as outras units do OpenSSL, as quais não tinham esse recurso.

Além disso existem outros carregamentos no initialization que também tiveram que ser reestruturados.

Editado por dalpiaze
  • Fundadores
Postado

Não sou contra a sugestão.. com as seguintes ressalvas...

{$IF CompilerVersion >= 21} //delayed só suportado do Delphi 2010 em diante
  {$DEFINE USE_DELAYED} //para carregar automaticamente apenas no primeiro uso as funções da DLL
  {$WARN SYMBOL_PLATFORM OFF} //para não exibir warnings do delayed pois é específico da plataforma
{$ENDIF}

Isso deve ser removido... devemos inserir algo no ACBr.inc para o usuário ligar/desligar o uso de "DELAYED" (já fiz nos meus fontes)

Porque você criou e chama um "INIT" em cada Unit e no ACBrDFeOpenSSL ? Não compreendi a necessidade dessa modificação e temo que ela possa causar efeitos colaterais..

Devemos modificar o mínimo possível as units do libxmlsec, pois isso dificultaria uma atualização quando eles lançarem uma nova versão...

 

  • Curtir 1
Consultor SAC ACBr

Daniel Simões de Almeida
O melhor TEF, é com o Projeto ACBr - Clique e Conheça
Ajude o Projeto ACBr crescer - Assine o SAC

Projeto ACBr     Telefone:(15) 2105-0750 WhatsApp(15)99790-2976.

  • Fundadores
Postado

Já apliquei as diretivas "{$IFDEF USE_DELAYED}delayed{$ENDIF}" nos meus fontes...

Fico no aguardo sobre as explicações a respeito do método "Init" e da necessidade da modificação em ACBrDFeOpenSSL

Consultor SAC ACBr

Daniel Simões de Almeida
O melhor TEF, é com o Projeto ACBr - Clique e Conheça
Ajude o Projeto ACBr crescer - Assine o SAC

Projeto ACBr     Telefone:(15) 2105-0750 WhatsApp(15)99790-2976.

Postado (editado)

Daniel, blz,

Quanto às diretivas, coloquei diretamente nas units por se tratar de algo muito específico para elas, pois vi que ainda não havia chamada ao ACBr.inc nelas também.

Essas diretivas garantirão que o delayed seja usado apenas nos Delphi's que suportem essa função e removendo assim os warnings de função específica de plataforma.

Segui a mesma ideia que já havia no libeay32.dll (já havia uma diretiva para ser ativada manualmente para delayed), apenas fiz ela ficar automática conforme o Delphi mais novo (poderia também ser colocada para ser ativada manualmente no ACBr.inc, porém acredito que quem tem suporte a delphi com delayed deveria preferir que sempre tivesse esse recurso sendo utilizado).

Quanto a procedure Init, é justamente aí é que está o ajuste que torna o carregamento dinâmico, pois antes nesse lugar estava o "initialization" da unit, o que fazia que todas as funções ali carregadas fossem sempre carregadas na inicialização do executável, independente do uso.

Por isso criei uma procedure chamada Init, que é chamada manualmente lá da unit ACBrDFeOpenSSL.pas, que é de onde vem toda a requisição referente as units: libxmlsec, libxml2 e libxslt.

Veja que lá na unit ACBrDFeOpenSSL tive que fazer uns pequenos ajustes pois, antes, como o carregamento era estático na inicialização da aplicação, nas diversas funções presentes nessa unit, exemplo "carga do certificado digital", não dependia da inicialização secundária da função "InitXmlSec", pois já haviam os ponteiros das funções das DLLs carregadas.

Agora como o carregamento passa a ser dinâmico, coloquei para efetuar a carga dos ponteiros ao criar o objeto TDFeOpenSSL, pois a partir dele todas as funções necessitarão da carga dos ponteiros e, subsequentemente, da carga da InitXmlSec.

Obrigado pelo retorno.

 

Editado por dalpiaze
Postado

PS: Tranquilo, quanto a modificação mínima possível das units também concordo, por isso deixo apenas como sugestão para avaliação, pois também não tenho garantia de que funcionará corretamente.

Por enquanto estou usando aqui dessa forma e está funcionando normal.

Postado
2 horas atrás, Daniel Simoes disse:

Não sou contra a sugestão.. com as seguintes ressalvas...


{$IF CompilerVersion >= 21} //delayed só suportado do Delphi 2010 em diante
  {$DEFINE USE_DELAYED} //para carregar automaticamente apenas no primeiro uso as funções da DLL
  {$WARN SYMBOL_PLATFORM OFF} //para não exibir warnings do delayed pois é específico da plataforma
{$ENDIF}

Isso deve ser removido... devemos inserir algo no ACBr.inc para o usuário ligar/desligar o uso de "DELAYED" (já fiz nos meus fontes)

Porque você criou e chama um "INIT" em cada Unit e no ACBrDFeOpenSSL ? Não compreendi a necessidade dessa modificação e temo que ela possa causar efeitos colaterais..

Devemos modificar o mínimo possível as units do libxmlsec, pois isso dificultaria uma atualização quando eles lançarem uma nova versão...

 

Daniel, realmente, estive fazendo testes em todas as rotinas que envolvem o uso do OpenSSL e algumas delas falham, exemplo a Validação da Assinatura. Usando no método tradicional antigo funciona normalmente.

Então, essa implementação do "delayed" realmente não funciona corretamente.

Acredito que podemos descartar essa ideia.

  • Fundadores
Postado

Será que é algo com essa versão do Delphi ?

Conseguiria testar em um Delphi mais recente ?

Acho que a ideia é boa... podemos deixar os fontes com os IFDEFs, e quem quiser testar liga a diretiva no ACBr.inc

Consultor SAC ACBr

Daniel Simões de Almeida
O melhor TEF, é com o Projeto ACBr - Clique e Conheça
Ajude o Projeto ACBr crescer - Assine o SAC

Projeto ACBr     Telefone:(15) 2105-0750 WhatsApp(15)99790-2976.

Postado

Bom dia,

Descobri qual é o problema:

existem as duas partes que carregam a mesma DLL:

- A parte do initialization (onde está dinamicamente com LoadLibrary)

- E a parte do external que carrega estaticamente direto

O fato é que, se observar no LoadLibrary, o carregamento dos ponteiros é feito e depois liberada a DLL, o que faz com que o endereçamento correspondente a ponteiros que se comunicam com função usada no external e que tenha propriedades que foram carregadas pelo LoadLibrary estourem a memória pois essa última não está mais acessível.

O que fiz aqui:

Transformei as 3 units (libxmlsec.pas, libxml2.pas e libxslt) com todas as funções em carregamento dinâmico (LoadLibrary). Ou seja, tirei todos os externals, e agora tudo é por LoadLibrary, assim não dependendo mais de versão do Delphi e nem do Delayed (que não iria funcionar mesmo por causa dos ponteiros).

Estou testando nas diversas rotinas (carregamento de certificado, envio de nota, assinatura, validação) e tudo funcionou normalmente até agora.

Agora o caso é:

Existe esse porém de que realmente não é interessante alterar essas units. E esse carregamento por LoadLibrary fez com que eu tivesse que reescrever toda a estrutura dessas units. Por isso, como você falou (Daniel), não sei até que ponto isso é seguro!

Então deixo aqui apenas como sugestão, para quem quiser conferir e dar uma garantia maior do funcionamento... pois caso contrário melhor deixar como estava para não haver problemas.

Obrigado.

  • Fundadores
Postado

Você alterou toda a Unit ? são centenas de métodos...

Acabo de enviar modificações modificações para o SVN, para contemplar a diretiva de compilação, USE_DELAYED

 

Não seria mais simples, remover o "FreeLibrary" da inicialização ? Algo como
 

finalization

if Assigned( libHandle )
  FreeLibrary(libHandle);  

 

  • Curtir 1
Consultor SAC ACBr

Daniel Simões de Almeida
O melhor TEF, é com o Projeto ACBr - Clique e Conheça
Ajude o Projeto ACBr crescer - Assine o SAC

Projeto ACBr     Telefone:(15) 2105-0750 WhatsApp(15)99790-2976.

Postado (editado)

Daniel, blz,

Sim alterei a estrutura das 4 units todas (são milhares de métodos....) (libeay32.pas, libxmlsec.pas, libxml2.pas, libxslt.pas)

Coloquei inclusive o FreeLibrary no finalization das units.

Mas foi preciso fazer o carregamento dinâmico dos métodos que antes estavam como "external", exatamente por dois motivos:

1- Como a carga acontecia em duas etapas (external e no loadlibrary), ficavam duas handles da dll separadas que dava problema de comunicação entre a dll

2- O delayed além de não funcionar no Lazarus e não ser compatível com todos os Delphi's, li na documentação da Embarcadero que ele não é recomendado utilizar quando houverem muitas funções (que é o caso - milhares de funções), pois tornará o carregamento dinâmico lento.

Dessa forma o carregamento ficou todo dinâmico por LoadLibrary e funcionando em todos os Delphis / Lararus.

PS: Para alterar as units escrevi um programinha para fazer isso automatizadamente, para não ter que alterar linha por linha na mão.

Editado por dalpiaze
  • Curtir 1
Postado
8 horas atrás, Daniel Simoes disse:

Acabo de enviar modificações modificações para o SVN

Daniel, nas alterações que você fez na unit ACBrDFeOpenSSL, faltou chamar o InitXmlSec no Create do TDFeOpenSSL, pois as inicializações do OpenSSL estão separadas em duas etapas: aquelas que eram no initialization e as das externals. Veja que no ACBrDFeOpenSSL existem funções (exemplo carga do Certificado) que não usa a InitXmlSec, porém usa funções que eram carregadas na initialization, por isso já precisam estar disponíveis quando o componente chamar essas funções.

  • Fundadores
Postado

Foi intencional... não achei necessário...

Da maneira que está... ele só inicializará a XMLSec, quando realmente precisar... ou seja, quando algum método que precisa da XMLSec for acionado...

  • Curtir 1
Consultor SAC ACBr

Daniel Simões de Almeida
O melhor TEF, é com o Projeto ACBr - Clique e Conheça
Ajude o Projeto ACBr crescer - Assine o SAC

Projeto ACBr     Telefone:(15) 2105-0750 WhatsApp(15)99790-2976.

Postado
1 hora atrás, Daniel Simoes disse:

Foi intencional... não achei necessário...

Da maneira que está... ele só inicializará a XMLSec, quando realmente precisar... ou seja, quando algum método que precisa da XMLSec for acionado...

Daniel, exato... realmente observando melhor notei que a dependência ao inicializar o objeto TDFeOpenSSL é apenas para a DLL libeay32.dll que contém métodos diretos external. Blz.

Fiz os testes aqui nas várias funções com o delayed desabilitado para ver se tudo continua funcionando, e, a princípio, tudo certo.

Daí fiz o teste com o delayed ativado, mas ocorreram erros em algumas funções... então fiz aquele ajuste que você havia sugerido do FreeLibrary no finalization, e então deu certo!

O ajuste fiz nas três units: libxmlsec, libxml2 e libxslt (todas iguais no final da unit, comentando o freelibrary no Init e deixando para o finalization):

...
    //FreeLibrary(libHandle);
  end;
end;

initialization
  libHandle := 0;

finalization
  if libHandle<>0 then FreeLibrary(libHandle);

end.

Com isso funcionaram as funções com o delayed habilitado.

 

  • Fundadores
Postado

poderia por favor anexar as Units modificadas ?

Seria muito melhor usarmos a carga dinâmica, com mapeamento dos métodos, para variáveis internas na Unit... mas essa mudança é bastante substancial... Tenho receio de que elas possam trazer efeitos colaterais...

Consultor SAC ACBr

Daniel Simões de Almeida
O melhor TEF, é com o Projeto ACBr - Clique e Conheça
Ajude o Projeto ACBr crescer - Assine o SAC

Projeto ACBr     Telefone:(15) 2105-0750 WhatsApp(15)99790-2976.

Postado
2 minutos atrás, Daniel Simoes disse:

poderia por favor anexar as Units modificadas ?

Seria muito melhor usarmos a carga dinâmica, com mapeamento dos métodos, para variáveis internas na Unit... mas essa mudança é bastante substancial... Tenho receio de que elas possam trazer efeitos colaterais...

Exato, concordo, por isso testei a suas alterações aqui, usando o recurso do delayed, o qual não alterou a declaração dos métodos... e funcionou desta forma... apenas fazendo aquele ajuste no final da unit. Assim não precisaria reestruturar as units para carga dinamica por variável.

Postado
13 minutos atrás, Daniel Simoes disse:

poderia por favor anexar as Units modificadas ?

Opa, me desculpe, achei que queria as units com as alterações para carregamento todo dinâmico...

Fiz o acerto aqui nas 3 units com base nos arquivos baixados agora do SVN (apenas incluído freelibrary no finalization para funcionamento do delayed corretamente)

Segue:

libxml2.pas

libxmlsec.pas

libxslt.pas

De acordo com os testes aqui, funcionando corretamente com o delayed desligado e também com ele ligado.

  • Fundadores
  • Solution
Postado

Muito obrigado pela analise e ajuda... já está no SVN...

Lembrando que para usar a Carga Tardia com DELAYED é necessário:

  • Ter Delphi 2010  ou superior (não funciona no Lazarus/FPC)
  • Ativar a diretiva USE_DELAYED no ACBr.inc
{.$DEFINE USE_DELAYED}      // DESATIVADA
{$DEFINE USE_DELAYED}       // ATIVADA 

 

  • Curtir 1
Consultor SAC ACBr

Daniel Simões de Almeida
O melhor TEF, é com o Projeto ACBr - Clique e Conheça
Ajude o Projeto ACBr crescer - Assine o SAC

Projeto ACBr     Telefone:(15) 2105-0750 WhatsApp(15)99790-2976.

  • 3 semanas depois ...
  • Este tópico foi criado há 3165 dias atrás.
  • Talvez seja melhor você criar um NOVO TÓPICO do que postar uma resposta aqui.

Crie uma conta ou entre para comentar

Você precisar ser um membro para fazer um comentário

Criar uma conta

Crie uma nova conta em nossa comunidade. É fácil!

Crie uma nova conta

Entrar

Já tem uma conta? Faça o login.

Entrar Agora
×
×
  • Criar Novo...

Informação Importante

Colocamos cookies em seu dispositivo para ajudar a tornar este site melhor. Você pode ajustar suas configurações de cookies, caso contrário, assumiremos que você está bem para continuar.