Klikacz

Adam Boduch

W tym gotowcu zaprezentowany zostanie sposób w jaki można sterować myszką, czyli przesuwać ją na określoną pozycje, klikać i powracać na poprzednią pozycję. Gotowiec, który tutaj zaprezentowano, a którego kod zostanie za chwile przedstawiony, działą następująco:

Najpierw użytkownik musi podać współrzędne ekranu na które myszka ma klikać. Następnie określa odstęp w jakim ma się to odbywać. Po tym naciska przycisk "Włącz". Co się dzieje później? Użytkownik może sobie spokojnie pracować o co określony czas aktywne okno zostaje przełączone, myszka kliknie się na określonych współrzędnych, a następnie powróci do poprzedniej pozycji.

Jeżeli użytkownik naciśnie określoną kombinacje klawiszy, obojętnie kiedy i gdzie (w naszym przypadku Ctrl+Enter) to w programie przypisywana zostaje aktualna pozycja kursora.

W sekcji Private należy deklarować metodę:

procedure WMHOTKEY(var Msg : TMessage); message WM_HOTKEY; 
// przechwytuje skrot klawiaturowy

Jej definicja wygląda następująco:

procedure TMainForm.WMHOTKEY(var Msg: TMessage);
var
  MousePos : TPoint;
  Buffer : array[0..255] of char;
begin
{
   procedura ta wykonywana jest w momencie, gdy uzytkownik wcisnie kombinacje
   klawiszy Ctrl+Enter. Wtedy pobierana jest pozycja ekranu, a do zmiennej przypisuje
   sie uchwyt aktywnego okna
}
  if Msg.WParam = $0001 then
  begin
    GetCursorPos(MousePos);  // pobierz pozycje kursora
    Xedt.Text := IntToStr(MousePos.X); // przypisz do kontrolek
    Yedt.Text := IntToStr(MousePos.Y);
    Foreground := GetForegroundwindow;  // pobierz uchwyt okna

    // pobierz tytul aktywnego okna
    GetWindowText(Foreground, Buffer, SizeOf(Buffer));

    lblTitle.Caption := Buffer;
  end;
end;

Bedzie to reakcja na nacisniecie kombinacji Ctrl+Enter. W takim przypadku nastapi pobranie pozycji kursora i wpisanie jego współrzędnych w kontrolkach typu TEdit. Dodatkowo do zmiennej Foreground zostanie przypisany uchwyt aktywnego okna, a do zmiennej Buffer - jego tytuł.

Oczywiście to nie wszystko, ponieważ trzeba obsłużyć rejestracje i zwalnianie tego skrótu:

procedure TMainForm.FormCreate(Sender: TObject);
begin
{ Ta funkcja rejestruje skrót: Ctrl + Enter dla naszej aplikacji }
  RegisterHotKey(Handle, $0001, MOD_CONTROL,  VK_Return);
end;

procedure TMainForm.FormDestroy(Sender: TObject);
begin
{ zwolnienie skrotu w systemi }
  UnregisterHotKey(Handle, $0001);
end;

Na formularzu umieszczony jest komponent Timer, ktory odgrywa kluczową rolę w programie.
Co pewien czas wykonuje on jakieś czynności, a konkretnie klika w określone miejsce ekranu.

Cały kod procedury zdarzenia OnTimer wygląda tak:

procedure TMainForm.TimerTimer(Sender: TObject);
var
  CurrentPos : TPoint;
  CurrentWindow : HWND;
begin
  CurrentWindow := GetForegroundWindow; // pobierz uchwyt aktywnego okna
  GetCursorPos(CurrentPos); // pobierz tymczasowa pozycje ekranu

  ShowWindow(Foreground, SW_SHOWNA);  // pokaż okno
  BringWindowToTop(Foreground); // dla pewnosci - wysun na wierzch
  SetForegroundwindow(Foreground);  // ustaw aktywne okno


  SetCursorPos(StrToInt(Xedt.Text), StrToInt(Yedt.Text));  // ustaw w wybranej pozycji
  { kliknij na pozycje }
  mouse_event(MOUSEEVENTF_LEFTDOWN, StrToInt(Xedt.Text), StrToInt(Yedt.Text), 0, 0);
  mouse_event(MOUSEEVENTF_LEFTUP, StrToInt(Xedt.Text), StrToInt(Yedt.Text), 0, 0);

  ShowWindow(Currentwindow, SW_SHOWNA);  // pokaż okno
  SetForegroundWindow(Currentwindow);  // ustaw aktywne okno
  SetCursorPos(CurrentPos.X, CurrentPos.Y); // ustaw ponownie na poprzedniej pozycji

  iCountdown := Wait.Value * 60;

  Inc(Counter); // zwieksz licznik
  StatusBar.SimpleText := 'Kliknięto... poraz ' + IntToStr(Counter);
