Cześć,
piszę sobie pewien program, który posiada komponent TMainMenu1. W tym menu są pozycje, które posiadają skróty klawiszowe (Shortcut). Moje pytanie brzmi - jak sprawić aby te skróty działały nawet gdy okno programu jest nieaktywne?
Pozdrawiam!
Cześć,
piszę sobie pewien program, który posiada komponent TMainMenu1. W tym menu są pozycje, które posiadają skróty klawiszowe (Shortcut). Moje pytanie brzmi - jak sprawić aby te skróty działały nawet gdy okno programu jest nieaktywne?
Pozdrawiam!
Skróty klawiszowe dla menu głównego i kontekstowych działają wyłącznie gdy okno jest odblokowane i zfokusowane – takie jest założenie i tak to musi działać. Jeśli z jakiegoś powodu potrzebujesz wywołać jakieś zdarzenie podczas gdy okno jest zablokowane (np. z powodu otwarcia okna dialogowego), to wywołaj je bezpośrednio, nie za pomocą hotkey'ów.
Nie próbuj zmieniać standardowego zachowania interfejsu, bo użytkownik na pewno nie będzie z tego powodu zadowolony. A najlepiej to napisz co konkretnie Twój program ma robić – wtedy się zobaczy czy problemem jest interfejs, czy jednak niepoprawna architektura.
Cześć, dzięki za odpowiedź :) Jeśli chodzi o mój problem, to tworzę automat do wykonywania prostych czynności i utworzyłam zadania, które dopisuję do listy zadań (np. kliknij w tym miejscu lewym klawiszem myszy). Te operacje działają na współrzędnych po których porusza się kursor i za pomocą skrótów klawiszowych dodaję sobie zadania do listy. Problem pojawia się wtedy, gdy wykonuję operację typu PPM->Nowy->Folder i moja aplikacja traci focus. Skrót klawiszowy nie działa i chciałabym wiedzieć w jaki sposób to naprawić.
Pozdrawiam!
OK problem rozwiązany - pomógł mi artykuł: https://www.swissdelphicenter.ch/en/showcode.php?id=147
Pozdrawiam!
W takim razie nie rozumiem do czego służą skróty klawiszowe i menu Twojego programu. Jeśli Twój ”clicker” ma wykonać jakieś zadanie (dodane do listy) to powinien przesuwać kursor i wywoływać zdarzenia myszy/klawiatury za pomocą SendInput lub od biedy mouse_event i keybd_event, dzięki czemu komunikaty zawsze będą dostarczane do aktywnego okna (i dostępne dla wszystkich innych programów nasłuchujących za pomocą hooków).
No nic, skoro masz już rozwiązanie i Cię zadowala to nie będę drążył tematu. ;)
furious programming napisał(a):
W takim razie nie rozumiem do czego służą skróty klawiszowe i menu Twojego programu. Jeśli Twój ”clicker” ma wykonać jakieś zadanie (dodane do listy) to powinien przesuwać kursor i wywoływać zdarzenia myszy/klawiatury za pomocą
SendInputlub od biedymouse_eventikeybd_event, dzięki czemu komunikaty zawsze będą dostarczane do aktywnego okna (i dostępne dla wszystkich innych programów nasłuchujących za pomocą hooków).No nic, skoro masz już rozwiązanie i Cię zadowala to nie będę drążył tematu. ;)
Moj program ma rejestrować w którym miejscu ktos chce kliknąć i zapisywać to miejsce do kolejki zadań i poki co to działa
@robertz68 nie sądzę, ponieważ w onDestroy wszystko wraca do normy a mi te skróty nie są potrzebne podczas działania programu
Dzieki za odpowiedzi
Kaska1988 napisał(a):
furious programming napisał(a):
W takim razie nie rozumiem do czego służą skróty klawiszowe i menu Twojego programu. Jeśli Twój ”clicker” ma wykonać jakieś zadanie (dodane do listy) to powinien przesuwać kursor i wywoływać zdarzenia myszy/klawiatury za pomocą
SendInputlub od biedymouse_eventikeybd_event, dzięki czemu komunikaty zawsze będą dostarczane do aktywnego okna (i dostępne dla wszystkich innych programów nasłuchujących za pomocą hooków).No nic, skoro masz już rozwiązanie i Cię zadowala to nie będę drążył tematu. ;)
Moj program ma rejestrować w którym miejscu ktos chce kliknąć i zapisywać to miejsce do kolejki zadań i poki co to działa
W miejscu czego?
Chodzi o pulpit Windows, dowolną aplikację Windows czy może całą Twoją aplikację, albo tez jakiś konkretny kontener w Twojej aplikacji (jak mazanie myszą po PaintBox)?
@robertz68 nie sądzę, ponieważ w onDestroy wszystko wraca do normy a mi te skróty nie są potrzebne podczas działania programu
Tego już w ogóle nie rozumiem...
To po co ci skróty klawiaturowe i ich obsługa, kiedy Twoja apka nie działa?
Jednak precyzja wypowiedzi będzie kluczowa dla jakości odpowiedzi.
Kaska1988 napisał(a):
Moj program ma rejestrować w którym miejscu ktos chce kliknąć i zapisywać to miejsce do kolejki zadań i poki co to działa
Aha, czyli chodzi nie o symulowanie akcji użytkownika (odtwarzanie zaprogramowanej listy czynności), a o jej przygotowywanie. Użytkownik np. umeiszcza kursor w danym miejscu, wciska kombinację klawiszy i Twoja aplikacja dodaje akcję do listy czynności danego zadania.
Skoro tak to spoko – globalne hotkey'e nadają się do tego idealnie. Można też hookiem jakby co. ;)
Dokładnie, jest przygotowywana kolejka zadań do wykonania z parametrami do działania (np. właśnie współrzędne w które użytkownik chce kliknąć) :)
Tylko pojawił się kolejny problem, który nie bardzo rozumiem. Przygotowałam sobie skróty klawiszowe do zadań od F1 do F12. Wszystkie działają... poza F12. I nie wiem dlaczego F12 nie działa. Oto kod:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
id1, id2, id3, id4, id5, id6, id7, id8, id9, id10, id11, id12: Integer;
procedure WMHotKey(var Msg: TWMHotKey); message WM_HOTKEY;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
// HotKey F1
id1 := GlobalAddAtom('HotKey1');
RegisterHotKey(Handle, id1, 0{MOD_CONTROL}, VK_F1);
// HotKey F2
id2 := GlobalAddAtom('HotKey2');
RegisterHotKey(Handle, id2, 0{MOD_ALT + MOD_SHIFT}, VK_F2{VK_A});
// HotKey F3
id3 := GlobalAddAtom('HotKey3');
RegisterHotKey(Handle, id3, 0{MOD_WIN}, VK_F3{VK_A});
// HotKey F4
id4 := GlobalAddAtom('HotKey4');
RegisterHotKey(Handle, id4, 0, VK_F4{VK_SNAPSHOT});
// HotKey F5
id5 := GlobalAddAtom('HotKey5');
RegisterHotKey(Handle, id5, 0{MOD_CONTROL}, VK_F5);
// HotKey F6
id6 := GlobalAddAtom('HotKey6');
RegisterHotKey(Handle, id6, 0{MOD_CONTROL}, VK_F6);
// HotKey F7
id7 := GlobalAddAtom('HotKey7');
RegisterHotKey(Handle, id7, 0{MOD_CONTROL}, VK_F7);
// HotKey F8
id8 := GlobalAddAtom('HotKey8');
RegisterHotKey(Handle, id8, 0{MOD_CONTROL}, VK_F8);
// HotKey F9
id9 := GlobalAddAtom('HotKey9');
RegisterHotKey(Handle, id9, 0{MOD_CONTROL}, VK_F9);
// HotKey F10
id10 := GlobalAddAtom('HotKey10');
RegisterHotKey(Handle, id10, 0{MOD_CONTROL}, VK_F10);
// HotKey F11
id11 := GlobalAddAtom('HotKey11');
RegisterHotKey(Handle, id11, 0{MOD_CONTROL}, VK_F11);
// HotKey F12
id12 := GlobalAddAtom('HotKey12');
RegisterHotKey(Handle, id12, 0{MOD_CONTROL}, VK_F12);
end;
//--------------- HOT KEYE ---------------------
procedure TForm1.WMHotKey(var Msg: TWMHotKey);
var
MausPos: TPoint;
begin
if Msg.HotKey = id1 then
begin
ShowMessage('F1');
end;
if Msg.HotKey = id2 then
begin
ShowMessage('F2');
end;
if Msg.HotKey = id3 then
begin
ShowMessage('F3');
end;
if Msg.HotKey = id4 then
begin
ShowMessage('F4');
end;
if Msg.HotKey = id5 then
begin
ShowMessage('F5');
end;
if Msg.HotKey = id6 then
begin
ShowMessage('F6');
end;
if Msg.HotKey = id7 then
begin
ShowMessage('F7');
end;
if Msg.HotKey = id8 then
begin
ShowMessage('F8');
end;
if Msg.HotKey = id9 then
begin
ShowMessage('F9');
end;
if Msg.HotKey = id10 then
begin
ShowMessage('F10');
end;
if Msg.HotKey = id11 then
begin
ShowMessage('F11');
end;
if Msg.HotKey = id12 then
begin
ShowMessage('F12');
end;
end;
//----------------------------------
end.
Czy ktoś z Was widzi dlaczego klawisz F12 nie działa?
A ten klawisz przypadkiem nie koliduje ze skrótami IDE? U siebie – co prawda pod Lazarusem, ale to jeden pies – gdy program jest uruchomiony pod debuggerem, wciśnięcie klawisza F12 powoduje zatrzymanie pracy programu i przejście do debugowania assemblerka. Może w tym leży problem, bo błędu po stronie kodu nie widać. W razie czego sprawdź poza IDE czy będzie tak samo.
Poza tym, jeśli już używasz funkcji z systemowego API, to waliduj rezultaty wywołań. Ja wiem, że to pewnie PoC, ale warto o tym wspomnieć. Sprawdź czy hotkey F12 jest w ogóle rejestrowany – w razie sukcesu, rezultat będzie niezerowy.
Odpaliłam program poza IDE i niestety wciąż nie działa. Spróbuję jeszcze na innym komputerze, bo @wloochacz pisał że u niego działa. A co do reszty co napisałeś to nie wiem o czym mówisz :D Sorki ale nie jestem pro z Delphi :p
PS. Sprawdzałam też na innym komputerze - również nie działa.
Często ludzie w takich prostych programach jak auto kliker i innych podobynch korzystają z systemowych funkcji, które piszą do okien, które są na wierzchu.
Sendinput, sendmessage, vb_key.
Do czytania też są api jedno jest takie co czyta globalnie czasem do keyloggerów ludzie tworzą, getAsyncKeyState, możesz wypisywać w pętli każdy wcisnięty klawisz i zobaczyć czy jest rejestrowany, może coś event przechwytuje i nie podaje dalej, systemy i aplikacje muszę podać event dalej, ale można zatrzymać go i nikt więcej się nie dowie o tym.
Metoda wypisywania wszystkich klawiszy jest o tyle dobra, że jest to prawie jak debugowanie.
Kaska1988 napisał(a):
Odpaliłam program poza IDE i niestety wciąż nie działa. […] A co do reszty co napisałeś to nie wiem o czym mówisz :D Sorki ale nie jestem pro z Delphi :p
Chodzi o to, że funkcje z WinAPI zwracają jakieś wartości, które należy sprawdzać.
Ty radośnie wołasz RegisterHotKey, ale nie sprawdzasz co ta funkcja zwróciła, więc nie wiadomo czy skrót został zarejestrowany czy nie. A dokumentacja jasno podaje, że skrót może nie zostać zarejestrowany i wtedy funkcja zwróci wartość zerową (albo inaczej False). ;)
Spróbuj w ten sposób:
if not RegisterHotKey(Handle, id12, 0{MOD_CONTROL}, VK_F12) then
ShowMessage('F12 cannot be registered');
i sprawdź czy wyskoczy komunikat błędu.
@wloochacz: myślałem że wszystko stało się jasne po odpowiedzi @furious programming i mojej aprobacie, ale odpowiem jeszcze raz - piszę program, który ma taki komponent memo. Do tego memo (za pomocą wspomnianych wcześniej skrótów) dodaję stringami zdefiniowane przeze mnie komendy. Komendy, np przy pomocy buttonów i po odpowiedniej analizie, będą później wykonywane. I teraz jak chcę kliknąć na pulpicie ikonkę to najeżdżam na nią kursorem myszy i klikam przykładowo F1 i bach - w memo pojawia się zapis w stylu kliknijLPM:50,200. I odpowiednia procedurka przeanalizuje ten zapis który powie jej, że ma kliknąć LPM w miejscu o współrzędnych 50, 200. I tak mam zaprogramowanych kilka czynności. Wszystko generalnie śmiga, tylko ten F12 bardzo mnie boli :(
furious programming napisał(a):
Kaska1988 napisał(a):
Odpaliłam program poza IDE i niestety wciąż nie działa. […] A co do reszty co napisałeś to nie wiem o czym mówisz :D Sorki ale nie jestem pro z Delphi :p
Chodzi o to, że funkcje z WinAPI zwracają jakieś wartości, które należy sprawdzać.
Ty radośnie wołasz
RegisterHotKey, ale nie sprawdzasz co ta funkcja zwróciła, więc nie wiadomo czy skrót został zarejestrowany czy nie. A dokumentacja jasno podaje, że skrót może nie zostać zarejestrowany i wtedy funkcja zwróci wartość zerową (albo inaczejFalse). ;)Spróbuj w ten sposób:
if not RegisterHotKey(Handle, id12, 0{MOD_CONTROL}, VK_F12) then ShowMessage('F12 cannot be registered');i sprawdź czy wyskoczy komunikat błędu.
Uuu podziałało. Faktycznie nie zarejestrował F12 (pojawił się komunikat) :/ Co mogę teraz zrobić?
Kaska1988 napisał(a):
Faktycznie nie zarejestrował F12 (pojawił się komunikat) :/ Co mogę teraz zrobić?
Hmm… GetLastError? ;)
Najpierw pobierz numer błędu ww. funkcją, a następnie uzyskaj treść błędu za pomocą FormatMessage – tak jak piszą w dokumentacji. A jak już będziesz miała treść to sobie ją wyświetl używjąc ShowMessage.
Nie wiem czy dobrze robię, ale wpisując kod
ShowMessage(IntToStr(GetLastError));
Otrzymuję kod "0".
Natomiast kod
ShowMessage(SysErrorMessage(GetLastError));
podaje mi komunikat "Operacja ukończona pomyślnie"
Nie o to chodziło. W przypadku gdy RegisterHotKey zwróci 0/False (zależnie od implementacji importu funkcji), należy wywołać GetLastError i zapisać kod błędu do zmiennej liczbowej, następnie wywołać FormatMessage do konwersji kodu błędu na ciąg znaków, a ten wynikowy ciąg np. wyświetlić sobie za pomocą ShowMessage.
Albo zrobić tak jak w drugim przykładzie, ale po spełnieniu warunku:
if not RegisterHotKey(Handle, id12, 0{MOD_CONTROL}, VK_F12) then
ShowMessage(SysErrorMessage(GetLastError()));
Mimo wszystko i tak sugeruję nie używać jednoklawiszowych hotkey'ów, bo istnieje zbyt duże prawdopodobieństwo, że aktywne okno może ten klawisz przetwarzać i wykonywać jakieś operacje, których nie chciałaś przeprowadzać. Skorzystaj choćby z jednego klawisza specjalnego, np. z Ctrl.
@furious programming dzięki za wyjaśnienie :) BTW zastosowałam Twój kod i dostałam komunikat "Klawisz dostępu jest już zarejestrowany". Będę próbować z CTRL ;)
OK z CTRL wszystko działa :) Dzięki i pozdrawiam!