Close Panel

20

Jul

2008

Window Spy

By DarkByte. Posted in Delphi | 8 Comments »

Win 32 APIProbabil că ştiţi că în Windows totul se bazează pe ferestre … era destul de evident, nu ?

Să vedem cum am putea afla câte ceva legat de ferestrele vizibile şi cum am putea să le ascundem, să le dezactivăm, etc, totul din Borland Delphi, folosind funcţii API.Borland Delphi 7

Pentru început, să vedem cum aflăm ce fereastră avem sub cursorul de mouse.

Pentru asta ne trebuie să ştim poziţia cursorului, care se poate afla folosind funcţia GetCursorPos, din care putem afla handle-ul ferestrei de sub cursor, folosind funcţia WindowFromPointSă vedem nişte cod.

var hWindow  : Longint;
    P : TPoint;
  ...
  GetCursorPos(P);
 
  hWindow := WindowFromPoint(P);

Destul de simplu, nu ? În variabila hWindow avem acum handle-ul care ne interesează.
Pentru a “vedea” în timp real ce fereastra e sub cursor, punem codul acesta într-un TTimer.

Să vedem cum afişăm informaţiile găsite, cum ar fi titlul ferestrei, clasa ferestrei (adică tipul componentei) şi handle-ul asociat părintelui ei, din TTimer. Pentru asta avem nevoie (în cazul acesta) de patru TEdit-uri.

procedure TForm1.Timer1Timer(Sender: TObject);
var hWindow  : Longint;
    P : TPoint;
    buff : Array[0..255] of char;
begin
  GetCursorPos(P);
 
  hWindow := WindowFromPoint(P);
  Edit2.Text := '$' + IntToHex(hWindow, 8);
 
  GetWindowText(StrToInt(Edit2.Text), Buff, 255);
  Edit1.Text := Buff;
 
  GetClassName(StrToInt(Edit2.Text), Buff, 255);
  Edit3.Text := Buff;
 
  If GetParent(StrToInt(Edit2.Text)) > 0
    then Edit4.Text := '$' + IntToHex(GetParent(StrToInt(Edit2.Text)), 8)
    else Edit4.Text := 'None';
end;

GetWindowText returnează titlul ferestrei (sau textul din fereastră, dacă e vorba de un TEdit), GetClassName returnează tipul componentei, iar GetParent handle-ul părintelui. Simplu, nu ? happy

Păcat că momentan tot ce aflăm sunt lucruri destul de puţin importante şi deloc atrăgătoare. Nişte numere şi ceva text care oricum se vedea şi fără un asemenea program. Să încercăm ceva mai atrăgător.

Pentru început vom ascunde / reafişa fereastra, apelând funcţia ShowWindow, care are nevoie de doi parametri: handle-ul ferestrei şi un parametru care specifică dacă vrem să afişăm sau să ascundem fereastra.

Afişare fereastră:

ShowWindow(wHandle, SW_SHOW);

Ascundere fereastră:

ShowWindow(wHandle, SW_HIDE);

Alt lucru care-l putem face : vom seta ca fereastra selectată să devină fereastră de top-level şi înapoi. Pentru acest lucru vom folosi funcţia SetWindowPos pentru a ne defini o procedură apelabilă cu un parametru de tip Boolean care să ştie modifica stilul ferestrei în ambele moduri, al doilea parametru fiind handle-ul ferestrei care vrem s-o modificăm.

procedure SetTop(Top : Boolean; Handle : HWnd);
Begin
  If Top
    Then
      Begin
        SetWindowPos(Handle, HWND_TOPMOST, 0, 0, 0, 0,
                     SWP_SHOWWINDOW + SWP_NOMOVE + SWP_NOSIZE);
        SetWindowPos(Handle, HWND_TOP, 0, 0, 0, 0,
                     SWP_SHOWWINDOW + SWP_NOMOVE + SWP_NOSIZE);
      End
    Else SetWindowPos(Handle, HWND_NOTOPMOST, 0, 0, 0, 0,
                      SWP_SHOWWINDOW + SWP_NOMOVE + SWP_NOSIZE);
End;

Acum nu mai avem decât să apelăm procedura asta din butonul corespunzător.

Ştiind handle-ul ferestrei, o putem şi activa / dezactiva folosind EnableWindow, care necesită specificarea handle-ului ferestrei şi un parametru logic care determină activarea / dezactivarea ei.

Codul pentru cele şase butoane de până acum:

procedure TForm1.bShowClick(Sender: TObject);
begin
  hWindow := HexToInt(Copy(Edit2.Text, 2, Length(Edit2.Text) - 1));
  ShowWindow(hWindow, SW_SHOW);
end;
 
procedure TForm1.bHideClick(Sender: TObject);
begin
  hWindow := HexToInt(Copy(Edit2.Text, 2, Length(Edit2.Text) - 1));
  ShowWindow(hWindow, SW_HIDE);
end;
 
procedure TForm1.bTopClick(Sender: TObject);
begin
  hWindow := HexToInt(Copy(Edit2.Text, 2, Length(Edit2.Text) - 1));
  SetTop(True, hWindow);
end;
 
procedure TForm1.bPotClick(Sender: TObject);
begin
  hWindow := HexToInt(Copy(Edit2.Text, 2, Length(Edit2.Text) - 1));
  SetTop(False, hWindow);
end;
 
procedure TForm1.bEnableClick(Sender: TObject);
begin
  hWindow := HexToInt(Copy(Edit2.Text, 2, Length(Edit2.Text) - 1));
  EnableWindow(hWindow, True);
end;
 
procedure TForm1.bDisableClick(Sender: TObject);
begin
  hWindow := HexToInt(Copy(Edit2.Text, 2, Length(Edit2.Text) - 1));
  EnableWindow(hWindow, False);
end;

Probabil că aţi remarcat linia

hWindow := HexToInt(Copy(Edit2.Text, 2, Length(Edit2.Text) - 1));

, despre care nu am scos nici un cuvânt până acum. Este inversa funcţiei IntToHex din Delphi, şi este definită mai jos.

function HexToInt(S : String): Longint;
var B : Byte;
    C : Char;
Begin
  Result := 0;
  s := UpperCase(s);
  For B := 1 To Length(s) Do
    Begin
      Result := Result * 16;
      c := S[B];
      Case c Of
        '0'..'9': Inc(Result, Ord(c) - Ord('0'));
        'A'..'F': Inc(Result, Ord(c) - Ord('A') + 10);
        Else
          Begin
            Result := 0;
            Exit;
          End;
      End;
    End;
End;

Puteam folosi variabila hWindow ca variabilă globală şi atunci scăpam de nevoia folosirii acestei funcţii, nu ? Dar ce se întâmpla dacă la utilizarea programului se voia reafişarea unei ferestre căreia îi ştim handle-ul ? În acest fel tot ce avem de făcut este editarea celei de-a doua căsuţe text cu handle-ul respectiv şi putem face orice, altfel putând lucra doar cu ferestrele vizibile pe ecran.

Cam acesta ar fi codul necesar pentru o mini-aplicaţie de lucru cu ferestrele … dar stai … e o problemă … dacă verificarea ferestrei de sub mouse se face în timp real, înseamnă că … da, dacă selectez butonul de “ascundere fereastră”, îmi va ascunde tocmai respectivul buton. Damn, nu e bine.

Trebuie să programăm o metodă de a opri scanarea ferestrelor în timp real, o dată ce am selectat fereastra cu care vrem să lucrăm, fără să mişcăm mouse-ul. Pentru asta ne vom defini o combinaţie de taste globală, folosind funcţia RegisterHotKey, care primeşte patru parametri: handle-ul ferestrei căreia să-i trimită mesaj în caz că s-a apăsat combinaţia noastră de taste, un identificator pentru combinaţie, definit de noi, şi combinaţia de taste, definită prin doi parametri – primul, care defineşte dacă tastele CTRL, ALT, SHIFT sau WIN sunt apăsate, şi al doilea, care defineşte o tastă normală (cum ar fi “R”winking. Apelul funcţiei, pentru a ne defini combinaţia CTRL + F8, este :

const StopKey = 100000;
  ...
  RegisterHotKey(Handle, StopKey, MOD_CONTROL, VK_F8);

Această linie trebuie apelată chiar la începutul rulării programului, în FormCreate.
Atenţie : StopKey este o constantă ce trebuie definită global, înaintea secţiunii implementation.

Pentru a fi anunţaţi când este apăsată combinaţia noastră de taste ne vom defini o procedură care să primească mesajul trimis de sistem, interceptând mesajul WM_HOTKEY.

type
  TForm1 = class(TForm)
  ...
  private
    { Private declarations }
    procedure WMHotKey(var Message: TMessage); message WM_HOTKEY;
 ...

Codul procedurii :

procedure TForm1.WMHotKey(var Message: TMessage);
begin
  If Message.WParam = StopKey
    Then Timer1.Enabled := not Timer1.Enabled;
end;

Practic, la fiecare apăsare a combinaţiei, TTimer-ul nostru se va opri / reporni. Pentru a nu lăsa mizerie după rularea programului, va trebui să distrugem această combinaţie de taste la oprire. Pentru acest lucru, în FormCloseQuery vom apela :

  UnregisterHotKey(Handle, StopKey);

Cam asta ar fi tot codul.

Pentru a nu vă pune să lucraţi cu copy / paste şi să vă faceţi griji de componente şi asignări de evenimente, aveţi mai jos sursele şi executabilul arhivate.

Download : Sursa :: Executabil


DarkByte is
Email this author | All posts by DarkByte | Subscribe to Entries (RSS)

 

8 Responses to “Window Spy”

  1. 1
    marius Says:

    sal, imi poti spune cum pot face sa aflu pozitia unui classname?

  2. 2
    DarkByte Says:

    Da-mi detalii despre ce vrei sa faci.

    ClassName-ul poate fi “ToolbarWindow32″ … dar poate fi in mai multe locuri pe ecran. Te referi la o anumita fereastra sau la ce ?

  3. 3
    marius Says:

    intr-adevar classname-ul meu e de 2 ori pe ecran, vreau sa inlocuiesc patratul cu avatarul de pe mess cu propria fereastra.

  4. 4
    DarkByte Says:

    Nu sunt sigur ca inteleg ce vrei sa faci, pentru ca esti destul de criptic.

    Daca vrei sa pui un control (fereastra, buton, etc) in Yahoo! Messenger (sau in alt program, for that matter) peste un control care deja exista, trebuie sa afli handle-ul ferestrei parinte, iar controlului tau ii vei da ca parinte acel handle. De acolo mai departe e doar o chestie de pozitionat controlul si de avut grija la eventuri.

    Spor !

  5. 5
    marius Says:

    am trecut de acele faze, am reusit sa pozitionez un handle din aplicatia mea dar nu si inversul (sa aiba aplicatia mea coordonatele handle-ului).Daca o sa aflu voi posta…poate mai intereseaza pe cineva winking.multumesc

  6. 6
    DarkByte Says:

    Ehm … vrei pozitia ferestrei care o gasesti ? Daca da, foloseste GetWindowRect.

    Daca nici asta nu e ce vrei tu, da-mi, te rog, mai multe detalii.

  7. 7
    marius Says:

    salut, mi-am gasit raspunsul aici: http://www.swissdelphicenter.ch/torry/showcode.php?id=347

    bafta

  8. 8
    DarkByte Says:

    Adica, GetWindowRect, cum am si spus … daca vroiai pozitia taskbarului, puteai spune asa si-ti dadeam direct cod. Cum am spus mai sus, esti foarte criptic.

 

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">