HOWTO · C++

How to Detect Keypress in Windows Using C++

Compare Windows C++ APIs for checking key state and handling low-level keyboard events.

On this page

To detect keyboard input in a Windows C++ application, choose among GetKeyState while handling messages, GetAsyncKeyState when polling current key state, and a WH_KEYBOARD_LL hook when low-level keyboard events are needed. These approaches serve different tasks: text input and shortcuts are usually handled through a window’s messages, while polling and hooks suit narrower monitoring needs.

Keyboard Input in Windows Applications

Keyboard input can represent text, an application shortcut such as Ctrl+O, or a system command such as Alt+Tab. A key-state API tells you about a key; it does not turn keyboard input into text. For text, let the window’s normal character-message handling account for keyboard layout and modifier keys.

Virtual-Key Codes

A scan code identifies a physical key position reported by a keyboard. Windows maps keyboard input to a virtual-key code, which APIs use to identify a key’s meaning. Letters and digits use their character-valued virtual-key codes: A through Z map to 0x41 through 0x5A, and 0 through 9 map to 0x30 through 0x39. Other keys have named constants in WinUser.h.

Virtual-key constant Key
VK_SPACE Spacebar
VK_SHIFT Shift
VK_ESCAPE Escape
VK_LEFT Left arrow
VK_F1 F1

See Microsoft’s virtual-key code reference for the full list.

Use GetKeyState to Check the Message-Queue State

GetKeyState reports the key state associated with the calling thread’s message queue. It is useful while processing a message, when the state should correspond to that input-processing sequence; it is not a general snapshot of the physical keyboard at any arbitrary time. The high-order bit indicates whether a key is down. For example, in a window procedure, check if (message == WM_KEYDOWN && (GetKeyState(VK_SHIFT) & 0x8000)) { /* handle Shift+key */ } to test Shift while processing a key-down message. Keep this check in the message handler rather than spinning in a loop.

This distinction matters for a spacebar action in a game: an application that already processes window messages can use those messages to handle the input. The best game-input design depends on its input framework and update loop; polling is not automatically required.

Use GetAsyncKeyState to Poll a Key

Use GetAsyncKeyState when a loop needs the current down state without relying on the calling thread’s message-queue state. The high-order bit is set while the key is down. Avoid using the low-order “pressed since previous call” bit for reliable detection, because another application can consume that state. Throttle polling and compare successive states to report a press transition once.

The portable example below illustrates the transition logic using a short sequence of key-state values. In a Windows polling loop, obtain each value from GetAsyncKeyState(VK_SPACE) and test its high-order bit; throttle the loop and use a separate Escape check to exit.

#include <array>
#include <iostream>

int main() {
    bool spaceWasDown = false;
    const std::array<bool, 4> spaceStates{false, true, true, false};

    for (bool spaceIsDown : spaceStates) {
        if (spaceIsDown && !spaceWasDown) {
            std::cout << "Space pressed\n";
        }
        spaceWasDown = spaceIsDown;
    }
    return 0;
}
Space pressed

Use SetWindowsHookEx for Low-Level Keyboard Events

A WH_KEYBOARD_LL hook receives low-level keyboard events on the desktop where it is installed. The callback runs in the context of the installing process, so that thread must keep a message loop. Hooks affect system input handling and should be reserved for cases that need event-level monitoring; keep the callback short and pass unhandled events to the next hook with CallNextHookEx.

For a Windows implementation, install the callback with SetWindowsHookExW(WH_KEYBOARD_LL, ...) and check that the returned hook handle is not null before entering the message loop. In the callback, inspect KBDLLHOOKSTRUCT::vkCode for the key of interest when code >= 0 and the message is WM_KEYDOWN or WM_SYSKEYDOWN. Call CallNextHookEx for events the application does not intentionally suppress. Keep the installing thread in a GetMessageW loop, then call UnhookWindowsHookEx after the loop ends; perform that cleanup for both normal message-loop completion and a GetMessageW error. This Windows API outline is platform-specific and is not a portable runnable example. A low-level hook callback must return promptly; Windows may remove a hook that exceeds its timeout. Return the next hook’s result unless the application intentionally needs to suppress an event.

Choose the Right Key-Detection Method

Use GetKeyState when handling a window message and interpreting its queue-associated state. Use GetAsyncKeyState for a throttled poll of whether a key is currently down. Choose SetWindowsHookEx with WH_KEYBOARD_LL only when low-level event monitoring is required and the program can maintain the callback and message loop correctly. For a game spacebar action, start with the game’s existing input system and use the API that matches its event or update model.