Terminal sends global key event with invalid VK code #19748

Open
opened 2026-01-31 06:52:31 +00:00 by claunia · 6 comments
Owner

Originally created by @jcmoyer on GitHub (Apr 20, 2023).

Windows Terminal version

1.16.10261.0

Windows build number

10.0.19044.1766

Other Software

Snippet from a larger piece of software that was crashing on an assert:

#include <Windows.h>
#include <iostream>

// Implemented following the guidelines here:
// https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-kbdllhookstruct

LRESULT CALLBACK key_hook(int nCode, WPARAM wParam, LPARAM lParam)
{
    if (nCode < 0) {
        return CallNextHookEx(0, nCode, wParam, lParam);
    }

    auto& event_info = *(PKBDLLHOOKSTRUCT)lParam;

    // This assertion is triggering:
    // assert(event_info.vkCode > 0 && event_info.vkCode < 255);

    if (!(event_info.vkCode > 0 && event_info.vkCode < 255)) {
        std::cerr << "!!! event_info.vkCode out of range !!!\n";
        std::cerr << "event_info dump:\n";
        std::cerr << "    .time  = " << event_info.time << "\n";
        std::cerr << "    .flags = " << event_info.flags << "\n";
        std::cerr << "    .scanCode = " << event_info.scanCode << "\n";
        std::cerr << "    .vkCode = " << event_info.vkCode << "\n";
        std::cerr << "    .dwExtraInfo = " << event_info.dwExtraInfo << "\n";
        std::cerr << "wParam = " << wParam << "\n";
    }

    return CallNextHookEx(0, nCode, wParam, lParam);
}

