TightVNC Server 2.8.88, Windows 11 24H2.
InputInjector::resetModifiers (win-system/InputInjector.cpp, lines 459-476) injects KEYEVENTF_KEYUP for Alt, LAlt, RAlt, Shift, LShift, RShift, Ctrl, LCtrl, RCtrl, LWin, RWin and Delete, one SendInput each, with no check of what was pressed. It runs from the InputInjector constructor and destructor, so every viewer connect and disconnect sends the burst to whatever window is foreground on the server. #1469 reports the connect side (Delete release erasing data in another app). This report is the disconnect side.
Effect on Windows Installer: msihnd.dll (the full-UI wizard) sends itself WM_SYSCOMMAND/SC_KEYMENU on every WM_SYSKEYUP for VK_MENU, with no check that Alt was ever pressed. Windows delivers the injected unpaired Alt release as WM_SYSKEYUP once any application in the session has received one ordinary Alt tap since boot. The wizard's dialog thread then sits in menu mode, the installer's next progress message blocks behind it, and the install freezes until someone presses a key or clicks. We captured this in the field: upgrading a VPN client over a TightVNC session carried by that VPN, the upgrade stops the VPN, the viewer connection dies, the helper's destructor releases the modifiers into the installer, and the installer sits at "Stopping Services" for minutes until the operator reconnects and presses Esc. Memory dumps show the dialog thread in the kernel menu loop below CMsiControl::SysKeyUp with WM_SYSKEYUP VK_MENU lParam 0x80380001 (bit 30 clear: no prior key-down).
Self-contained reproduction, no VPN needed: https://github.com/johnmaguire/msi-menu-repro. It replays the exact twelve releases into a minimal WiX package and reports a verdict; docs/priming.md has the measurements. Tap Alt once in Notepad first, then:
.\repro.ps1 -Input TightVncReset -UI Full
Requested change: make resetModifiers release only the keys the injector itself pressed. InputInjector already tracks them (m_controlIsPressed, m_menuIsPressed, m_deleteIsPressed, m_shiftIsPressed, m_winIsPressed, maintained in injectKeyEvent at lines 91-103), but resetModifiers does not consult them. Releasing only flagged keys keeps the stuck-key protection for a connection that actually held a modifier when it dropped, sends nothing on a clean connect or disconnect, and fixes #1469 as well. If an unconditional reset must stay for some viewers, a server option to disable it would also solve our case.
Happy to test a build.