João Mello

June 27, 2009

Dll - rotinas de início e término do processo/thread

Filed under: Cotidiano, Delphi, Dica, Karmas — Tags: , , , — joaomello @ 12:34 pm

Lambda! Lambda! Lambda!
Ja se faz um longo tempo que não posto aqui, e assuntos não faltaram como lançamento do Delphi 2009, Visual Studio 2010, o término do meu curso técnico em eletrônica - e falando nele estarei na FIERGS POA do dia 24/06 á 27/06 no INOVA SENAI.

Enfim que de falar e o assunto de hoje é sobre os eventos de entrada/saída da DLL no Delphi. Me deparei com um problema parecido estes dias, criei um aplicativo parecido com o gerenciador de serviço do Windows mas, com muitos plus (hehehehe), e todos os serviço se basseiam em uma DLL (aliás devo me lembrar de postar mais sobre ele, tem muita coisa enteressante nele que vale apena repassar para vocês). E uma das coisas que aprendi com ele, foi descobrir como capturar os eventos de entrada/saída da DLL.

A váriavel DLLProc global corresponder a um ponteiro de procedimento ao qual pode ser atribuído o precedimento de entrada/saída. Essa variável inicia-se em nil, ao menos que você defina seu própio porcedimento. Ao definir um procedimento de entrada/saída, será possível responder aos eventos listados:

DLL_PROCESS_ATTACH: A DLL será anexada ao espaço de endereços do processo atual, quando o mesmo iniciar ou quando for feita uma chamada para LoadLibrary(). As DLLs inicializam quaisquer dados da instância durante esse evento.
DLL_PROCESS_DETACH: A DLL será desanexada do espaço de endereços do processo de chamada. Isso ocorrerá durante a saída de um processo de limpeza ou quando for feita uma chamda para FreeLibrary(). A DLL pode liberar quaisquer dados da instância durante esse evento.
DLL_THREAD_ATTACH: Esse evento ocorrerá quando o processo atual criar um novo thread. Quando isso ocorrer, o sistema chamará a função de ponto de entrada de quaisquer DLLs anexadas ao processo. Essa chamada é feita no contexto de novo thread e pode ser usada para alocar quaisquer dados específicos ao thread.
DLL_THREAD_DETACH: Este evento ocorrerá quando o thread estiver saindo. Durante esse evento, a DLL pode liberar quaisquer dados inicializados específicos do thread.

Código-fonte para DllEntry.dpr:

library DllEntry;
 
uses
  SysUtils,
  Windows,
  Dialogs,
  Classes;
 
procedure DLLEntryPoint(dwReasson: DWord);
begin
  case dwReasson of
    DLL_PROCESS_ATTACH: ShowMessage('Anexando ao processo');
    DLL_PROCESS_DETACH: ShowMessage('Desanexando do processo');
    DLL_THREAD_ATTACH : MessageBeep(0);
    DLL_THREAD_DETACH : MessageBeep(0);
  end;
end;
 
begin
  { Primeirto, atribui o procedimento à variável DLLProc }
  DllProc := @DLLEntryPoint;
  { Agora, chama o procedimento apra refletir se a DLL está anexada ao processo }
  DLLEntryPoint(DLL_PROCESS_ATTACH);
end.

Código de exemplo para demonstração de entrada/saída da DLL:

unit ufrmPrincipal;
 
interface
 
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;
 
type
  TTestThread = class(TThread)
    procedure Execute; override;
    procedure SetCaptionData;
  end;
 
  TfrmPrincipal = class(TForm)
    btnLoadLibrary: TButton;
    btnFreeLibrary: TButton;
    btnCreateThread: TButton;
    btnFreeThread: TButton;
    Label1: TLabel;
    lblCount: TLabel;
    procedure btnLoadLibraryClick(Sender: TObject);
    procedure btnFreeLibraryClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure btnCreateThreadClick(Sender: TObject);
    procedure btnFreeThreadClick(Sender: TObject);
  private
    LibHandle : THandle;
    TestThread: TTestThread;
    Counter   : Integer;
    GoThread  : Boolean;
  end;
 
var
  frmPrincipal: TfrmPrincipal;
 
implementation
 
{$R *.dfm}
 
{ TTestThread }
 
procedure TTestThread.Execute;
begin
  while frmPrincipal.GoThread do
  begin
    Synchronize(SetCaptionData);
    Inc(frmPrincipal.Counter);
  end;
end;
 
procedure TTestThread.SetCaptionData;
begin
  frmPrincipal.lblCount.Caption := IntToStr(frmPrincipal.Counter);
end;
 
procedure TfrmPrincipal.btnCreateThreadClick(Sender: TObject);
{ Este procedimento cria a instância da TThread. Se a DLL for carregada,
  ocorrerá um bipe de mensagem. }
begin
  if not Assigned(TestThread) then
  begin
    GoThread := True;
    TestThread := TTestThread.Create(False);
  end;
end;
 
procedure TfrmPrincipal.btnFreeLibraryClick(Sender: TObject);
{ Este procedimento libera a biblioteca }
begin
  if not (LibHandle = 0) then
  begin
    FreeLibrary(LibHandle);
    LibHandle := 0;
  end;
end;
 
procedure TfrmPrincipal.btnFreeThreadClick(Sender: TObject);
{ Na liberação da TThread, um bipe de mensagem ocorrerá, se a DLL for carregada. }
begin
  if Assigned(TestThread) then
  begin
    GoThread := False;
 
    while TestThread.Suspended do
      TestThread.Resume;
 
    TestThread.WaitFor;
 
    FreeAndNil(TestThread);
 
    Counter := 0;
  end;
end;
 
procedure TfrmPrincipal.btnLoadLibraryClick(Sender: TObject);
{ Este procedimento carrega a biblioteca DllEntryLib.dll }
begin
  if LibHandle = 0 then
  begin
    LibHandle := LoadLibrary('DllEntry.dll');
    if LibHandle = 0 then
      raise Exception.Create('Erro no carregar DLL');
  end else
  begin
    MessageDlg('DLL ja carregada!', mtWarning, [mbOK], 0);
  end;
end;
 
procedure TfrmPrincipal.FormCreate(Sender: TObject);
begin
  LibHandle  := 0;
  TestThread := nil;
end;
 
end.

É um exemplo simples de como controlar o eventos básicos de uma DLL, mas como o desenvolvimento de uma DLL requer um bom estudo e cuidado com as pequenas coisas (isso também servira de base para os novos posts sobre serviços windows).
Download do exemplo.

Espero que tenham gostado, até próxima!
Att,
Mello.

Powered by WordPress