[fix] correct Windows remote control session routing and key mapping

This commit is contained in:
dijunkun
2026-09-19 03:59:35 +08:00
parent 000b2e44e3
commit 4797af9260
13 changed files with 334 additions and 41 deletions
@@ -8,6 +8,7 @@
#define _INTERACTIVE_DESKTOP_H_
#include <Windows.h>
#include <WtsApi32.h>
#include <string>
@@ -33,6 +34,52 @@ inline std::wstring GetDesktopNameW(HDESK desktop) {
return name;
}
inline bool IsDesktopReceivingInput(HDESK desktop) {
BOOL receiving_input = FALSE;
return desktop != nullptr &&
GetUserObjectInformationW(desktop, UOI_IO, &receiving_input,
sizeof(receiving_input), nullptr) &&
receiving_input != FALSE;
}
inline bool IsCurrentProcessSession(DWORD session_id) {
DWORD process_session_id = 0xFFFFFFFF;
return session_id != 0xFFFFFFFF &&
ProcessIdToSessionId(GetCurrentProcessId(), &process_session_id) &&
session_id == process_session_id;
}
inline bool IsCurrentSessionUnlocked() {
DWORD session_id = 0xFFFFFFFF;
if (!ProcessIdToSessionId(GetCurrentProcessId(), &session_id)) return false;
PWTSINFOEXW info = nullptr;
DWORD bytes = 0;
if (!WTSQuerySessionInformationW(
WTS_CURRENT_SERVER_HANDLE, session_id, WTSSessionInfoEx,
reinterpret_cast<LPWSTR*>(&info), &bytes)) {
return false;
}
const bool unlocked =
info != nullptr && bytes >= sizeof(WTSINFOEXW) && info->Level == 1 &&
info->Data.WTSInfoExLevel1.SessionState == WTSActive &&
info->Data.WTSInfoExLevel1.SessionFlags == WTS_SESSIONSTATE_UNLOCK;
if (info != nullptr) WTSFreeMemory(info);
return unlocked;
}
inline bool IsCurrentSessionUserDesktopActive() {
// LockApp can be visible on Default while the session is still locked.
if (!IsCurrentSessionUnlocked()) return false;
HDESK desktop = OpenInputDesktop(0, FALSE, DESKTOP_READOBJECTS);
if (desktop == nullptr) return false;
// OpenInputDesktop also succeeds in disconnected RDP sessions. The name
// alone does not tell us whether this desktop can currently receive input.
const bool active = IsDesktopReceivingInput(desktop) &&
_wcsicmp(GetDesktopNameW(desktop).c_str(), L"Default") == 0;
CloseDesktop(desktop);
return active;
}
// Use only on worker threads without windows or hooks. Restore the original
// desktop before closing the handle: Windows cannot close a desktop in use.
class ScopedInteractiveDesktop {
@@ -88,6 +135,10 @@ class ScopedInteractiveDesktop {
return !name_.empty() && _wcsicmp(name_.c_str(), input_name.c_str()) == 0;
}
bool IsReceivingInput() const {
return IsDesktopReceivingInput(desktop_);
}
const std::wstring& name() const { return name_; }
private: