From d6d30b68427f5b91c018c1801e4d208e6107719a Mon Sep 17 00:00:00 2001 From: dijunkun Date: Mon, 6 Jul 2026 17:47:50 +0800 Subject: [PATCH] [fix] fix Windows UAC prompt input by routing Consent UI through secure input helper --- .../windows/screen_capturer_win.cpp | 36 +++- .../windows/screen_capturer_win.h | 2 + src/service/windows/service_host.cpp | 157 +++++++++++++++--- src/service/windows/service_host.h | 8 +- src/service/windows/session_helper_main.cpp | 136 +++++++++++---- tests/windows_service_mouse_ipc_test.cpp | 34 +++- 6 files changed, 308 insertions(+), 65 deletions(-) diff --git a/src/screen_capturer/windows/screen_capturer_win.cpp b/src/screen_capturer/windows/screen_capturer_win.cpp index 8a50a56..8c98a3f 100644 --- a/src/screen_capturer/windows/screen_capturer_win.cpp +++ b/src/screen_capturer/windows/screen_capturer_win.cpp @@ -46,6 +46,7 @@ struct SecureDesktopServiceStatus { DWORD active_session_id = 0xFFFFFFFF; DWORD error_code = 0; std::string interactive_stage; + std::string interactive_desktop; std::string error; }; @@ -134,12 +135,16 @@ class WgcPluginCapturer final : public ScreenCapturer { std::string BuildSecureCaptureCommand(int left, int top, int width, int height, bool show_cursor, - const std::string& stage) { + const std::string& stage, + const std::string& desktop) { std::ostringstream stream; stream << kCrossDeskSecureInputCaptureCommandPrefix << left << ":" << top << ":" << width << ":" << height << ":" << (show_cursor ? 1 : 0); if (!stage.empty()) { stream << ":" << stage; + if (!desktop.empty()) { + stream << ":" << desktop; + } } return stream.str(); } @@ -147,13 +152,17 @@ std::string BuildSecureCaptureCommand(int left, int top, int width, int height, std::string BuildSecureCaptureStartCommand(int left, int top, int width, int height, bool show_cursor, int fps, - const std::string& stage) { + const std::string& stage, + const std::string& desktop) { std::ostringstream stream; stream << kCrossDeskSecureInputCaptureStartCommandPrefix << left << ":" << top << ":" << width << ":" << height << ":" << (show_cursor ? 1 : 0) << ":" << fps; if (!stage.empty()) { stream << ":" << stage; + if (!desktop.empty()) { + stream << ":" << desktop; + } } return stream.str(); } @@ -284,6 +293,7 @@ bool QuerySecureDesktopServiceStatus(SecureDesktopServiceStatus* status) { json.value("last_session_event", std::string()))) { status->active_session_id = json.value("active_session_id", 0xFFFFFFFFu); status->interactive_stage = "user-desktop"; + status->interactive_desktop.clear(); status->capture_active = false; return true; } @@ -291,6 +301,8 @@ bool QuerySecureDesktopServiceStatus(SecureDesktopServiceStatus* status) { status->active_session_id = json.value("active_session_id", 0xFFFFFFFFu); status->helper_running = json.value("secure_input_helper_running", false); status->interactive_stage = json.value("interactive_stage", std::string()); + status->interactive_desktop = + json.value("interactive_input_desktop", std::string()); const bool secure_desktop_active = json.value("interactive_secure_desktop_active", json.value("secure_desktop_active", false)); @@ -358,6 +370,7 @@ bool QuerySecureDesktopHelperCommand(DWORD session_id, bool QuerySecureDesktopHelperFrame(DWORD session_id, int left, int top, int width, int height, bool show_cursor, const std::string& stage, + const std::string& desktop, std::vector* nv12_frame_out, int* captured_width_out, int* captured_height_out, @@ -368,7 +381,8 @@ bool QuerySecureDesktopHelperFrame(DWORD session_id, int left, int top, } const std::string command = - BuildSecureCaptureCommand(left, top, width, height, show_cursor, stage); + BuildSecureCaptureCommand(left, top, width, height, show_cursor, stage, + desktop); std::vector response; if (!QuerySecureDesktopHelperCommand(session_id, command, &response, error_out)) { @@ -823,6 +837,7 @@ void ScreenCapturerWin::StopSecureDesktopSharedCapture(DWORD session_id) { secure_shared_fps_ = 0; secure_shared_show_cursor_ = true; secure_shared_stage_.clear(); + secure_shared_desktop_.clear(); } bool ScreenCapturerWin::OpenSecureDesktopSharedFrame(DWORD session_id, @@ -952,7 +967,8 @@ bool ScreenCapturerWin::ReadSecureDesktopSharedFrame( bool ScreenCapturerWin::StartSecureDesktopSharedCapture( DWORD session_id, int left, int top, int width, int height, - const std::string& stage, bool show_cursor, int fps, + const std::string& stage, const std::string& desktop, bool show_cursor, + int fps, std::string* error_out) { const size_t payload_size = static_cast(width) * height * 3 / 2; const size_t mapping_size = @@ -968,7 +984,7 @@ bool ScreenCapturerWin::StartSecureDesktopSharedCapture( secure_shared_session_id_ == session_id && secure_shared_left_ == left && secure_shared_top_ == top && secure_shared_width_ == width && secure_shared_height_ == height && - secure_shared_stage_ == stage && + secure_shared_stage_ == stage && secure_shared_desktop_ == desktop && secure_shared_show_cursor_ == show_cursor && secure_shared_fps_ == fps && OpenSecureDesktopSharedFrame(session_id, mapping_size, error_out)) { return true; @@ -978,7 +994,7 @@ bool ScreenCapturerWin::StartSecureDesktopSharedCapture( const std::string command = BuildSecureCaptureStartCommand(left, top, width, height, show_cursor, fps, - stage); + stage, desktop); std::vector response; if (!QuerySecureDesktopHelperCommand(session_id, command, &response, error_out)) { @@ -1002,6 +1018,7 @@ bool ScreenCapturerWin::StartSecureDesktopSharedCapture( secure_shared_show_cursor_ = show_cursor; secure_shared_fps_ = fps; secure_shared_stage_ = stage; + secure_shared_desktop_ = desktop; if (!OpenSecureDesktopSharedFrame(session_id, mapping_size, error_out)) { StopSecureDesktopSharedCapture(session_id); @@ -1161,8 +1178,10 @@ void ScreenCapturerWin::SecureDesktopCaptureLoop() { if (StartSecureDesktopSharedCapture(status.active_session_id, left, top, width, height, - status.interactive_stage, show_cursor, - shared_fps, &error_message) && + status.interactive_stage, + status.interactive_desktop, + show_cursor, shared_fps, + &error_message) && ReadSecureDesktopSharedFrame( static_cast(frame_interval_ms + 20), &secure_frame, &captured_width, &captured_height, &error_message)) { @@ -1177,6 +1196,7 @@ void ScreenCapturerWin::SecureDesktopCaptureLoop() { QuerySecureDesktopHelperFrame(status.active_session_id, left, top, width, height, show_cursor, status.interactive_stage, + status.interactive_desktop, &secure_frame, &captured_width, &captured_height, &error_message)) { if (cb_orig_ && !secure_frame.empty()) { diff --git a/src/screen_capturer/windows/screen_capturer_win.h b/src/screen_capturer/windows/screen_capturer_win.h index 1196825..a86ae09 100644 --- a/src/screen_capturer/windows/screen_capturer_win.h +++ b/src/screen_capturer/windows/screen_capturer_win.h @@ -75,6 +75,7 @@ class ScreenCapturerWin : public ScreenCapturer { int secure_shared_fps_ = 0; bool secure_shared_show_cursor_ = true; std::string secure_shared_stage_; + std::string secure_shared_desktop_; bool secure_shared_capture_started_ = false; void BuildCanonicalFromImpl(); @@ -87,6 +88,7 @@ class ScreenCapturerWin : public ScreenCapturer { bool StartSecureDesktopSharedCapture(DWORD session_id, int left, int top, int width, int height, const std::string& stage, + const std::string& desktop, bool show_cursor, int fps, std::string* error_out); void StopSecureDesktopSharedCapture(DWORD session_id); diff --git a/src/service/windows/service_host.cpp b/src/service/windows/service_host.cpp index a37ba49..340037d 100644 --- a/src/service/windows/service_host.cpp +++ b/src/service/windows/service_host.cpp @@ -64,6 +64,7 @@ struct ScopedEnvironmentBlock { LPVOID environment = nullptr; }; +std::wstring Utf8ToWide(const std::string& value); std::string WideToUtf8(const std::wstring& value); std::wstring GetCurrentExecutablePathW() { @@ -358,24 +359,32 @@ std::string BuildSecureDesktopMouseIpcCommand(int x, int y, int wheel, std::string BuildSecureInputHelperKeyboardCommand( int key_code, bool is_down, uint32_t scan_code, bool extended, - const std::string& interactive_stage) { + const std::string& interactive_stage, + const std::string& interactive_desktop) { std::ostringstream stream; stream << kCrossDeskSecureInputKeyboardCommandPrefix << key_code << ":" << (is_down ? 1 : 0) << ":" << scan_code << ":" << (extended ? 1 : 0); if (!interactive_stage.empty()) { stream << ":" << interactive_stage; + if (!interactive_desktop.empty()) { + stream << ":" << interactive_desktop; + } } return stream.str(); } std::string BuildSecureInputHelperMouseCommand( int x, int y, int wheel, int flag, - const std::string& interactive_stage) { + const std::string& interactive_stage, + const std::string& interactive_desktop) { std::ostringstream stream; stream << kCrossDeskSecureInputMouseCommandPrefix << x << ":" << y << ":" << wheel << ":" << flag; if (!interactive_stage.empty()) { stream << ":" << interactive_stage; + if (!interactive_desktop.empty()) { + stream << ":" << interactive_desktop; + } } return stream.str(); } @@ -592,15 +601,23 @@ const char* DetermineInteractiveStage(bool lock_app_visible, } bool IsCredentialUiVisible(bool prelogin, bool session_locked, - bool logon_ui_running, + bool logon_ui_running, bool consent_ui_visible, bool input_desktop_available, bool secure_desktop_active) { - return (prelogin || session_locked || secure_desktop_active) && - (logon_ui_running || !input_desktop_available); + return consent_ui_visible || + ((prelogin || session_locked || secure_desktop_active) && + (logon_ui_running || !input_desktop_available)); } std::wstring SecureInputHelperDesktopForStage( - const std::string& interactive_stage) { + const std::string& interactive_stage, + const std::string& interactive_desktop) { + if (!interactive_desktop.empty()) { + std::wstring interactive_desktop_w = Utf8ToWide(interactive_desktop); + if (!interactive_desktop_w.empty()) { + return L"winsta0\\" + interactive_desktop_w; + } + } if (interactive_stage == "credential-ui" || interactive_stage == "secure-desktop") { return L"winsta0\\Winlogon"; @@ -663,7 +680,12 @@ bool QuerySessionLockState(DWORD session_id, bool* session_locked_out) { return success; } -bool IsLogonUiRunningInSession(DWORD session_id) { +bool IsProcessRunningInSession(const wchar_t* executable_name, + DWORD session_id) { + if (executable_name == nullptr || executable_name[0] == L'\0') { + return false; + } + HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (snapshot == INVALID_HANDLE_VALUE) { return false; @@ -674,7 +696,7 @@ bool IsLogonUiRunningInSession(DWORD session_id) { bool found = false; if (Process32FirstW(snapshot, &entry)) { do { - if (_wcsicmp(entry.szExeFile, L"LogonUI.exe") != 0) { + if (_wcsicmp(entry.szExeFile, executable_name) != 0) { continue; } @@ -691,6 +713,14 @@ bool IsLogonUiRunningInSession(DWORD session_id) { return found; } +bool IsLogonUiRunningInSession(DWORD session_id) { + return IsProcessRunningInSession(L"LogonUI.exe", session_id); +} + +bool IsConsentUiRunningInSession(DWORD session_id) { + return IsProcessRunningInSession(L"Consent.exe", session_id); +} + InputDesktopInfo GetInputDesktopInfo() { InputDesktopInfo info; HDESK desktop = OpenInputDesktop(0, FALSE, GENERIC_READ); @@ -724,6 +754,24 @@ InputDesktopInfo GetInputDesktopInfo() { return info; } +std::wstring Utf8ToWide(const std::string& value) { + if (value.empty()) { + return {}; + } + + int size_needed = + MultiByteToWideChar(CP_UTF8, 0, value.c_str(), -1, nullptr, 0); + if (size_needed <= 1) { + return {}; + } + + std::wstring result(static_cast(size_needed), L'\0'); + MultiByteToWideChar(CP_UTF8, 0, value.c_str(), -1, result.data(), + size_needed); + result.pop_back(); + return result; +} + std::string WideToUtf8(const std::wstring& value) { if (value.empty()) { return {}; @@ -1024,6 +1072,7 @@ int CrossDeskServiceHost::InitializeRuntime() { secure_input_helper_last_error_code_ = 0; session_locked_ = false; logon_ui_visible_ = false; + consent_ui_visible_ = false; prelogin_ = false; secure_desktop_active_ = false; input_desktop_available_ = false; @@ -1032,6 +1081,7 @@ int CrossDeskServiceHost::InitializeRuntime() { session_helper_report_input_desktop_available_ = false; session_helper_report_lock_app_visible_ = false; session_helper_report_logon_ui_visible_ = false; + session_helper_report_consent_ui_visible_ = false; session_helper_report_secure_desktop_active_ = false; session_helper_report_credential_ui_visible_ = false; session_helper_report_unlock_ui_visible_ = false; @@ -1056,6 +1106,7 @@ int CrossDeskServiceHost::InitializeRuntime() { session_helper_report_interactive_stage_.clear(); secure_input_helper_last_error_.clear(); secure_input_helper_interactive_stage_.clear(); + secure_input_helper_interactive_desktop_.clear(); last_session_event_type_ = 0; last_session_event_session_id_ = active_session_id_; RefreshSessionState(); @@ -1283,6 +1334,7 @@ void CrossDeskServiceHost::RefreshSessionState() { process_session_id_ = process_session_id; } logon_ui_visible_ = IsLogonUiRunningInSession(active_session_id_); + consent_ui_visible_ = IsConsentUiRunningInSession(active_session_id_); InputDesktopInfo desktop_info = GetInputDesktopInfo(); input_desktop_available_ = desktop_info.available; input_desktop_error_code_ = desktop_info.error_code; @@ -1312,6 +1364,7 @@ void CrossDeskServiceHost::ResetSessionHelperReportedStateLocked( session_helper_report_input_desktop_.clear(); session_helper_report_lock_app_visible_ = false; session_helper_report_logon_ui_visible_ = false; + session_helper_report_consent_ui_visible_ = false; session_helper_report_secure_desktop_active_ = false; session_helper_report_credential_ui_visible_ = false; session_helper_report_unlock_ui_visible_ = false; @@ -1334,10 +1387,12 @@ bool CrossDeskServiceHost::HasSecureInputUiLocked() const { const bool service_host_credential_ui_visible = !session_helper_status_ok_ && IsCredentialUiVisible(prelogin_, session_locked_, logon_ui_visible_, + consent_ui_visible_, input_desktop_available_, secure_desktop_active_); return IsSasSecureDesktopGraceActiveLocked() || prelogin_ || secure_desktop_active_ || service_host_credential_ui_visible || + session_helper_report_consent_ui_visible_ || session_helper_report_credential_ui_visible_ || session_helper_report_secure_desktop_active_ || session_helper_report_unlock_ui_visible_ || @@ -1392,6 +1447,7 @@ std::string CrossDeskServiceHost::ResolveInteractiveStageLocked() const { const bool service_host_credential_ui_visible = IsCredentialUiVisible(prelogin_, session_locked_, logon_ui_visible_, + consent_ui_visible_, input_desktop_available_, secure_desktop_active_); return DetermineInteractiveStage( @@ -1401,6 +1457,33 @@ std::string CrossDeskServiceHost::ResolveInteractiveStageLocked() const { session_helper_report_secure_desktop_active_ || secure_desktop_active_); } +std::string CrossDeskServiceHost::ResolveInteractiveDesktopLocked( + const std::string& interactive_stage) const { + if (interactive_stage == "lock-screen") { + return "Default"; + } + + if (session_helper_status_ok_ && + session_helper_report_input_desktop_available_ && + !session_helper_report_input_desktop_.empty() && + (interactive_stage == "credential-ui" || + session_helper_report_consent_ui_visible_)) { + return session_helper_report_input_desktop_; + } + + if (input_desktop_available_ && !input_desktop_name_.empty() && + (interactive_stage == "credential-ui" || consent_ui_visible_)) { + return input_desktop_name_; + } + + if (interactive_stage == "credential-ui" || + interactive_stage == "secure-desktop") { + return "Winlogon"; + } + + return ""; +} + std::wstring CrossDeskServiceHost::GetSessionHelperPath() const { std::wstring current_executable = GetCurrentExecutablePathW(); if (current_executable.empty()) { @@ -1491,6 +1574,7 @@ void CrossDeskServiceHost::ReapSecureInputHelper() { secure_input_helper_exit_code_ = exit_code; secure_input_helper_started_at_tick_ = 0; secure_input_helper_interactive_stage_.clear(); + secure_input_helper_interactive_desktop_.clear(); } if (process_handle != nullptr) { @@ -1550,6 +1634,7 @@ void CrossDeskServiceHost::StopSecureInputHelper() { secure_input_helper_process_id_ = 0; secure_input_helper_started_at_tick_ = 0; secure_input_helper_interactive_stage_.clear(); + secure_input_helper_interactive_desktop_.clear(); } if (stop_event_handle != nullptr) { @@ -1678,7 +1763,8 @@ bool CrossDeskServiceHost::LaunchSessionHelper(DWORD session_id) { } bool CrossDeskServiceHost::LaunchSecureInputHelper( - DWORD session_id, const std::string& interactive_stage) { + DWORD session_id, const std::string& interactive_stage, + const std::string& interactive_desktop) { std::wstring helper_path = GetSecureInputHelperPath(); if (helper_path.empty() || !std::filesystem::exists(helper_path)) { std::lock_guard lock(state_mutex_); @@ -1713,7 +1799,7 @@ bool CrossDeskServiceHost::LaunchSecureInputHelper( STARTUPINFOW startup_info{}; startup_info.cb = sizeof(startup_info); std::wstring secure_input_helper_desktop = - SecureInputHelperDesktopForStage(interactive_stage); + SecureInputHelperDesktopForStage(interactive_stage, interactive_desktop); startup_info.lpDesktop = const_cast(secure_input_helper_desktop.c_str()); PROCESS_INFORMATION process_info{}; @@ -1765,13 +1851,14 @@ bool CrossDeskServiceHost::LaunchSecureInputHelper( secure_input_helper_running_ = true; secure_input_helper_started_at_tick_ = GetTickCount64(); secure_input_helper_interactive_stage_ = interactive_stage; + secure_input_helper_interactive_desktop_ = interactive_desktop; } LOG_INFO( "Secure input helper started: session_id={}, pid={}, stage='{}', " - "desktop='{}'", + "interactive_desktop='{}', desktop='{}'", session_id, process_info.dwProcessId, interactive_stage, - WideToUtf8(secure_input_helper_desktop)); + interactive_desktop, WideToUtf8(secure_input_helper_desktop)); return true; } @@ -1860,6 +1947,8 @@ void CrossDeskServiceHost::RefreshSessionHelperReportedState() { json.value("lock_app_visible", false); session_helper_report_logon_ui_visible_ = json.value("logon_ui_visible", false); + session_helper_report_consent_ui_visible_ = + json.value("consent_ui_visible", false); session_helper_report_secure_desktop_active_ = json.value("secure_desktop_active", false); session_helper_report_credential_ui_visible_ = @@ -1885,6 +1974,7 @@ void CrossDeskServiceHost::RecordSessionEvent(DWORD event_type, process_session_id_ = process_session_id; } logon_ui_visible_ = IsLogonUiRunningInSession(active_session_id_); + consent_ui_visible_ = IsConsentUiRunningInSession(active_session_id_); InputDesktopInfo desktop_info = GetInputDesktopInfo(); input_desktop_available_ = desktop_info.available; input_desktop_error_code_ = desktop_info.error_code; @@ -1911,7 +2001,7 @@ void CrossDeskServiceHost::RecordSessionEvent(DWORD event_type, LOG_INFO("Session event: type={}, session_id={}, active_session_id={}", SessionEventToString(event_type), session_id, active_session_id_); EnsureSessionHelper(); - if (!secure_desktop_active_ && !logon_ui_visible_) { + if (!secure_desktop_active_ && !logon_ui_visible_ && !consent_ui_visible_) { StopSecureInputHelper(); } } @@ -1955,10 +2045,13 @@ std::string CrossDeskServiceHost::BuildStatusResponse() { bool launch_secure_input_helper = false; DWORD secure_input_target_session_id = 0xFFFFFFFF; std::string secure_input_interactive_stage; + std::string secure_input_interactive_desktop; { std::lock_guard lock(state_mutex_); secure_input_target_session_id = active_session_id_; secure_input_interactive_stage = ResolveInteractiveStageLocked(); + secure_input_interactive_desktop = + ResolveInteractiveDesktopLocked(secure_input_interactive_stage); keep_secure_input_helper = ShouldKeepSecureInputHelperLocked(secure_input_target_session_id); launch_secure_input_helper = @@ -1966,14 +2059,17 @@ std::string CrossDeskServiceHost::BuildStatusResponse() { (!secure_input_helper_running_ || secure_input_helper_session_id_ != secure_input_target_session_id || secure_input_helper_interactive_stage_ != - secure_input_interactive_stage); + secure_input_interactive_stage || + secure_input_helper_interactive_desktop_ != + secure_input_interactive_desktop); } if (keep_secure_input_helper) { if (launch_secure_input_helper) { StopSecureInputHelper(); LaunchSecureInputHelper(secure_input_target_session_id, - secure_input_interactive_stage); + secure_input_interactive_stage, + secure_input_interactive_desktop); } } else { StopSecureInputHelper(); @@ -1999,6 +2095,8 @@ std::string CrossDeskServiceHost::BuildStatusResponse() { EscapeJsonString(secure_input_helper_last_error_); std::string secure_input_helper_interactive_stage = EscapeJsonString(secure_input_helper_interactive_stage_); + std::string secure_input_helper_interactive_desktop = + EscapeJsonString(secure_input_helper_interactive_desktop_); bool interactive_state_ready = session_helper_status_ok_; const bool sas_secure_desktop_grace_active = IsSasSecureDesktopGraceActiveLocked(); @@ -2011,6 +2109,7 @@ std::string CrossDeskServiceHost::BuildStatusResponse() { : false; const bool service_host_credential_ui_visible = IsCredentialUiVisible(prelogin_, session_locked_, logon_ui_visible_, + consent_ui_visible_, input_desktop_available_, secure_desktop_active_); bool credential_ui_visible = @@ -2062,6 +2161,8 @@ std::string CrossDeskServiceHost::BuildStatusResponse() { << ",\"unlock_ui_visible\":" << (unlock_ui_visible ? "true" : "false") << ",\"credential_ui_visible\":" << (credential_ui_visible ? "true" : "false") + << ",\"consent_ui_visible\":" + << (consent_ui_visible_ ? "true" : "false") << ",\"password_box_visible\":" << (credential_ui_visible ? "true" : "false") << ",\"logon_ui_visible\":" << (logon_ui_visible_ ? "true" : "false") @@ -2104,6 +2205,8 @@ std::string CrossDeskServiceHost::BuildStatusResponse() { << (session_helper_report_lock_app_visible_ ? "true" : "false") << ",\"session_helper_report_logon_ui_visible\":" << (session_helper_report_logon_ui_visible_ ? "true" : "false") + << ",\"session_helper_report_consent_ui_visible\":" + << (session_helper_report_consent_ui_visible_ ? "true" : "false") << ",\"session_helper_report_secure_desktop_active\":" << (session_helper_report_secure_desktop_active_ ? "true" : "false") << ",\"session_helper_report_credential_ui_visible\":" @@ -2134,6 +2237,8 @@ std::string CrossDeskServiceHost::BuildStatusResponse() { << secure_input_helper_last_error_code_ << ",\"secure_input_helper_stage\":\"" << secure_input_helper_interactive_stage << "\"" + << ",\"secure_input_helper_desktop\":\"" + << secure_input_helper_interactive_desktop << "\"" << ",\"secure_input_helper_uptime_ms\":" << (secure_input_helper_started_at_tick_ >= started_at_tick_ ? (GetTickCount64() - secure_input_helper_started_at_tick_) @@ -2190,12 +2295,15 @@ std::string CrossDeskServiceHost::SendSecureDesktopKeyboardInput( bool helper_running = false; bool can_inject = false; std::string interactive_stage; + std::string interactive_desktop; { std::lock_guard lock(state_mutex_); target_session_id = active_session_id_; interactive_stage = ResolveInteractiveStageLocked(); + interactive_desktop = ResolveInteractiveDesktopLocked(interactive_stage); const bool helper_stage_matches = - secure_input_helper_interactive_stage_ == interactive_stage; + secure_input_helper_interactive_stage_ == interactive_stage && + secure_input_helper_interactive_desktop_ == interactive_desktop; helper_running = secure_input_helper_running_ && secure_input_helper_session_id_ == target_session_id && helper_stage_matches; @@ -2211,7 +2319,8 @@ std::string CrossDeskServiceHost::SendSecureDesktopKeyboardInput( if (!helper_running) { StopSecureInputHelper(); - if (!LaunchSecureInputHelper(target_session_id, interactive_stage)) { + if (!LaunchSecureInputHelper(target_session_id, interactive_stage, + interactive_desktop)) { std::lock_guard lock(state_mutex_); return BuildErrorJson(secure_input_helper_last_error_.c_str(), secure_input_helper_last_error_code_); @@ -2221,7 +2330,8 @@ std::string CrossDeskServiceHost::SendSecureDesktopKeyboardInput( return QueryNamedPipeMessage( GetCrossDeskSecureInputHelperPipeName(target_session_id), BuildSecureInputHelperKeyboardCommand(key_code, is_down, scan_code, - extended, interactive_stage), + extended, interactive_stage, + interactive_desktop), 1000); } @@ -2237,12 +2347,15 @@ std::string CrossDeskServiceHost::SendSecureDesktopMouseInput(int x, int y, bool helper_running = false; bool can_inject = false; std::string interactive_stage; + std::string interactive_desktop; { std::lock_guard lock(state_mutex_); target_session_id = active_session_id_; interactive_stage = ResolveInteractiveStageLocked(); + interactive_desktop = ResolveInteractiveDesktopLocked(interactive_stage); const bool helper_stage_matches = - secure_input_helper_interactive_stage_ == interactive_stage; + secure_input_helper_interactive_stage_ == interactive_stage && + secure_input_helper_interactive_desktop_ == interactive_desktop; helper_running = secure_input_helper_running_ && secure_input_helper_session_id_ == target_session_id && helper_stage_matches; @@ -2258,7 +2371,8 @@ std::string CrossDeskServiceHost::SendSecureDesktopMouseInput(int x, int y, if (!helper_running) { StopSecureInputHelper(); - if (!LaunchSecureInputHelper(target_session_id, interactive_stage)) { + if (!LaunchSecureInputHelper(target_session_id, interactive_stage, + interactive_desktop)) { std::lock_guard lock(state_mutex_); return BuildErrorJson(secure_input_helper_last_error_.c_str(), secure_input_helper_last_error_code_); @@ -2267,7 +2381,8 @@ std::string CrossDeskServiceHost::SendSecureDesktopMouseInput(int x, int y, return QueryNamedPipeMessage( GetCrossDeskSecureInputHelperPipeName(target_session_id), - BuildSecureInputHelperMouseCommand(x, y, wheel, flag, interactive_stage), + BuildSecureInputHelperMouseCommand(x, y, wheel, flag, interactive_stage, + interactive_desktop), 1000); } diff --git a/src/service/windows/service_host.h b/src/service/windows/service_host.h index e044f34..87e8c56 100644 --- a/src/service/windows/service_host.h +++ b/src/service/windows/service_host.h @@ -46,7 +46,8 @@ class CrossDeskServiceHost { void ReapSecureInputHelper(); void StopSecureInputHelper(); bool LaunchSecureInputHelper(DWORD session_id, - const std::string& interactive_stage); + const std::string& interactive_stage, + const std::string& interactive_desktop); std::wstring GetSessionHelperPath() const; std::wstring GetSessionHelperStopEventName(DWORD session_id) const; std::wstring GetSecureInputHelperPath() const; @@ -60,6 +61,8 @@ class CrossDeskServiceHost { bool IsSasSecureDesktopGraceActiveLocked() const; bool ShouldKeepSecureInputHelperLocked(DWORD target_session_id) const; std::string ResolveInteractiveStageLocked() const; + std::string ResolveInteractiveDesktopLocked( + const std::string& interactive_stage) const; void RefreshSessionHelperReportedState(); void RecordSessionEvent(DWORD event_type, DWORD session_id); std::string HandleIpcCommand(const std::string& command); @@ -108,6 +111,7 @@ class CrossDeskServiceHost { ULONGLONG sas_secure_desktop_until_tick_ = 0; bool session_locked_ = false; bool logon_ui_visible_ = false; + bool consent_ui_visible_ = false; bool prelogin_ = false; bool secure_desktop_active_ = false; bool input_desktop_available_ = false; @@ -117,6 +121,7 @@ class CrossDeskServiceHost { bool session_helper_report_input_desktop_available_ = false; bool session_helper_report_lock_app_visible_ = false; bool session_helper_report_logon_ui_visible_ = false; + bool session_helper_report_consent_ui_visible_ = false; bool session_helper_report_secure_desktop_active_ = false; bool session_helper_report_credential_ui_visible_ = false; bool session_helper_report_unlock_ui_visible_ = false; @@ -137,6 +142,7 @@ class CrossDeskServiceHost { std::string session_helper_report_interactive_stage_; std::string secure_input_helper_last_error_; std::string secure_input_helper_interactive_stage_; + std::string secure_input_helper_interactive_desktop_; static CrossDeskServiceHost* instance_; }; diff --git a/src/service/windows/session_helper_main.cpp b/src/service/windows/session_helper_main.cpp index 369f8d1..cd6c288 100644 --- a/src/service/windows/session_helper_main.cpp +++ b/src/service/windows/session_helper_main.cpp @@ -51,6 +51,7 @@ struct HelperState { std::string input_desktop_name; bool lock_app_visible = false; bool logon_ui_visible = false; + bool consent_ui_visible = false; bool secure_desktop_active = false; ULONGLONG started_at_tick = 0; ULONGLONG last_update_tick = 0; @@ -64,6 +65,7 @@ struct SecureCaptureRequest { bool show_cursor = true; int fps = 30; std::string interactive_stage; + std::string interactive_desktop; }; struct SecureMouseRequest { @@ -72,6 +74,7 @@ struct SecureMouseRequest { int wheel = 0; int flag = 0; std::string interactive_stage; + std::string interactive_desktop; }; struct SecureCaptureBuffers { @@ -288,6 +291,10 @@ bool IsLogonUiRunningInCurrentSession(DWORD session_id) { return IsProcessRunningInCurrentSession(L"LogonUI.exe", session_id); } +bool IsConsentUiRunningInCurrentSession(DWORD session_id) { + return IsProcessRunningInCurrentSession(L"Consent.exe", session_id); +} + bool IsLockAppRunningInCurrentSession(DWORD session_id) { return IsProcessRunningInCurrentSession(L"LockApp.exe", session_id); } @@ -341,10 +348,12 @@ const char* DetermineInteractiveStage(bool lock_app_visible, } bool IsCredentialUiVisible(bool session_locked, bool logon_ui_running, + bool consent_ui_visible, bool input_desktop_available, bool secure_desktop_active) { - return (session_locked || secure_desktop_active) && - (logon_ui_running || !input_desktop_available); + return consent_ui_visible || + ((session_locked || secure_desktop_active) && + (logon_ui_running || !input_desktop_available)); } std::string BuildErrorJson(const char* error, DWORD error_code = 0) { @@ -367,6 +376,10 @@ void UpdateHelperState(HelperState* helper_state) { IsLockAppRunningInCurrentSession(helper_state->session_id); bool logon_ui_visible = IsLogonUiRunningInCurrentSession(helper_state->session_id); + bool consent_ui_visible = + IsConsentUiRunningInCurrentSession(helper_state->session_id); + const bool consent_on_input_desktop = + desktop_info.available && consent_ui_visible; const bool input_desktop_is_winlogon = _stricmp(desktop_info.name.c_str(), "Winlogon") == 0; const bool inaccessible_secure_input_desktop = @@ -388,6 +401,8 @@ void UpdateHelperState(HelperState* helper_state) { helper_state->input_desktop_name = desktop_info.name; helper_state->lock_app_visible = lock_app_visible; helper_state->logon_ui_visible = logon_ui_visible; + helper_state->consent_ui_visible = + consent_on_input_desktop || consent_ui_visible; helper_state->secure_desktop_active = secure_desktop_active; helper_state->last_update_tick = GetTickCount64(); } @@ -401,6 +416,7 @@ std::string BuildHelperStatusResponse(HelperState* helper_state) { std::lock_guard lock(helper_state->mutex); const bool credential_ui_visible = IsCredentialUiVisible( helper_state->session_locked, helper_state->logon_ui_visible, + helper_state->consent_ui_visible, helper_state->input_desktop_available, helper_state->secure_desktop_active); const bool unlock_ui_visible = @@ -414,6 +430,7 @@ std::string BuildHelperStatusResponse(HelperState* helper_state) { json["input_desktop"] = helper_state->input_desktop_name; json["lock_app_visible"] = helper_state->lock_app_visible; json["logon_ui_visible"] = helper_state->logon_ui_visible; + json["consent_ui_visible"] = helper_state->consent_ui_visible; json["secure_desktop_active"] = helper_state->secure_desktop_active; json["credential_ui_visible"] = credential_ui_visible; json["unlock_ui_visible"] = unlock_ui_visible; @@ -639,8 +656,12 @@ bool EnsureThreadInteractiveDesktop(HDESK* opened_desktop_out = nullptr) { return false; } -const wchar_t* DesktopNameForInteractiveStage( - const std::string& interactive_stage) { +std::wstring DesktopNameForInteractiveStage( + const std::string& interactive_stage, + const std::string& interactive_desktop) { + if (!interactive_desktop.empty()) { + return Utf8ToWide(interactive_desktop); + } if (interactive_stage == "credential-ui" || interactive_stage == "secure-desktop") { return L"Winlogon"; @@ -648,7 +669,7 @@ const wchar_t* DesktopNameForInteractiveStage( if (interactive_stage == "lock-screen") { return L"Default"; } - return nullptr; + return {}; } struct DesktopSwitchDetails { @@ -666,14 +687,14 @@ struct InputInjectionResult { }; DesktopSwitchDetails BuildDesktopSwitchDetails( - const std::string& interactive_stage) { + const std::string& interactive_stage, + const std::string& interactive_desktop) { DesktopSwitchDetails details; details.stage = interactive_stage; - const wchar_t* desktop_name = - DesktopNameForInteractiveStage(interactive_stage); + const std::wstring desktop_name = + DesktopNameForInteractiveStage(interactive_stage, interactive_desktop); details.target_desktop = - desktop_name != nullptr ? WideToUtf8(std::wstring(desktop_name)) - : "input-or-Winlogon"; + !desktop_name.empty() ? WideToUtf8(desktop_name) : "input-or-Winlogon"; details.current_desktop = WideToUtf8(GetCurrentThreadDesktopNameW()); return details; } @@ -709,6 +730,7 @@ Json BuildInputFailureJson(const InputInjectionResult& result) { bool EnsureThreadInteractiveDesktopForStage( const std::string& interactive_stage, + const std::string& interactive_desktop, HDESK* opened_desktop_out = nullptr, DesktopSwitchDetails* switch_details = nullptr) { if (opened_desktop_out != nullptr) { @@ -716,15 +738,15 @@ bool EnsureThreadInteractiveDesktopForStage( } DesktopSwitchDetails local_details = - BuildDesktopSwitchDetails(interactive_stage); + BuildDesktopSwitchDetails(interactive_stage, interactive_desktop); if (switch_details != nullptr) { *switch_details = local_details; } - const wchar_t* desktop_name = - DesktopNameForInteractiveStage(interactive_stage); - if (desktop_name != nullptr) { - if (EnsureThreadDesktop(desktop_name, opened_desktop_out)) { + const std::wstring desktop_name = + DesktopNameForInteractiveStage(interactive_stage, interactive_desktop); + if (!desktop_name.empty()) { + if (EnsureThreadDesktop(desktop_name.c_str(), opened_desktop_out)) { if (switch_details != nullptr) { switch_details->current_desktop = WideToUtf8(GetCurrentThreadDesktopNameW()); @@ -741,7 +763,7 @@ bool EnsureThreadInteractiveDesktopForStage( LOG_WARN( "Failed to switch secure input helper to stage desktop, stage='{}', " "desktop='{}', error={}, current='{}'", - interactive_stage, WideToUtf8(std::wstring(desktop_name)), + interactive_stage, WideToUtf8(desktop_name), error, WideToUtf8(GetCurrentThreadDesktopNameW())); SetLastError(error); @@ -790,10 +812,12 @@ bool PreferSideSpecificVkInjection(int key_code) { InputInjectionResult InjectKeyboardInput( int key_code, bool is_down, uint32_t scan_code, bool extended, - const std::string& interactive_stage) { + const std::string& interactive_stage, + const std::string& interactive_desktop) { ScopedDesktopHandle desktop; DesktopSwitchDetails desktop_switch; if (!EnsureThreadInteractiveDesktopForStage(interactive_stage, + interactive_desktop, &desktop.handle, &desktop_switch)) { const DWORD error = GetLastError(); @@ -851,11 +875,37 @@ InputInjectionResult InjectKeyboardInput( return BuildInputSuccess(); } +void ParseInteractionTail(const std::string& tail, + std::string* interactive_stage_out, + std::string* interactive_desktop_out) { + if (interactive_stage_out != nullptr) { + interactive_stage_out->clear(); + } + if (interactive_desktop_out != nullptr) { + interactive_desktop_out->clear(); + } + if (tail.empty()) { + return; + } + + const size_t separator = tail.find(':'); + if (interactive_stage_out != nullptr) { + *interactive_stage_out = separator == std::string::npos + ? tail + : tail.substr(0, separator); + } + if (separator != std::string::npos && interactive_desktop_out != nullptr) { + *interactive_desktop_out = tail.substr(separator + 1); + } +} + bool ParseSecureInputKeyboardCommand(const std::string& command, int* key_code_out, bool* is_down_out, uint32_t* scan_code_out, bool* extended_out, std::string* interactive_stage_out = + nullptr, + std::string* interactive_desktop_out = nullptr) { if (key_code_out == nullptr || is_down_out == nullptr || scan_code_out == nullptr || extended_out == nullptr) { @@ -867,6 +917,9 @@ bool ParseSecureInputKeyboardCommand(const std::string& command, if (interactive_stage_out != nullptr) { interactive_stage_out->clear(); } + if (interactive_desktop_out != nullptr) { + interactive_desktop_out->clear(); + } if (command.rfind(crossdesk::kCrossDeskSecureInputKeyboardCommandPrefix, 0) != 0) { @@ -926,8 +979,9 @@ bool ParseSecureInputKeyboardCommand(const std::string& command, : command.substr(extended_separator + 1, stage_separator - extended_separator - 1); if (stage_separator != std::string::npos && - interactive_stage_out != nullptr) { - *interactive_stage_out = command.substr(stage_separator + 1); + (interactive_stage_out != nullptr || interactive_desktop_out != nullptr)) { + ParseInteractionTail(command.substr(stage_separator + 1), + interactive_stage_out, interactive_desktop_out); } if (extended_str == "1" || extended_str == "true") { *extended_out = true; @@ -951,6 +1005,7 @@ bool ParseSecureInputMouseCommand(const std::string& command, return false; } request_out->interactive_stage.clear(); + request_out->interactive_desktop.clear(); const size_t x_begin = std::strlen(crossdesk::kCrossDeskSecureInputMouseCommandPrefix); @@ -993,7 +1048,9 @@ bool ParseSecureInputMouseCommand(const std::string& command, ? command.substr(flag_begin) : command.substr(flag_begin, stage_separator - flag_begin)); if (stage_separator != std::string::npos) { - request_out->interactive_stage = command.substr(stage_separator + 1); + ParseInteractionTail(command.substr(stage_separator + 1), + &request_out->interactive_stage, + &request_out->interactive_desktop); } } catch (...) { return false; @@ -1013,6 +1070,7 @@ bool ParseSecureInputCaptureCommand(const std::string& command, return false; } request_out->interactive_stage.clear(); + request_out->interactive_desktop.clear(); const size_t values_begin = std::strlen(crossdesk::kCrossDeskSecureInputCaptureCommandPrefix); @@ -1042,9 +1100,11 @@ bool ParseSecureInputCaptureCommand(const std::string& command, request_out->width = parsed_values[2] & ~1; request_out->height = parsed_values[3] & ~1; request_out->show_cursor = parsed_values[4] != 0; - request_out->interactive_stage = - separator == std::string::npos ? std::string() - : command.substr(token_begin); + if (separator != std::string::npos) { + ParseInteractionTail(command.substr(token_begin), + &request_out->interactive_stage, + &request_out->interactive_desktop); + } return request_out->width > 0 && request_out->height > 0; } @@ -1059,6 +1119,7 @@ bool ParseSecureInputCaptureStartCommand(const std::string& command, return false; } request_out->interactive_stage.clear(); + request_out->interactive_desktop.clear(); const size_t values_begin = std::strlen( crossdesk::kCrossDeskSecureInputCaptureStartCommandPrefix); @@ -1089,9 +1150,11 @@ bool ParseSecureInputCaptureStartCommand(const std::string& command, request_out->height = parsed_values[3] & ~1; request_out->show_cursor = parsed_values[4] != 0; request_out->fps = parsed_values[5] > 0 ? parsed_values[5] : 30; - request_out->interactive_stage = - separator == std::string::npos ? std::string() - : command.substr(token_begin); + if (separator != std::string::npos) { + ParseInteractionTail(command.substr(token_begin), + &request_out->interactive_stage, + &request_out->interactive_desktop); + } return request_out->width > 0 && request_out->height > 0; } @@ -1129,6 +1192,7 @@ InputInjectionResult InjectMouseInput(const SecureMouseRequest& request) { ScopedDesktopHandle desktop; DesktopSwitchDetails desktop_switch; if (!EnsureThreadInteractiveDesktopForStage(request.interactive_stage, + request.interactive_desktop, &desktop.handle, &desktop_switch)) { const DWORD error = GetLastError(); @@ -1205,6 +1269,7 @@ std::vector CaptureSecureDesktopFrame( ScopedDesktopHandle desktop; if (!EnsureThreadInteractiveDesktopForStage(request.interactive_stage, + request.interactive_desktop, &desktop.handle)) { const DWORD error = GetLastError(); return BuildTextResponseBytes(BuildErrorJson( @@ -1355,6 +1420,7 @@ void SecureDesktopSharedCaptureThread( ScopedDesktopHandle desktop; if (!EnsureThreadInteractiveDesktopForStage(request.interactive_stage, + request.interactive_desktop, &desktop.handle)) { LOG_ERROR("Secure shared capture desktop switch failed, error={}", GetLastError()); @@ -1607,11 +1673,13 @@ std::vector HandleSecureInputHelperCommand( uint32_t scan_code = 0; bool extended = false; std::string interactive_stage; + std::string interactive_desktop; if (ParseSecureInputKeyboardCommand(command, &key_code, &is_down, &scan_code, - &extended, &interactive_stage)) { + &extended, &interactive_stage, + &interactive_desktop)) { const InputInjectionResult inject_result = InjectKeyboardInput(key_code, is_down, scan_code, extended, - interactive_stage); + interactive_stage, interactive_desktop); if (!inject_result.ok) { LOG_WARN( "Secure input helper input failed for key_code={}, is_down={}, " @@ -1631,6 +1699,7 @@ std::vector HandleSecureInputHelperCommand( json["scan_code"] = scan_code; json["extended"] = extended; json["stage"] = interactive_stage; + json["interactive_desktop"] = interactive_desktop; json["desktop"] = WideToUtf8(GetCurrentThreadDesktopNameW()); return BuildTextResponseBytes(json.dump()); } @@ -1658,6 +1727,7 @@ std::vector HandleSecureInputHelperCommand( json["wheel"] = mouse_request.wheel; json["flag"] = mouse_request.flag; json["stage"] = mouse_request.interactive_stage; + json["interactive_desktop"] = mouse_request.interactive_desktop; json["desktop"] = WideToUtf8(GetCurrentThreadDesktopNameW()); return BuildTextResponseBytes(json.dump()); } @@ -1887,6 +1957,7 @@ int main(int argc, char* argv[]) { std::string last_desktop_name; bool last_lock_app = false; bool last_logon_ui = false; + bool last_consent_ui = false; bool last_secure_desktop = false; bool last_session_locked = false; std::string last_stage; @@ -1898,6 +1969,7 @@ int main(int argc, char* argv[]) { bool input_desktop_available = false; bool lock_app_visible = false; bool logon_ui_running = false; + bool consent_ui_visible = false; bool secure_desktop_active = false; { std::lock_guard lock(helper_state.mutex); @@ -1906,10 +1978,12 @@ int main(int argc, char* argv[]) { input_desktop_available = helper_state.input_desktop_available; lock_app_visible = helper_state.lock_app_visible; logon_ui_running = helper_state.logon_ui_visible; + consent_ui_visible = helper_state.consent_ui_visible; secure_desktop_active = helper_state.secure_desktop_active; } const bool credential_ui_visible = IsCredentialUiVisible(session_locked, logon_ui_running, + consent_ui_visible, input_desktop_available, secure_desktop_active); std::string stage = DetermineInteractiveStage( @@ -1919,17 +1993,19 @@ int main(int argc, char* argv[]) { session_locked != last_session_locked || lock_app_visible != last_lock_app || logon_ui_running != last_logon_ui || + consent_ui_visible != last_consent_ui || secure_desktop_active != last_secure_desktop || stage != last_stage) { LOG_INFO( "Session helper state: session_id={}, input_desktop='{}', " "session_locked={}, lock_app_visible={}, logon_ui_running={}, " - "secure_desktop_active={}, stage={}", + "consent_ui_visible={}, secure_desktop_active={}, stage={}", current_session_id, desktop_name, session_locked, lock_app_visible, - logon_ui_running, secure_desktop_active, stage); + logon_ui_running, consent_ui_visible, secure_desktop_active, stage); last_desktop_name = desktop_name; last_session_locked = session_locked; last_lock_app = lock_app_visible; last_logon_ui = logon_ui_running; + last_consent_ui = consent_ui_visible; last_secure_desktop = secure_desktop_active; last_stage = stage; } diff --git a/tests/windows_service_mouse_ipc_test.cpp b/tests/windows_service_mouse_ipc_test.cpp index 163dc40..b846986 100644 --- a/tests/windows_service_mouse_ipc_test.cpp +++ b/tests/windows_service_mouse_ipc_test.cpp @@ -84,7 +84,7 @@ int main() { ok &= ExpectContains("targets.lua", targets, "target(\"crossdesk_session_helper\")"); ok &= ExpectContains("targets.lua", targets, - "add_files(\"scripts/windows/crossdesk.rc\")"); + "add_files(crossdesk_windows_resource)"); ok &= ExpectContains("session_helper_main.cpp", session_helper, "EnablePerMonitorDpiAwareness"); ok &= ExpectContains("session_helper_main.cpp", session_helper, @@ -105,11 +105,33 @@ int main() { "const std::string& interactive_stage"); ok &= ExpectContains("service_host.h", service_host_h, "bool LaunchSecureInputHelper(DWORD session_id,\n" - " const std::string& interactive_stage)"); + " const std::string& interactive_stage,\n" + " const std::string& interactive_desktop)"); ok &= ExpectContains("service_host.h", service_host_h, "std::string secure_input_helper_interactive_stage_"); ok &= ExpectContains("service_host.cpp", service_host, "SecureInputHelperDesktopForStage"); + ok &= ExpectContains("service_host.cpp", service_host, + "IsConsentUiRunningInSession"); + ok &= ExpectContains("service_host.cpp", service_host, + "L\"Consent.exe\""); + ok &= ExpectContains("session_helper_main.cpp", session_helper, + "IsConsentUiRunningInCurrentSession"); + ok &= ExpectContains("session_helper_main.cpp", session_helper, + "L\"Consent.exe\""); + ok &= ExpectContains("session_helper_main.cpp", session_helper, + "desktop_info.available && consent_ui_visible"); + ok &= ExpectContains("service_host.cpp", service_host, + "session_helper_report_input_desktop_available_ &&"); + ok &= ExpectContains("service_host.cpp", service_host, + "session_helper_report_consent_ui_visible_"); + ok &= ExpectContains("service_host.cpp", service_host, + "secure_input_helper_interactive_desktop_"); + ok &= ExpectContains("service_host.cpp", service_host, + "SecureInputHelperDesktopForStage(" + "interactive_stage, interactive_desktop)"); + ok &= ExpectContains("service_host.cpp", service_host, + "return L\"winsta0\\\\\" + interactive_desktop_w"); ok &= ExpectContains("service_host.cpp", service_host, "return L\"winsta0\\\\Winlogon\""); ok &= ExpectContains("service_host.cpp", service_host, @@ -121,7 +143,8 @@ int main() { ok &= ExpectContains("service_host.cpp", service_host, "secure_input_helper_interactive_stage_.clear()"); ok &= ExpectContains("service_host.cpp", service_host, - "LaunchSecureInputHelper(target_session_id, interactive_stage)"); + "LaunchSecureInputHelper(target_session_id, interactive_stage,\n" + " interactive_desktop)"); ok &= ExpectContains("service_host.cpp", service_host, "\\\"secure_input_helper_stage\\\":\\\""); ok &= ExpectContains("service_host.cpp", service_host, @@ -189,10 +212,11 @@ int main() { "json[\"stage\"]"); ok &= ExpectContains("session_helper_main.cpp", session_helper, "ParseSecureInputKeyboardCommand(command, &key_code, &is_down, &scan_code,\n" - " &extended, &interactive_stage)"); + " &extended, &interactive_stage,\n" + " &interactive_desktop)"); ok &= ExpectContains("session_helper_main.cpp", session_helper, "InjectKeyboardInput(key_code, is_down, scan_code, extended,\n" - " interactive_stage)"); + " interactive_stage, interactive_desktop)"); ok &= ExpectContains("session_helper_main.cpp", session_helper, "InjectMouseInput(mouse_request)"); ok &= ExpectNotContains("session_helper_main.cpp", session_helper,