Esse artigo faz parte da revista Clube Delphi edição 11. Clique aqui para ler todos os artigos desta edição



Atenção: por essa edição ser muito antiga não há arquivo PDF para download. Os artigos dessa edição estão disponíveis somente através do formato HTML.

Um pequeno ladrão

Descobrindo informações secretas das janelas

 

Apresento-lhes um pequeno programa, muito útil para descobrir algumas informações sobre determinada janela (ou um controle dentro dela).

         Alguns desenvolvedores necessitam descobrir informações "secretas" de programas, tais como: nome de janelas, objetos, classes, etc.

         O ponto de partida concentra-se na função WindowFromPoint() da API do Windows. Esta função recebe como parâmetro as coordenadas do ponteiro do mouse, no formato da estrutura TPoint, cujos campos são x e y, do tipo inteiro.

 

 

         Na unit Windows.pas, esta função está declarada da seguinte forma:

 

type

  TPoint = record

    x: Longint;

    y: Longint;

  end;

 

 

function WindowFromPoint(Point: TPoint): HWND; stdcall;

 

         O valor de retorno é justamente o Handle da janela (ou do controle dentro da mesma) sobre a qual o mouse está passando. De posse desse Handle, podemos efetuar diversas operações, como por exemplo, obter o título, o nome da classe, a relação dos objetos filhos, seu menu principal, seu menu de controle (System Menu), entre outros. Só depende da sua criatividade (e necessidade).

 

         Para acionar a captura da janela, foi utilizado um componente TTimer com  seu intervalo configurado para 100 ms. No evento OnTimer, temos a rotina abaixo:

 

procedure TForm1.Timer1Timer(Sender: TObject);

begin

  GetCursorPos(pt); //Obtém as coordenadas do mouse

  h := WindowFromPoint(pt); //Obtém o handle da janela

  {DETALHE: O handle retornado pode ser de um controle dentro da janela.}

  GetWindowText(h, WndCaption, SizeOf(WndCaption)); //Pega o título

GetClassName(h, WndClass, SizeOf(WndClass)); //Pega o nome da classe.

  Label3.Caption := IntToStr(h); //Mostra ...

  Label4.Caption := '(' + IntToStr(pt.X) + ', ' + IntToStr(pt.Y) + ')';

  Label6.SetTextBuf(WndCaption);

  Label8.SetTextBuf(WndClass);

end;

 

         Todas as variáveis utilizadas neste procedimento estão declaradas dentro da classe do form, na seção private:

 

  TForm1 = class(TForm)

    ...

  private

    pt: TPoint;

    h: THandle;

    WndCaption: array[0..127] of char;

    WndClass: array[0..127] of char;

  end;

 

         Logo na primeira linha do procedimento Timer1Timer(), obtemos as coordenadas do ponteiro do mouse, que é armazenada em pt. Em seguida, recuperamos o Handle do objeto que está naquela região, armazenado na variável h. De posse desse valor, podemos  consultar outras informações sobre o objeto:

 

GetWindowText      (h, WndCaption, SizeOf(WndCaption)), retorna o título da janela.

GetClassName         (h, WndClass, SizeOf(WndClass)) , retorna nome da classe do objeto.

 

         E, de quebra, uma rotina que envia um comando "clique de mouse" para a Window onde está o ponteiro do mouse (Obs: alguns controles aceitam o click e outros ignoram). Este procedimento está ligado ao evento OnKeyDown do form - e será acionado a cada pressionamento da tecla SHIFT:

 

procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;

  Shift: TShiftState);

var

  sc: TPoint;

begin

  if Shift = [ssShift] then

  begin

    sc := pt;

    Windows.ScreenToClient(h, sc);

    SendMessage(h, WM_LBUTTONDOWN, 0, MakeLong(sc.X, sc.Y));

    Sleep(200);

    SendMessage(h, WM_LBUTTONUP, 0, MakeLong(sc.X, sc.Y));

  end;

end;

 

         Esta procedure envia duas mensagens para a janela cujo Handle está armazenado na variável h, utilizando  a função SendMessage(). As mensagens são as seguintes:

 

WM_LBUTTONDOWN

Pressionamento do botão primário do mouse.

 

WM_LBUTTONDOWN

Pressionamento do botão primário do mouse.

 

         Os outros parâmetros de SendMessage() são apenas informações extras, contendo valores úteis para o receptor da mensagem. No caso, o penúltimo parâmetro é o valor de WParam, o qual não será útil para o nosso objetivo. O último parâmetro (valor de LParam) é um valor inteiro, no formato binário, contendo as coordenadas do mouse.

 

Vamos ao código completo deste aplicativo:

 

unit uMain;

interface

uses

  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,

  StdCtrls, ExtCtrls;

type

  TForm1 = class(TForm)

    Label1: TLabel;

    Label2: TLabel;

    Button1: TButton;

    Label3: TLabel;

    Label4: TLabel;

    Timer1: TTimer;

    Label5: TLabel;

    Label6: TLabel;

    Label7: TLabel;

   Label8: TLabel;

    procedure Timer1Timer(Sender: TObject);

    procedure Button1Click(Sender: TObject);

    procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);

  private

    pt: TPoint;

    h: THandle;

    WndCaption: array[0..127] of char;

    WndClass: array[0..127] of char;

  end;

 

var

  Form1: TForm1;

implementation

 

{$R *.DFM}

 

procedure TForm1.Timer1Timer(Sender: TObject);

begin

  GetCursorPos(pt); //Obtém as coordenadas do mouse

  h := WindowFromPoint(pt); //Obtém o handle da janela

  {DETALHE: O handle retornado pode ser de um controle dentro da janela.}

  GetWindowText(h, WndCaption, SizeOf(WndCaption)); //Pega o título

  GetClassName(h, WndClass, SizeOf(WndClass)); //Pega o nome da classe.

  Label3.Caption := IntToStr(h); //Mostra ...

  Label4.Caption := '(' + IntToStr(pt.X) + ', ' + IntToStr(pt.Y) + ')';

  Label6.SetTextBuf(WndCaption);

  Label8.SetTextBuf(WndClass);

end;

 

procedure TForm1.Button1Click(Sender: TObject);

begin

  Close;

end;

 

procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;

  Shift: TShiftState);

var

  sc: TPoint;

begin

  if Shift = [ssShift] then

  begin

    sc := pt;

    Windows.ScreenToClient(h, sc);

    SendMessage(h, WM_LBUTTONDOWN, 0, MakeLong(sc.X, sc.Y));

    Sleep(200);

    SendMessage(h, WM_LBUTTONUP, 0, MakeLong(sc.X, sc.Y));

  end;

end;

end.