66 Votes

Delphi: System-Wide HotKey

Tutorial by Stefan Trost | Last update on 2021-06-21 | Created on 2014-01-07

In this tutorial I will show you how to register a hotkey for your Delphi application that can be used system wide (see the comments for a solution working in Lazarus). This means that the program gets a message about pressing a certain key combination, even if the program does not have the focus.

A typical application could be a program that only has an icon in the Windows Systray and can be brought out using a unique combination of keys (such as CTRL + ALT + X).

Normally, only the program currently having the focus can receive key strokes. Therefore, if an application should also react to events when actually another program is operated, we need a workaround.

For example, this can be that we register the key or key combination as a hotkey in Windows. If this registered key combination is then pressed, Windows knows the score and sends a notification message that the button was pressed to our application.

As you can imagine, a difficulty grows from the fact that we need a unique key combination. If trying to register a key combination that is already in use by another program, this will not work. Also, we should not register any key or key combination that is needed in another program.

But first, we should look how we can register such a hotkey.

ID of our Hot Keys

First, each of our hotkeys we would like to register need a unique ID with which we can recognize it later. We declare an arbitrary integer variable in the "private" section of our Unit for this:

private
  { Private declarations }
  HotKeyID: Integer;
  HotKeyIDFunctionA, HotKeyIDFunctionB: Integer;
  HotKeyIDPrintKey: Integer;

For each hotkey, we want to register, we need a separate variable. If we only want to register a single hotkey, the variable "HotKeyID" is enough. When registering several hotkeys, we should use the corresponding function or the key that is pressed as name, so that we do not get confused when working with it later.

Register Hot Key

Next, we will ensure that our Hot Keys will be registered in the Windows system when our program is started. We write the code for this into the OnCreate event of our Form.

The registration always runs the same. First, we are using the function GlobalAddAtom letting Windows to create a unique ID that we can store in our HotKeyID variable (because we need it again later). Instead of using the GlobalAddAtom function, we could also use any arbitrary integer value like "1" or "2". But we should not do so, because another program can also try to choose these values making our registration not working.

After we got the unique ID by Windows, we use the RegisterHotKey function to register our HotKey. We have to pass  to this function the handle of our program (so that Windows knows, to which program the message of the pressed key combination should be send), the unique ID and the key combination we want to register.

procedure TForm1.FormCreate(Sender: TObject);
const
  VK_A = $41;
begin
  // HotKey CTRL + F1
  HotKeyID := GlobalAddAtom('HotKey1');
  RegisterHotKey(Handle, HotKeyID, MOD_CONTROL, VK_F1);

  // HotKey ALT + SHIFT + A
  HotKeyIDFunctionA := GlobalAddAtom('HotKey2');
  RegisterHotKey(Handle, HotKeyIDFunctionA, MOD_ALT + MOD_SHIFT, VK_A);

  // HotKey Windows-Key + A
  HotKeyIDFunctionB := GlobalAddAtom('HotKey3');
  RegisterHotKey(Handle, HotKeyIDFunctionA, MOD_WIN, VK_A);

  // HotKey Print Key
  HotKeyIDPrintKey := GlobalAddAtom('HotKey4');
  RegisterHotKey(Handle, HotKeyIDPrintKey, 0, VK_SNAPSHOT);
end;

The key combination is passed in two parameters. With the first parameter the ALT, CTRL, SHIFT or the Windows Key can be registered. These keys can be specified with the constants MOD_ALT, MOD_CONTROL, MOD_SHIFT and MOD_WIN having the values 1, 2, 4 and 8. If several of these keys are pressed simultaneously, the corresponding values are added. So, for example, MOD_ALT + MOD_CONTROL (1+2) for the simultaneous pressing of ALT and CTRL (ALT + SHIFT + A in the example). If passing a 0, only the second parameter counts for the hotkey (Print button in the example).

The second parameter is the virtual key code of the other key pressed. So for example VK_SNAPSHOT for the print button, VK_F1 for F1 or $41 for the letter A.

