Jump to content

Recommended Posts

Posted (edited)

“DLL Hell”

Uma expressão muito usada, e nada incentivadora para o programador que pensa nas DLL’s (Dynamic Link Librarys) pela primeira vez.

Ou se ama ou se odeia, este tipo de recurso. Há quem não abdique, há quem nem queira ouvir falar. No caso destes últimos, é geralmente porque não compreendem como as construir e como as usar.

Antes de mais, algumas vantagens das DLL’s, que deverão ser suficientemente apelativas para que se pelo menos pondere o seu uso:

- Se tivermos vários programas a correr que usem código partilhado, o uso de DLL’s permite poupar recursos, uma vez que apenas uma instância dessa DLL é necessária para ser usada por todos esses programas.

- Como as DLL’s são pré-compiladas, podemos criar código numa linguagem e usá-lo noutra. Logo que os tipos de dados usados existam em ambas as linguagens, não haverá problema.

- Há no Delphi dois métodos possíveis de carregar uma DLL: Carregamento Estática ou Dinâmico. No caso do carregamento dinâmico, os recursos ocupados pela DLL só serão usados durante o seu uso efectivo, e não durante toda a execução do programa principal.

Como de costume, o Delphi/Lazarus tem a solução simples para que estes úteis recursos possam ser usados por todos.

library TesteDLLLazarus;

Function Area(Tipo:Byte;a,b:Real):Real; StdCall; Export;
const
    aTrianguloRect=1;
    aQuadrado=2;
begin
    case Tipo of
         aTrianguloRect:Result:=a*b/2;
         aQuadrado:Result:=a*b;
    end;
end;

Function Hipotenusa(a,b:Real):Real; StdCall; Export;
begin
    Result:=Sqrt(Sqr(a)+Sqr(b));
end;

Exports
      Area,
      Hipotenusa index 2;

begin
end.  

Começando do topo, a palavra-chave Library indica que se trata de uma DLL.

Segue-se o código das funções e procedimentos desejados. Apenas duas particularidades:

A palavra-chave Export marca a função como exportável. Isto significa que poderá ser usada externamente, a partir de uma aplicação que use a DLL.

Caso se trate de uma função meramente interna, para uso de outras dentro da DLL, não é necessário usar a palavra Export.

A palavra StdCall indica qual a Calling Convention (a ordem de passagem dos parâmetros de e para a DLL e a forma como é gerida a memória por eles usada) a usar para a função. Existem várias Calling Conventions, sendo as mais comuns, as seguintes:

- Register -> Predefinição do Delphi

- StdCall -> Predefinição da API do Windows

- CdEcl -> Predefinição do C/C++

Não é por demais importante qual é usada, desde que seja a mesma tanto na DLL como na aplicação que a vai usar. Não é obrigatório definir uma Calling Convention, sendo que se nenhuma for especificada, a Register será usada.

A palavra-chave Exports serve para exportar as funções a que queremos permitir acesso externo. Basta indicar à sua frente o nome dessas funções, separados por virgulas.

A palavra-chave Index é opcional. Usa-se apenas se quisermos atribuir um índice numérico à função ou procedimento. Para tal, basta coloca-la entre o nome da função e o seu índice, como é mostrado no exemplo.

E aí está, simples como é hábito nas linguagens da família Pascal.

Falta no entanto, neste pequeno piscar de olho às DLL’s, o método de usar as DLL’s criadas.

Como referido, o Delphi apresenta dois métodos distintos para usar as funções existentes em DLL’s: Estático e Dinâmico.

O método estático é o mais simples… E mais simples é impossível.

Function Area(Tipo:Byte;a,b:Real):Real; External ‘TesteDLLLazarus.dll’;
Function Hipotenusa(a,b:Real):Real; StdCall; Export; External ‘TesteDLLLazarus.dll’ index 2;

Realmente, nada mais simples do que criar a declaração da função, e colocar à frente o nome da DLL onde a rotina está alojada, através da palavra-chave External. Simples e intuito.

Mais uma vez, a palavra Index é opcional, e representa o índice numérico que atribuímos na DLL.

Uma vez declaradas desta forma, basta usar as funções e procedimentos, como se de uma rotina local se tratasse.

Neste método, a DLL é carregada quando o programa é iniciado e libertada quando o programa termina.

O segundo método é efectivamente mais complexo. Serve para carregar a DLL apenas quando alguma função é realmente necessária e libertá-la logo de seguinda, poupando recursos do sistema. Ainda assim, não é de difícil compreensão:

interface
type 
  Area = Function (Tipo:Byte;a,b:Real):Real;

function LArea(Tipo:Byte;a,b:Real):Real; 


implementation 
function LArea(Tipo:Byte;a,b:Real):Real; 
var 
  Handle : THandle;
begin 
  Handle:=LoadLibrary (‘TesteDLLLazarus.dll’); 
  if Handle <> 0 
     then begin
                 @Area:=GetProcAddress (Handle, ‘Area’);
                 if @Area <> nil 
                    then Result := Area (Tipo,a,b); 
                 FreeLibrary (Handle); 
              end;
end;

Começa-se por definir uma função dinâmica, na secção Interface. Esta será usada mais à frente, quando chamar-mos a DLL.

De seguida, declaramos uma função local. Esta é opcional, o seu conteúdo pode ser inserido directamente na secção de código onde precisamos dos resultados da função da DLL. Para efeitos meramente exemplificativos, foi esta a opção escolhida.

Avançando para a secção implementation, fazemos a implementação da função local declarada.

Precisamos de uma variável do tipo THandle para lidar com a DLL.

A esta variável, vamos atribuir a localização da DLL usando o procedimento LoadLibrary.

Se o resultado for 0, não foi possível carregar a DLL.

De contrário vamos atribuir à função dinâmica declarada em cima o endereço de memória da função da DLL que queremos usar, e se o resultado não for nil (o que significaria que a função especificada não foi encontrada na DLL), podemos executar a função da DLL simplesmente chamando a nossa função dinâmica.

Por fim, chamamos o procedimento FreeLibrary para libertar a DLL da memória.

Ainda que um pouco mais complexo do que a chamada estática da DLL, esta chamada dinâmica é, como se vê, ainda assim muito simples.

Quanto ao “DLL Hell” de que tanto se fala, basta que se guarde as DLL’s na pasta da aplicação que a vai usar… Para quê complicar? 😉

Outras considerações sobre DLL’s:

- O tipo de dados String não é tratado pelo Delphi da mesma forma que a maioria das restantes linguagens. Muitas usam ponteiros para arrays para guardar dados alfanuméricos, outras apenas suportam 256 bytes nas strings.

Assim, se houver a intenção de usar a DLL com programas criados noutras linguagens, é recomendado o uso do tipo ShortString ou PChar em vez do tipo String.

- Ao criar uma DLL, convém verificar exaustivamente a existência de potenciais erros de programação (como sempre, aliás). Um erro numa DLL não tratado poderá ser um pesadelo de detectar no programa principal.

Edited by nunopicado

"A humanidade está a perder os seus génios... Aristóteles morreu, Newton já lá está, Einstein finou-se, e eu hoje não me estou a sentir bem!"

> Não esclareço dúvidas por PM: Indica a tua dúvida no quadro correcto do forum.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...

Important Information

By using this site you accept our Terms of Use and Privacy Policy. We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.