DINAMIC LINK LIBRARIES Capítulo 7 Neste capítulo discutiremos o que é uma DLL, seus aspectos e tipos e ainda, porquê é tão importante para as aplicações Windows O QUE É UMA DLL? Dinamic Link Libraries são módulos de programas que armazenam código, dados ou recursos que podem ser compartilhados entre diversas aplicações Windows. Uma DLL é funcionalmente equivalente a uma unit, pois representa uma coleção de funções externas ao módulo principal da aplicação. A principal diferença entre DLL e unit está no modo como são linkadas, pois a unit é linkada em tempo de compilação, enquanto que a DLL é linkada em tempo de execução. Um dos principais usos das DLLs é permitir que as aplicações carregam código executável em run-time, ao invés de linkar o código a aplicação em compile-time. As DLLs KERNEL32.DLL, USER32.DLL e GDI32.DLL são 3 das DLLs que compõem a Win32. O Kernel32.dll é responsável pela gerência de memória, processos e threads. O User32.dll possui rotinas para que a interface com o usuário possa criar janelas e manipular mensagens, O Gdi32.dll, manipula gráficos. Outras DLLs importantes são: ADVAPI32.DLL (gerencia a segurança de objetos e do Registry) e COMDLG32.DLL que manipula janelas de diálogo. Outra grande vantagem do uso de DLLs, é tornar a aplic ação modular, isso simplifica a atualização da aplicação, pois só é necessário atualizar a DLL e não toda a aplicação. O S.O. Windows é um grande exemplo de modularidade. Uma DLL é basicamente a mesma coisa que uma programa executável, a principal diferença é que uma DLL não é um arquivo executável independente, apesar de armazenar código executável. A maioria das DLLs possuem a extensão.dll, porém existem outras extensões tais como:.drv ( device driver),.sys (arquivos do sistema),.fon( recursos de fonte). DLLs compartilham seu código com outras aplicações em um processo chamado Linkagem Dinâmica. Quando uma aplicação usa uma DLL, a Win32 assegura que somente uma cópia daquela DLL está em memória, esta tarefa é realizada através de uma arquivo mapeado em memória, isto é, a DLL é carregada na heap da Win32 e depois mapeada no endereço do processo chamador. Quando uma DLL é chamada por múltiplos processos, cada processo recebe uma imagem da DLL. Isso não significa que existem diversas copias da DLL em memória, A imagem da DLL é coloca na espaço de endereçamento de cada processo, através do mapeamento da heap do sistema. Delphi Pag. 109
CRIANDO UMA DLL Para criar uma DLL, selecione o item de menu File/New: O Delphi cria o esqueleto de uma DLL, veja a figura abaixo: Delphi Pag. 110
Uma vez gerado o código do esqueleto da DLL, basta adicionar o conjunto de funções que serão exportadas. O código abaixo ilustra uma DLL de funções matemáticas triviais. library math; uses SysUtils, Classes; function pi : extended; result := 3.14159; function AreaCircunf(Raio: Extended): Extended; stdcall; export; result := pi*sqr(raio); function soma(a,b: Extended):Extended;stdcall; export; result := A+B; function Multiplica (A,B: Extended): Extended; stdcall; export; result := A*B; exports AreaCircunf index 1, Soma Index 2, Multiplica index 3; end. A função PI não está sendo exportada, pois é privada à DLL. Funções privadas em DLLs só podem ser usadas no interior da library. CARREAGANDO UMA DLL Uma DLL pode ser carregada de duas maneiras distintas: Estaticamente ou Dinamicamente. A escolha de como carregar uma DLL deverá levar em consideração a vantagem e desvantagem de cada modo. STATIC LINKING x DYNAMIC LINKING A linkagem estática (Static Linking) refere-se ao método usado pelo compilador Delphi para carregar a DLL, isto é, O método estático sempre referencia o mesmo endereço de entrada na DLL e o Windows, ao carregar o aplicativo para a memória, Delphi Pag. 111
também carrega a DLL. Caso a DLL não seja encontrada, Windows não permite que o aplicativo seja executado pois falta um componente essencial. A linkagem estática pode ser feita através do.dpr ou da própria unit. O código abaixo ilustra a importação de uma DLL estaticamente. unit Unit1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(tform) Button1: TButton; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } var Form1: TForm1; implementation {$R *.DFM} function Soma (A,B : Extended): Extended; external 'teste.dll' index 2; procedure TForm1.Button1Click(Sender: TObject); Soma(1,2); end. A carga dinâmica (Dinamic Linking) a ligação entre a chamada da função e o código executável é resolvido em run-time através do uso de uma referência externa à função da DLL. Essa declaração geralmente é feita em uma unit separada chamada Import. Esta unit declara as funções e procedimentos importados. Suponha que você possui uma DLL chamada MAXLIB.DLL que possui a função: Function Max(I1, I2 : integer): integer; A unit Import será: Unit MaxUnit; Interface Delphi Pag. 112
Function Max(I1, I2: Integer): Integer; Implementation Function Max; External MaxLib ; End. Este exemplo ilustra uma das duas formas de fazer uma carga dinâmica, ele é chamado Implicit Loading. A outra forma de fazer a carga dinâmica de uma DLL é através do uso da API LoadLibrary( ). Neste caso, chamamos de Explicit Loading. Deve-se atentar para o modo de carga de uma DLL. Por exemplo, suponha que uma DLL possua muitas funções e nem sempre sua aplicação faz uso dessas rotinas, logo, se a carga da DLL for implícita, haverá um desperdício de memória, então neste caso deve-se fazer uma carga explícita. A Win32 possui funções específicas para DLLs, a saber: LoadLibrary( ), FreeLibrary( ) e GetProcAddress( ). LoadLibrary( ) FreeLibrary( ) GetProcAddress( ) Faz a carga de uma DLL e faz seu mapeamento no espaço de endereçamento do p rocesso. Libera a instância da library Retorna o endereço de uma função O exemplo abaixo ilustra uma carga explícita: procedure TMainForm.btnGetCalendarClick(Sender: TObject); var LibHandle : THandle; ShowCalendar: TShowCalendar; { Tenta carregar a DLL } LibHandle := LoadLibrary('CALENDARLIB.DLL'); try if LibHandle = 0 then raise EDLLLoadError.Create('Unable to Load DLL'); @ShowCalendar := GetProcAddress(LibHandle, 'ShowCalendar'); if not (@ShowCalendar = nil) then lbldate.caption := DateToStr(ShowCalendar(Application.Handle, Caption)) else RaiseLastWin32Error; finally FreeLibrary(LibHandle); // Descarrea a DLL. EXCEPTIONS em DLLs Nas versões anteriores do Delphi, fazia -se necessário verificar se ocorreria uma exception na DLL, caso ocorre-se a exception ela deveria ser capturada antes que escapa-se da DLL. No Delphi 4 isso não é mais necessário pois as exceptions do Delphi4 Delphi Pag. 113
são mapedas como exceptions da Win32. Para que isso funcione, você deve incluir a unit SysUtils na cláusula uses da DLL. A DIRETIVA SAFECALL Funções do tipo SafeCall são usadas em manipulação de exceções e no COM, que garantem que qualquer exceção será propagada ao chamador da função. Resumindo, uma função do tipo SafeCall converte a exceção em um valor de retorno HResult. Veja um exemplo da sintaxe de uma função SafeCall: Function NCE_UFRJ(Serra : Integer): Integer; Safecall; O compilador enxerga a função como: Function NCE_UFRJ(Serra : Integer): Integer; HResult; StdCall; O compilado ainda insere implicitamente um bloco Try..Except que envolve toda o conteúdo da função e captura qualquer exceção. HOUSEKEEPING Algumas vezes, necessitamos desalocar estruturas alocadas durante a execução de uma DLL. A melhor maneira de fazer isso é implementar toda a funcionalidade da DLL em unit a serem utilizadas pela DLL. O código para desalocar as estruturas poderia ser colocado na cláusula Finalization das units que fazes as alocações. Recomenda-se que toda unit que possui uma sessão Initialization também inclua uma sessão Finalization. LOCALIZAÇÃO DOS.DLL Para garantir que uma DLL será encontrada no momento de sua chamada, é necessário que ela esteja em um dos seguintes diretórios: Diretório do programa executável; Diretório \WINDOWS Diretório \WINDOWS\SYSTEM Ou qualquer outro diretório mencionado na variável de ambiente PATH. Delphi Pag. 114