The RegisterHotKey function returns false if the registration failed. If necessary, you can still catch this case at this point.

Delete Hot Key Registration

When our program is closed, we should ensure that our registered hotkeys are also deleted from the system.

procedure TForm1.FormDestroy(Sender: TObject);
begin
  UnRegisterHotKey(Handle, HotKeyID);
  GlobalDeleteAtom(HotKeyID);

  UnRegisterHotKey(Handle, HotKeyIDFunctionA);
  GlobalDeleteAtom(HotKeyIDFunctionA);

  UnRegisterHotKey(Handle, HotKeyIDFunctionB);
  GlobalDeleteAtom(HotKeyIDFunctionB);

  UnRegisterHotKey(Handle, HotKeyIDPrintKey);
  GlobalDeleteAtom(HotKeyIDPrintKey);
end;

For this, we are calling the functions UnRegisterHotKey and GlobalDeleteAtom. Here, we have to pass the handle of our program and the ID of our hotkey, we want to unregister.

React to HotKey in the Program

Finally, we come to the most important part. So far, we have given our hot keys unique IDs and we have registered them, but our program is not yet responding to them.

To respond to a registered hotkey, we must receive the Windows message that Windows sends us to our application, when an appropriately registered key has been pressed.

For this, we declare the function WMHotKey in the private area of our unit:

private
   { Private declarations }
   procedure WMHotKey(var Msg: TWMHotKey); message WM_HOTKEY;

The code for this function might look like this:

procedure TForm1.WMHotKey(var Msg: TWMHotKey);
begin
  if Msg.HotKey = HotKeyID          then ShowMessage('CTRL + F1');
  if Msg.HotKey = HotKeyIDFunctionA then ShowMessage('ALT + SHIFT + A');
  if Msg.HotKey = HotKeyIDFunctionB then ShowMessage('WIN + A');
  if Msg.HotKey = HotKeyIDPrintKey  then ShowMessage('PRINT KEY');
end;

In Msg.HotKey, the ID of the hotkey that called the function is stored. That is, we only need to query which of our IDs is stored in Msg.HotKey to know which hotkey has been pressed and we can run the code that should be executed when pressing the hotkey.

In this example, we only give a ShowMessage() message, but of course, it would also be possible to call any other function or any other code.

ReplyPositiveNegative
55 Votes

If you want to implement this tutorial in Lazarus, you probably will be faced with an error message that TWMHotKey is not defined:

Error: Identifier not found "TWMHotKey"

To nevertheless be able to run the code, you have to replace TWMHotKey

procedure WMHotKey(var Msg:TWMHotkey);message WM_HOTKEY;

by TMessage:

procedure WMHotKey(var Msg:TMessage);message WM_HOTKEY;

TMessage does not provide the property "HotKey", but we can use "wParam" instead:

procedure TForm1.WMHotKey(var Msg: TMessage);
begin
  if Msg.wParam = HotKeyID          then ...
  if Msg.wParam = HotKeyIDFunctionA then ... 
  if Msg.wParam = HotKeyIDFunctionB then ...
  if Msg.wParam = HotKeyIDPrintKey  then ...
end;

Don't forget: You have to add the unit "Windows" to your USES section.

So, you should be able to register a system wide hotkey in Windows using Lazarus.
2014-08-16 at 15:40

ReplyPositive Negative
Reply

About the Author

AvatarYou can find Software by Stefan Trost on sttmedia.com. Do you need an individual software solution according to your needs? - sttmedia.com/contact
Show Profile

 

Related Topics

VirtualBox: Change Date and Time

Tutorial | 10 Comments

Important Note

Please note: The contributions published on askingbox.com are contributions of users and should not substitute professional advice. They are not verified by independents and do not necessarily reflect the opinion of askingbox.com. Learn more.

Participate

Ask your own question or write your own article on askingbox.com. That’s how it’s done.