From a long time go and a project that got cancelled, but maybe in the future I will need a similar thing again: back in the days not all raw key codes were readily documented or converted correctly from winuser.h
to other environments (0x45
is the keyboard raw scan code value for VK_NUMLOCK
of the Num Lock key).
[Wayback/Archive] delphi – What is the meaning of the bScan parameter value 0x45 in keybd_event? – Stack Overflow (thanks [Wayback/Archive] David Heffernan and [Wayback/Archive] kludg):
Q
[Wayback/Archive] Many examples of using [Wayback/Archive] keybd_event, have the value 0x45
for the bScan
parameter.
What is the meaning of that 0x45
value?
I was under the impression [Wayback/Archive] 0x45 was a keyboard scancode, but since it is used for a various number of keys, I’m not so sure about that any more.
My goal is to use keybd_event
either from .NET P/Invoke, or from Delphi, and make the types more restrictive (using for instance enums or flagged enums) so my code becomes easier to maintain.
A
It is indeed a scan code and for many keyboards it is the scan code for the NumLock key.
The example code attached to the documentation of keybd_event
is an example of how to toggle the NumLock state. And so naturally 0x45 is used as the scan code. My guess is that lots of the other examples that you found simply copied blindly that value from the keybd_event
MSDN example. Since [Wayback/Archive] applications typically ignore the scan code and respond to the virtual key code, it usually doesn’t matter what value is passed as the scan code.
Finally, you’ll want to use SendInput
rather than keybd_event
. The reason being that that former allows you to place a sequence of events in the queue. With keybd_event
you place the events in the queue one at a time and it’s possible that your faked events can get interspersed with real events. And that problem is one of the main reasons why SendInput
was introduced.
C
Delphi ignores scan code info, ex when generates OnKeyDown
event. If somebody needs to extract key scan codes he should handle corresponding windows messages (ex WM_KEYDOWN
) directly.
…
Thanks. I’ll give [Wayback/Archive] SendInput and [Wayback/Archive] P/Invoke SendInput a shot. Will get back later on this.
Related:
- [Wayback/Archive] Keyboard Input Overview – Win32 apps | Microsoft Learn is a very comprehensive page which in depth explains keyboard handling. Read in full. Then read it in full again as it is dense.
- [Wayback/Archive] keybd_event function (winuser.h) – Win32 apps | Microsoft Docs
Examples
The following sample program toggles the NUM LOCK light by using keybd_event with a virtual key of VK_NUMLOCK. It takes a Boolean value that indicates whether the light should be turned off (FALSE) or on (TRUE). The same technique can be used for the CAPS LOCK key (VK_CAPITAL) and the SCROLL LOCK key (VK_SCROLL).
#include <windows.h>
void SetNumLock( BOOL bState )
{
BYTE keyState[256];
GetKeyboardState((LPBYTE)&keyState);
if( (bState && !(keyState[VK_NUMLOCK] & 1)) ||
(!bState && (keyState[VK_NUMLOCK] & 1)) )
{
// Simulate a key press
keybd_event( VK_NUMLOCK,
0x45,
KEYEVENTF_EXTENDEDKEY | 0,
0 );
// Simulate a key release
keybd_event( VK_NUMLOCK,
0x45,
KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP,
0);
}
}
void main()
{
SetNumLock( TRUE );
}
- Tables with raw keyboard scan codes are in:
- [Wayback/Archive] Keyboard inputs – scancodes, raw input, text input, key names | Handmade Network
…
How to get scancodes
First let’s start by saying that you can use both scancodes and virtual keys at the same time. It’s probably a good feature to have both.
When you receive a WM_KEYDOWN
, WM_KEYUP
, WM_SYSKEYDOWN
, WM_SYSKEYUP
message the lParam parameter contains the scancode in the 16 – 23 bits and the 24th bit indicates if the scancode is 1 or 2 bytes (extended). If the scancode is extended, you just need to add 0xE000 or 0xE100.
There are a few catches:
VK_PAUSE
will give you a value of 0x45, but when using raw input it is 0xE11D 0x45 (the value in the list)
VK_NUMLOCK
will give you a value of 0xE045, but when using raw input it is 0x45 (the value in the list)
- Some keys change the scancode value if shift, alt, control (left or right) or a combination of those keys are pressed. The only visible change in the
WM_KEYDOWN
… messages are VK_PAUSE
that generates 0x0E46 (sc_cancel
, or “Break”) when control is pressed and VK_SNAPSHOT
(print screen) that will give you a value of 0x54 if alt is pressed. We could use 0x54 and consider it a different key, but windows doesn’t have a text name for the scancode value of 0x54 so I chose to force it to the same value as print screen
A few things to know:
- There are no
WM_KEYDOWN
for the print screen key and so if you hold the key down it’s never repeated
- There are no break code for “Pause” so I don’t know how the key down/up is expected to work (I remember reading somewhere that the key should be consider up as soon as the down message is sent, but I can’t find it back). Raw input sends “keydown” and “keyup” message, and it appears that the keyup message is sent directly after the keydown message (you can’t hold the key down) so depending on when
GetMessage
or PeekMessage
are called, you may get both a keydown and keyup message “in the same frame”. If you use VK messages most of the time you only get keydown messages, but some times you get a keyup message too
- According to the Microsoft
scancode.doc
(links above), pause is the only key that never repeats when held down (Print screen reapeats if you use raw input)
- The right alt key (Alt Gr which stands for Alternate Graphics) sends a alt message and a control message
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
case WM_SYSKEYDOWN:
case WM_SYSKEYUP:
case WM_KEYDOWN:
case WM_KEYUP: {
unsigned int scancode = ( message.lParam >> 16 ) & 0xff;
unsigned int extended = ( message.lParam >> 24 ) & 0x1;
if ( extended ) {
if ( scancode != 0x45 ) {
scancode |= 0xE000;
}
} else {
if ( scancode == 0x45 ) {
scancode = 0xE11D45;
} else if ( scancode == 0x54 ) {
scancode = 0xE037;
}
}
// Get the key state and store it...
}
|
- [Wayback/Archive] PS/2 Keyboard – OSDev Wiki
…
Scan codes themselves are sequences of one or more bytes. In some cases the sequence can be as many as 6 bytes (e.g. the Pause/Break key in scan code set 1 generates the sequence 0xE1, 0x1D, 0x45, 0xE1, 0x9D, 0xC5 when pressed). This situation isn’t really ideal. In general (for later processing) you want to convert these “one or more byte sequences” into a single integer that uniquely identifies a specific key, that can be used effectively in things like lookup tables (without having sparsely used “many GiB” lookup tables).
…
Scan Code Set 1:
…
…
0xE1, 0x1D, 0x45, 0xE1, 0x9D, 0xC5 |
pause pressed |
…
Scan Code set 2:
…
…
0xF0, 0x45 |
0 (zero) released |
…
–jeroen
Leave a comment