Universidade Federal do Rio de Janeiro N ú c l e o d e C o mputação Eletrônica Divisão de Assistência ao Usuário Capítulo 7 DINAMIC LINK LIBRARIES 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 Universidade Federal do Rio de Janeiro Núcleo de Computação Eletrônica Divisão de Assistência ao Usuário 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 Universidade Federal do Rio de Janeiro N ú c l e o d e C o mputação Eletrônica Divisão de Assistência ao Usuário 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; begin result := 3.14159; end; function AreaCircunf(Raio: Extended): Extended; stdcall; export; begin result := pi*sqr(raio); end; function soma(A,B: Extended):Extended;stdcall; export; begin result := A+B; end; function Multiplica (A,B: Extended): Extended; stdcall; export; begin result := A*B; end; exports AreaCircunf index 1, Soma Index 2, Multiplica index 3; begin 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 Universidade Federal do Rio de Janeiro Núcleo de Computação Eletrônica Divisão de Assistência ao Usuário 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 } end; var Form1: TForm1; implementation {$R *.DFM} function Soma (A,B : Extended): Extended; external 'teste.dll' index 2; procedure TForm1.Button1Click(Sender: TObject); begin Soma(1,2); end; 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 Universidade Federal do Rio de Janeiro N ú c l e o d e C o mputação Eletrônica Divisão de Assistência ao Usuário 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; begin { 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. end; end; 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 Universidade Federal do Rio de Janeiro Núcleo de Computação Eletrônica Divisão de Assistência ao Usuário 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: • • • • Delphi 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. Pag. 114