end;

Ta metoda działa mniej więcej tak: Pobiera dotychczasowe aktywne okno i pozycje kursora. Następnie ustawia kursor w wybranym miejscu i następuje kliknięcie. Wszystko to za sprawą procedury mouse_event, która symuluje naciśnięcie klawisza myszki.

W naszym przypadku wywołanie tej procedury następuje dwa razy, gdyż w pierwszym przypadku następuje
wciśnięcie lewego klawisza, a później lego "wyciśnięcie". Na samym końcu po wykonaniu tej operacji kursor powraca do poprzedniej pozycji i do poprzedniego aktywnego okna.

Właściwie cały program jest już gotowy. Normalnie cała procedura jest wyłączona (Timer.Enabled = False) i nic się nie dzieje. Trzeba oprogramować zdarzenie OnClick które powoduje zatrzymanie lub uruchomienie zegara:

procedure TMainForm.btnAcceptClick(Sender: TObject);
begin
  if not Timer.Enabled then  // jezeli Timer jest wylaczony
  begin
    Timer.Interval := Wait.Value * 60000;  // ustaw czas pomiedzy dzialaniami
    Timer.Enabled := True;  // wlacz timer
    Caption := 'Klikacz - włączony!'; // zmien wlasciwosc okna

    // licznik ktory bedzie odliczal do kolejnego klikniecia
    iCountdown := Wait.Value * 60;
    Countdown.Interval := 1000;
    // wlaczenie licznika
    Countdown.Enabled := True;
  end else
  begin
    Timer.Enabled := False; // w przeciwnym wypadku - wylacz timer
    Caption := 'Klikacz - wyłączony!'; // zmien wartosc

    Countdown.Enabled := False;
  end;
end;

Czas, który ma upłynąć jest pobierany z kontrolki i mnożony przez 60,000 milisekund. Tak
więc kliknięcie będzie nastąpywać conajmniej raz na minute.

Cały kod programu wygląda tak:

           

(****************************************************************)
(*                                                              *)
(*              Copyright (c) 2002 by Adam Boduch               *)
(*                    www.4programmers.net                      *)
(*                    [email protected]                     *)
(*                                                              *)
(****************************************************************)

unit MainFrm;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ExtCtrls, jpeg, Spin, ComCtrls;


type
  TMainForm = class(TForm)
    gbHome: TGroupBox;
    X: TLabel;
    Y: TLabel;
    Xedt: TEdit;
    Yedt: TEdit;
    btnAccept: TButton;
    Timer: TTimer;
    Image1: TImage;
    Wait: TSpinEdit;
    Label1: TLabel;
    Label2: TLabel;
    StatusBar: TStatusBar;
    lblCountdown: TLabel;
    Countdown: TTimer;
    lblTitle: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure btnAcceptClick(Sender: TObject);
    procedure TimerTimer(Sender: TObject);
    procedure CountdownTimer(Sender: TObject);
  private
    Foreground : HWND;  // zmienna przechowuje uchwyt okna
    procedure WMHOTKEY(var Msg : TMessage); message WM_HOTKEY; // przechwytuje skrot klawiaturowy
   public
    { Public declarations }
  end;

var
  MainForm: TMainForm;

implementation

{$R *.DFM}

{ TMainForm }

var iCountdown : Integer; // czas pozostaly do kolejnego klikniecia
var Counter : Integer = 0;   // licznik klikniec w banner u gorze

procedure TMainForm.WMHOTKEY(var Msg: TMessage);
var
  MousePos : TPoint;
  Buffer : array[0..255] of char;
begin
{
   procedura ta wykonywana jest w momencie, gdy uzytkownik wcisnie kombinacje
   klawiszy Ctrl+Enter. Wtedy pobierana jest pozycja ekranu, a do zmiennej przypisuje
   sie uchwyt aktywnego okna
}
  if Msg.WParam = $0001 then
  begin
    GetCursorPos(MousePos);  // pobierz pozycje kursora
    Xedt.Text := IntToStr(MousePos.X); // przypisz do kontrolek
    Yedt.Text := IntToStr(MousePos.Y);
    Foreground := GetForegroundwindow;  // pobierz uchwyt okna

    // pobierz tytul aktywnego okna
    GetWindowText(Foreground, Buffer, SizeOf(Buffer));

    lblTitle.Caption := Buffer;
  end;
end;

procedure TMainForm.FormCreate(Sender: TObject);
begin
{ Ta funkcja rejestruje skrót: Ctrl + Enter dla naszej aplikacji }
  RegisterHotKey(Handle, $0001, MOD_CONTROL,  VK_Return);
end;

procedure TMainForm.FormDestroy(Sender: TObject);
begin
{ zwolnienie skrotu w systemi }
  UnregisterHotKey(Handle, $0001);
end;

procedure TMainForm.btnAcceptClick(Sender: TObject);
begin
  if not Timer.Enabled then  // jezeli Timer jest wylaczony
  begin
    Timer.Interval := Wait.Value * 60000;  // ustaw czas pomiedzy dzialaniami
    Timer.Enabled := True;  // wlacz timer
    Caption := 'Klikacz - włączony!'; // zmien wlasciwosc okna

    // licznik ktory bedzie odliczal do kolejnego klikniecia
    iCountdown := Wait.Value * 60;
    Countdown.Interval := 1000;
    // wlaczenie licznika
    Countdown.Enabled := True;
  end else
  begin
    Timer.Enabled := False; // w przeciwnym wypadku - wylacz timer
    Caption := 'Klikacz - wyłączony!'; // zmien wartosc

    Countdown.Enabled := False;
  end;
end;

procedure TMainForm.TimerTimer(Sender: TObject);
var
  CurrentPos : TPoint;
  CurrentWindow : HWND;
begin
  CurrentWindow := GetForegroundWindow; // pobierz uchwyt aktywnego okna
  GetCursorPos(CurrentPos); // pobierz tymczasowa pozycje ekranu

  ShowWindow(Foreground, SW_SHOWNA);  // pokaż okno
  BringWindowToTop(Foreground); // dla pewnosci - wysun na wierzch
  SetForegroundwindow(Foreground);  // ustaw aktywne okno


  SetCursorPos(StrToInt(Xedt.Text), StrToInt(Yedt.Text));  // ustaw w wybranej pozycji
  { kliknij na pozycje }
  mouse_event(MOUSEEVENTF_LEFTDOWN, StrToInt(Xedt.Text), StrToInt(Yedt.Text), 0, 0);
  mouse_event(MOUSEEVENTF_LEFTUP, StrToInt(Xedt.Text), StrToInt(Yedt.Text), 0, 0);

  ShowWindow(Currentwindow, SW_SHOWNA);  // pokaż okno
  SetForegroundWindow(Currentwindow);  // ustaw aktywne okno
  SetCursorPos(CurrentPos.X, CurrentPos.Y); // ustaw ponownie na poprzedniej pozycji

  iCountdown := Wait.Value * 60;

  Inc(Counter); // zwieksz licznik
  StatusBar.SimpleText := 'Kliknięto... poraz ' + IntToStr(Counter);
end;

procedure TMainForm.CountdownTimer(Sender: TObject);
begin
  lblCountdown.Caption := 'Czas pozostały do kolejnego kliknięcia: ' + IntToStr(iCountdown);
  Dec(iCountdown);
end;

end.

Kod źródłowy programu:

2 komentarzy

Witam,
Super artykuł. Mam jednak problem.. Co należy zrobić, aby program wykonywał np. 4 kliknięcia pod rząd w różne miejsca?
//BTW. Na moim Windowsie 98 wogóle nie zalicza tych kliknięć..

nie da sie sciagnąć
i omów dokładniej
mouse_event(MOUSEEVENTF_LEFTDOWN, StrToInt(Xedt.Text), StrToInt(Yedt.Text), 0, 0);
mouse_event(MOUSEEVENTF_LEFTUP, StrToInt(Xedt.Text), StrToInt(Yedt.Text), 0, 0);
co to są te zera??