Recent

Author Topic: How to catch key event independent from Form?  (Read 4390 times)

İbrahim

  • New Member
  • *
  • Posts: 19
How to catch key event independent from Form?
« on: October 27, 2019, 02:13:39 am »
Hi;
We can catch key events on form like this: TForm1.FormKeyPress
But this is dependent to any form. I mean it works if TForm1 is activated.
I want to catch key events globally, I mean it needs to independent from any form. It will works if Form1 is inactivated (example of: form is minimized). How can I do it? Thanks.

fabiopesaju

  • Jr. Member
  • **
  • Posts: 96
Re: How to catch key event independent from Form?
« Reply #1 on: October 27, 2019, 03:21:10 am »
it smells like a keylogger

Thaddy

  • Hero Member
  • *****
  • Posts: 18324
  • Here stood a man who saw the Elbe and jumped it.
Re: How to catch key event independent from Form?
« Reply #2 on: October 27, 2019, 09:41:47 am »
Under Windows it is really easy, but I do not know a cross-platform solution:
Code: Pascal  [Select][+][-]
  1. RegisterHotKey(Handle, <a_number>, MOD_CONTROL, <a_virtual_key_code>); // VK_**** style, you can *or* combinations
The handle is your application handle.
The number should be larger than 1024 (system reserved)
And OnCloseQuery event:
Code: Pascal  [Select][+][-]
  1. UnregisterHotKey(Handle, <same_number>);

Now you can use the global hotkey by processing the WM_HOTKEY message.
Code: Pascal  [Select][+][-]
  1.  procedure WMHotKey(var Message: TMessage); message WM_HOTKEY; // somewhere in your main form, for example

While your app is running, the hotkey will be processed by your app, whatever focus or not.
Note that you should be careful to choose the key or key combination, because other programs may need it.

See msdn for an example https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-hotkey
and https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-registerhotkey?redirectedfrom=MSDN

(I will add a pascal version of that C++ example without GUI later, just in case you are not able to do so. The above description should work with any Lazarus forms Windows application.)
« Last Edit: October 27, 2019, 10:16:47 am by Thaddy »
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

Zvoni

  • Hero Member
  • *****
  • Posts: 3135
Re: How to catch key event independent from Form?
« Reply #3 on: October 27, 2019, 10:24:45 am »
under Windows: GetAsyncKeyState-API
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad

Thaddy

  • Hero Member
  • *****
  • Posts: 18324
  • Here stood a man who saw the Elbe and jumped it.
Re: How to catch key event independent from Form?
« Reply #4 on: October 27, 2019, 10:37:34 am »
under Windows: GetAsyncKeyState-API
No. not global. That was asked and see my code.
[edit] here's the pascalized example from msdn:
Code: Pascal  [Select][+][-]
  1. {$mode objfpc}
  2. uses windows, messages;
  3. const NO_REPEAT = $4000; // Marco? missing....
  4. var
  5.   msg:Tmsg;
  6. begin
  7.   if RegisterHotKey(0,1,MOD_ALT or NO_REPEAT,$42)  { $42 is 'b'} then
  8.     writeln('Hotkey ''ALT+b'' registered, using MOD_NOREPEAT flag');
  9.   while GetMessage(msg, 0, 0, 0) do
  10.     if msg.message = WM_HOTKEY then
  11.       writeln('WM_HOTKEY received');
  12. end.

Note this does not UN-register here, and you should do that, This is simply the Pascal version of that example on msdn.
Run this program in a console, start another one, whatever, and the alt-b key will be globally intercepted.
Don't worry about the loop, that is OS scheduled...
If you want to do it from a GUI application, follow my steps as described above.

For Windows this is really simple, any tips to make it cross-platform would be appreciated.

(Last time I did such code was TSR time..)
« Last Edit: October 27, 2019, 12:24:39 pm by Thaddy »
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

Zvoni

  • Hero Member
  • *****
  • Posts: 3135
Re: How to catch key event independent from Form?
« Reply #5 on: October 27, 2019, 12:41:49 pm »
under Windows: GetAsyncKeyState-API
No. not global.

Drat. Missed that
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad

Thaddy

  • Hero Member
  • *****
  • Posts: 18324
  • Here stood a man who saw the Elbe and jumped it.
