0

I use the TApplicationEvents.OnShortCut event in Delphi 11 Alexandria with a Delphi VCL Application in Windows 10, for example:

procedure TformMain.ApplicationEvents1ShortCut(var Msg: TWMKey; var Handled: Boolean);
begin
  CodeSite.Send('TformMain.ApplicationEvents1ShortCut: Msg.CharCode', Msg.CharCode);
end;

Unfortunately, this event is even triggered when no modifier key is pressed, e.g. the "V" key or the "B" key alone. How can I exit this event handler when no modifier key is pressed, for example:

procedure TformMain.ApplicationEvents1ShortCut(var Msg: TWMKey; var Handled: Boolean);
begin
  if NoModifierKeyPressed then EXIT;
  ...
end;
user1580348
  • 5,721
  • 4
  • 43
  • 105

2 Answers2

3

You can use the GetKeyState function from unit Winapi.Windows, together with virtual key codes such as VK_CONTROL or VK_SHIFT. For example:

procedure TformMain.ApplicationEvents1ShortCut(var Msg: TWMKey; var Handled: Boolean);
begin
  if (Msg.CharCode = Ord('V')) and (GetKeyState(VK_CONTROL) < 0) then
    ShowMessage('Ctrl+V was pressed');
end;
Matthias B
  • 404
  • 4
  • 11
  • Thanks. This is an implicit solution. How to EXPLICITLY detect that NOT any modifier key is pressed - i.e. only the V key WITHOUT modifier keys is pressed? – user1580348 Nov 09 '21 at 12:48
  • `If GetKeyState(VK_CONTROL) >= 0)` then EXIT; – Freddie Bell Nov 09 '21 at 14:11
  • @user1580348 how is an *explicit* function call an *implicit* solution? – Remy Lebeau Nov 09 '21 at 15:56
  • I think the OP's concern isn't really about implicit vs explicit, but about the fact that `GetKeyState(VK_CONTROL) < 0` only tests *one particular* modifier, not every possible modifier. But of course, you can easily combine such single-modifier tests to make yourself an any-modifier test. – Andreas Rejbrand Nov 09 '21 at 19:52
  • Interesting nuance ... how to find the exhaustive list of all possible modifiers? I can list the modifiers I know (SHIFT, CTRL, maybe some more), but how do I know that there don't exist more? Maybe next year, a new keyboard with more modifier keys becomes available and Windows adds support for it, including new modifier constants. Indeed, the question is interesting ... and I don't know the answer to that. – Matthias B Nov 10 '21 at 09:21
1

Taking into account the kind comments of @RemyLebeau and @Andreas Rejbrand:

This works for me:

function NoModifierKeyPressed: Boolean;
var
  keys: TKeyboardState;
begin
  GetKeyboardState(keys);
  Result := (keys[VK_SHIFT] and $80 = 0) and (keys[VK_CONTROL] and $80 = 0) and (keys[VK_MENU] and $80 = 0);
end;
user1580348
  • 5,721
  • 4
  • 43
  • 105
  • You should be using `GetKeyState()` instead in this situation, since you are invoking this code inside of an active key handler, so look at the latest key state that has been processed by the calling thread's message queue. However, when checking multiple keys at a time, you should use a single `GetKeyboardState()` call instead of multiple `GetKeyState()` calls, eg: `function NoModifierKeyPressed: Boolean; var keys: array[0..255] of Byte; begin GetKeyboardState(keys); Result := (keys[VK_SHIFT] and $80 = 0) and (keys[VK_CONTROL] and $80 = 0) and (keys[VK_MENU] and $80 = 0); end;` – Remy Lebeau Nov 09 '21 at 18:44
  • @RemyLebeau Thanks, but the compiler says: Types of actual and formal var parameters must be identical: https://i.imgur.com/VrQQNj3.png – user1580348 Nov 09 '21 at 19:51
  • @user1580348: Write `keys: TKeyboardState` instead of `keys: array[0..255] of Byte`. – Andreas Rejbrand Nov 09 '21 at 19:56
  • @user1580348 sorry, I had written that without looking at Delphi's actual declaration of `GetKeyboardState()`: `type ...; TKeyboardState = array[0..255] of Byte; ... function GetKeyboardState(var KeyState: TKeyboardState): BOOL; stdcall;` So Andreas is correct. The actual Win32 function takes a `PBYTE`. – Remy Lebeau Nov 09 '21 at 19:59
  • Thanks to all. This seems to work now: https://i.imgur.com/F16V3eZ.png Is this correct? – user1580348 Nov 09 '21 at 20:07