int main() {
    HHOOK hk = SetWindowsHookEx(WH_KEYBOARD_LL, key_hook, 0, 0);
    if (!hk) {
        std::cerr << "SetWindowsHookEx failed\n";
        return 1;
    }
    for (;;) {
        MSG msg;
        BOOL status = GetMessage(&msg, 0, 0, 0);
        if (status == -1) {
            std::cerr << "fatal: GetMessage() returned -1" << std::endl;
            break;
        }
        else if (msg.message == WM_QUIT) {
            break;
        }
        else {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    UnhookWindowsHookEx(hk);
    return 0;
}

Steps to reproduce

  1. Compile above: cl /c /EHsc hook.cpp && link hook.obj user32.lib
  2. Run program as administrator so it can install the hook
  3. Open terminal
  4. Drag the single terminal tab outside of the window so it displays a cancel icon
  5. Rarely, the program will print events like:
!!! event_info.vkCode out of range !!!
event_info dump:
    .time  = 196871921
    .flags = 18
    .scanCode = 0
    .vkCode = 0
    .dwExtraInfo = 0
wParam = 256
!!! event_info.vkCode out of range !!!
event_info dump:
    .time  = 197479750
    .flags = 18
    .scanCode = 0
    .vkCode = 0
    .dwExtraInfo = 0
wParam = 256

I haven't worked out how to reproduce this reliably yet. It seems to mostly occur after leaving terminal minimized for a while in the background then coming back to it then trying to drag a tab.

Expected Behavior

MSDN states here: "vkCode ... must be a value in the range 1 to 254."
https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-kbdllhookstruct?redirectedfrom=MSDN

Zero is also not listed on the VK code table here:
https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes

Based on this documentation, I would either expect an event to not be caught, or for an event to be caught that has a non-zero vkCode.

Actual Behavior

A key event with a VK code of zero is caught.

Originally created by @jcmoyer on GitHub (Apr 20, 2023). ### Windows Terminal version 1.16.10261.0 ### Windows build number 10.0.19044.1766 ### Other Software Snippet from a larger piece of software that was crashing on an assert: ```cpp #include <Windows.h> #include <iostream> // Implemented following the guidelines here: // https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-kbdllhookstruct LRESULT CALLBACK key_hook(int nCode, WPARAM wParam, LPARAM lParam) { if (nCode < 0) { return CallNextHookEx(0, nCode, wParam, lParam); } auto& event_info = *(PKBDLLHOOKSTRUCT)lParam; // This assertion is triggering: // assert(event_info.vkCode > 0 && event_info.vkCode < 255); if (!(event_info.vkCode > 0 && event_info.vkCode < 255)) { std::cerr << "!!! event_info.vkCode out of range !!!\n"; std::cerr << "event_info dump:\n"; std::cerr << " .time = " << event_info.time << "\n"; std::cerr << " .flags = " << event_info.flags << "\n"; std::cerr << " .scanCode = " << event_info.scanCode << "\n"; std::cerr << " .vkCode = " << event_info.vkCode << "\n"; std::cerr << " .dwExtraInfo = " << event_info.dwExtraInfo << "\n"; std::cerr << "wParam = " << wParam << "\n"; } return CallNextHookEx(0, nCode, wParam, lParam); } int main() { HHOOK hk = SetWindowsHookEx(WH_KEYBOARD_LL, key_hook, 0, 0); if (!hk) { std::cerr << "SetWindowsHookEx failed\n"; return 1; } for (;;) { MSG msg; BOOL status = GetMessage(&msg, 0, 0, 0); if (status == -1) { std::cerr << "fatal: GetMessage() returned -1" << std::endl; break; } else if (msg.message == WM_QUIT) { break; } else { TranslateMessage(&msg); DispatchMessage(&msg); } } UnhookWindowsHookEx(hk); return 0; } ``` ### Steps to reproduce 1. Compile above: `cl /c /EHsc hook.cpp && link hook.obj user32.lib` 2. Run program as administrator so it can install the hook 3. Open terminal 4. Drag the single terminal tab outside of the window so it displays a cancel icon 5. Rarely, the program will print events like: ``` !!! event_info.vkCode out of range !!! event_info dump: .time = 196871921 .flags = 18 .scanCode = 0 .vkCode = 0 .dwExtraInfo = 0 wParam = 256 !!! event_info.vkCode out of range !!! event_info dump: .time = 197479750 .flags = 18 .scanCode = 0 .vkCode = 0 .dwExtraInfo = 0 wParam = 256 ``` I haven't worked out how to reproduce this reliably yet. It seems to mostly occur after leaving terminal minimized for a while in the background then coming back to it then trying to drag a tab. ### Expected Behavior MSDN states here: "`vkCode` ... must be a value in the range 1 to 254." https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-kbdllhookstruct?redirectedfrom=MSDN Zero is also not listed on the VK code table here: https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes Based on this documentation, I would either expect an event to not be caught, or for an event to be caught that has a non-zero `vkCode`. ### Actual Behavior A key event with a VK code of zero is caught.
claunia added the Issue-BugArea-InputProduct-Terminal labels 2026-01-31 06:52:31 +00:00
Author
Owner

@jcmoyer commented on GitHub (Apr 21, 2023):

Found a reliable repro with Steam:

  1. Open steam friends list
  2. Drag terminal tab (doesn't need to be dragged anywhere in particular, just starting the drag is enough)
  3. VK=0 event fires

Repeating the drag has no effect until the friends list is closed and reopened.

@jcmoyer commented on GitHub (Apr 21, 2023): Found a reliable repro with Steam: 1. Open steam friends list 2. Drag terminal tab (doesn't need to be dragged anywhere in particular, just starting the drag is enough) 3. VK=0 event fires Repeating the drag has no effect until the friends list is closed and reopened.
Author
Owner

@zadjii-msft commented on GitHub (Apr 24, 2023):

This doesn't sound like anything coming from our code. I suspect this is an artifact of the modern drag-drop platform itself. Lemme look up if there's a good place to file this feedback. Maybe the WASDK repo?

as an aside, I probably wouldn't assert on an invalid VK. I'd just ignore it. 0 seems like a pretty sensible "this was not actually a key event" message.

@zadjii-msft commented on GitHub (Apr 24, 2023): This doesn't sound like anything coming from our code. I suspect this is an artifact of the modern drag-drop platform itself. Lemme look up if there's a good place to file this feedback. Maybe the WASDK repo? as an aside, I probably wouldn't `assert` on an invalid VK. I'd just ignore it. `0` seems like a pretty sensible "this was not actually a key event" message.
Author
Owner

@zadjii-msft commented on GitHub (Apr 24, 2023):

Interestingly, we've seen this before ourselves in #11306, though, I dunno if we ever root-caused it.

@zadjii-msft commented on GitHub (Apr 24, 2023): Interestingly, we've seen this before ourselves in #11306, though, I dunno if we ever root-caused it.
Author
Owner

@lhecker commented on GitHub (Apr 29, 2023):

This issue was easily and immediately reproducible with Windows Terminal 1.16 but I'm having trouble reproducing it with the current main branch. Maybe the upgrade to WinUI 2.8 fixed it? Unless this is a fluke, I think this will be fixed in Windows Terminal Preview 1.18.

@lhecker commented on GitHub (Apr 29, 2023): This issue was easily and immediately reproducible with Windows Terminal 1.16 but I'm having trouble reproducing it with the current main branch. Maybe the upgrade to WinUI 2.8 fixed it? Unless this is a fluke, I think this will be fixed in Windows Terminal Preview 1.18.
Author
Owner

@zadjii-msft commented on GitHub (Jul 5, 2023):

@jcmoyer You seeing this still with Terminal Preview 1.18/?

@zadjii-msft commented on GitHub (Jul 5, 2023): @jcmoyer You seeing this still with Terminal Preview 1.18/?
Author
Owner

@jcmoyer commented on GitHub (Jul 6, 2023):

@zadjii-msft Yes, this still occurs for me on 1.18.230526002-preview with the steam friends list repro.

@jcmoyer commented on GitHub (Jul 6, 2023): @zadjii-msft Yes, this still occurs for me on 1.18.230526002-preview with the steam friends list repro.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/terminal#19748