Re: How to catch key event independent from Form?
« Reply #6 on: October 27, 2019, 12:50:45 pm »
Drat. Missed that
Yes. The aircrafts I jumped out of are probably not perfectly fine anymore ( 1979 para wing, but tank commander - Centurion - https://www.youtube.com/watch?v=h1hc7T3KiWw )
Any tips to make it cross-platform?
(let's not get distracted, I provided the code above.
« Last Edit: October 27, 2019, 01:12:48 pm by Thaddy »
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

İbrahim

  • New Member
  • *
  • Posts: 19
Re: How to catch key event independent from Form?
« Reply #7 on: October 27, 2019, 02:37:51 pm »
@Thaddy Thanks. It's works. But I think GetMessage is synchronize but I want asynchronize. I wrote this code:
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs
  9.   {$IFDEF WINDOWS}
  10.     , Windows, Messages
  11.   {$ENDIF};
  12.  
  13. type
  14.  
  15.   { THotKeyThread }
  16.  
  17.   THotKeyThread = class(TThread)
  18.   private
  19.   protected
  20.     procedure Execute; override;
  21.   public
  22.     constructor Create;
  23.     destructor Destroy; override;
  24.   end;
  25.  
  26.   { TForm1 }
  27.  
  28.   TForm1 = class(TForm)
  29.     procedure FormCreate(Sender: TObject);
  30.     procedure FormDestroy(Sender: TObject);
  31.   private
  32.     HotKeyThread : THotKeyThread;
  33.   public
  34.  
  35.   end;
  36.  
  37. var
  38.   Form1: TForm1;
  39.  
  40. implementation
  41.  
  42. {$R *.lfm}
  43.  
  44. { THotKeyThread }
  45.  
  46. procedure THotKeyThread.Execute;
  47. var
  48.   msg : TMSG;
  49. begin
  50.   while GetMessage(msg, 0, 0, 0) do
  51.   begin
  52.     if msg.message = WM_HOTKEY then
  53.       ShowMessage('Ctrl+R');
  54.   end;
  55. end;
  56.  
  57. constructor THotKeyThread.Create;
  58. begin
  59.   inherited Create(True);
  60.   FreeOnTerminate := True;
  61. end;
  62.  
  63. destructor THotKeyThread.Destroy;
  64. begin
  65.   inherited;
  66. end;
  67.  
  68. { TForm1 }
  69.  
  70. procedure TForm1.FormCreate(Sender: TObject);
  71. const
  72.   NO_REPEAT = $4000;
  73. begin
  74.   if not Assigned(HotKeyThread) then
  75.     HotKeyThread := THotKeyThread.Create;
  76.   if RegisterHotKey(0, 1, MOD_CONTROL or NO_REPEAT, $52 {R}) then
  77.   begin
  78.     HotKeyThread.Start;
  79.   end;
  80. end;
  81.  
  82. procedure TForm1.FormDestroy(Sender: TObject);
  83. begin
  84.   HotKeyThread.Free;
  85.   UnregisterHotKey(0, 1);
  86. end;
  87.  
  88. end.
  89.  

But it is not working. How can I get the message as asynchronize?

@fabiopesaju
My application is just a screenshot app. The users will want to take screenshot while my screenshot app is minimized.

Thaddy

  • Hero Member
  • *****
  • Posts: 18324
  • Here stood a man who saw the Elbe and jumped it.
Re: How to catch key event independent from Form?
« Reply #8 on: October 27, 2019, 04:58:08 pm »
In that case - again assuming Windows - you need the clipbooard API. There are many examples on the web and most Delphi examples also work with Lazarus.
Also note shift-prnscn and alt-prnscn already does a screenshot and stores it on the clipboard.
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1566
    • Lebeau Software
Re: How to catch key event independent from Form?
« Reply #9 on: October 27, 2019, 08:01:13 pm »
I want to catch key events globally, I mean it needs to independent from any form. It will works if Form1 is inactivated (example of: form is minimized).

Then you need a keyboard hook via SetWindowsHookEx() or RegisterRawInputDevices().
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

Thaddy

  • Hero Member
  • *****
  • Posts: 18324
  • Here stood a man who saw the Elbe and jumped it.
Re: How to catch key event independent from Form?
« Reply #10 on: October 27, 2019, 10:01:46 pm »
Then you need a keyboard hook via SetWindowsHookEx() or RegisterRawInputDevices().
That's overkill. See my example. But these are options, although not the preferred one, in my opinion.
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

440bx

  • Hero Member
  • *****
  • Posts: 5809
Re: How to catch key event independent from Form?
« Reply #11 on: October 27, 2019, 11:46:41 pm »
But it is not working. How can I get the message as asynchronize?

@fabiopesaju
My application is just a screenshot app. The users will want to take screenshot while my screenshot app is minimized.
One reason it isn't working is because you specify a window handle of 0 (NULL) when calling RegisterHotKey() and that call is made by the thread that created Form1 (which is then the main/"current thread"), that causes the hotkey to be sent to the thread that created TForm1 instead of the other thread you created to process the hotkey.

quote from MSDN:
Quote
If the hWnd parameter is NULL, then the hot key is associated with the current thread rather than with a particular window.
The simplest solution would be to have the thread you create, instead of TForm1's thread, call RegisterHotKey() before entering its message loop.  That way the hotkey will be posted to the correct thread's message queue.

HTH.

 
« Last Edit: October 27, 2019, 11:50:44 pm by 440bx »
FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

Thaddy

  • Hero Member
  • *****
  • Posts: 18324
  • Here stood a man who saw the Elbe and jumped it.
Re: How to catch key event independent from Form?
« Reply #12 on: October 28, 2019, 06:18:17 am »
Yes, that is correct and also what I tried to describe - did describe - in my initial answer. The second answer was the msdn example ad verbatim and indeed uses 0, because that is a console app.
In the first example the message loop is not necessary, because there is already one for the application. So Application.Handle or Mainform.Handle and the message declaration.
It is really easy.
« Last Edit: October 28, 2019, 09:18:44 am by Thaddy »
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

CM630

  • Hero Member
  • *****
  • Posts: 1523
  • Не съм сигурен, че те разбирам.
    • http://sourceforge.net/u/cm630/profile/
Re: How to catch key event independent from Form?
« Reply #13 on: March 28, 2022, 08:19:39 am »
Maybe a few years later someone knows a way to catch a key globally on Linux?
Лазар 4,2 32 bit (sometimes 64 bit); FPC3,2,2

af0815

  • Hero Member
  • *****
  • Posts: 1404
Re: How to catch key event independent from Form?
« Reply #14 on: March 28, 2022, 09:38:43 am »
Keylogging on Linux:

Start with this Ubuntu Manpage https://manpages.ubuntu.com/manpages/xenial/man8/logkeys.8.html

There is a information about /dev/input/eventX

This is not a FPC Problem, you have to identify this on the Linux systems itself how it works.

 
regards
Andreas

 

TinyPortal © 2005-2018