From 06c53fdc9cb04392f41a041084ea0fb61263268f Mon Sep 17 00:00:00 2001 From: dijunkun Date: Tue, 26 May 2026 04:38:07 +0800 Subject: [PATCH] [fix] handle SAS secure desktop transitions and restore desktop capture promptly, refs #77 --- src/gui/render.cpp | 31 +++- src/gui/render.h | 1 + src/service/windows/service_host.cpp | 57 +++++++- src/service/windows/service_host.h | 4 + src/service/windows/session_helper_main.cpp | 102 +++++++++++++- tests/windows_sas_guard_test.cpp | 149 ++++++++++++++++++++ xmake/targets.lua | 6 + 7 files changed, 336 insertions(+), 14 deletions(-) create mode 100644 tests/windows_sas_guard_test.cpp diff --git a/src/gui/render.cpp b/src/gui/render.cpp index b4467c8..ff50e4e 100644 --- a/src/gui/render.cpp +++ b/src/gui/render.cpp @@ -82,12 +82,14 @@ HICON LoadTrayIcon() { struct WindowsServiceInteractiveStatus { bool available = false; + bool sas_secure_desktop_grace_active = false; unsigned int error_code = 0; std::string interactive_stage; std::string error; }; constexpr uint32_t kWindowsServiceStatusIntervalMs = 1000; +constexpr uint32_t kWindowsServiceSasSecureDesktopGraceMs = 2000; constexpr DWORD kWindowsServiceQueryTimeoutMs = 500; constexpr DWORD kWindowsServiceSasTimeoutMs = 500; @@ -130,6 +132,8 @@ bool QueryWindowsServiceInteractiveStatus( } status->interactive_stage = json.value("interactive_stage", std::string()); + status->sas_secure_desktop_grace_active = + json.value("sas_secure_desktop_grace_active", false); if (ShouldNormalizeUnlockToUserDesktop( json.value("interactive_lock_screen_visible", false), @@ -1928,6 +1932,12 @@ void Render::HandleWindowsServiceIntegration() { LOG_WARN("Remote SAS request failed: {}", response); } else { LOG_INFO("Remote SAS request forwarded to local Windows service"); + optimistic_windows_secure_desktop_until_tick_ = + static_cast(SDL_GetTicks()) + + kWindowsServiceSasSecureDesktopGraceMs; + local_service_status_received_ = true; + local_service_available_ = true; + local_interactive_stage_ = "secure-desktop"; } last_windows_service_status_tick_ = 0; force_broadcast = true; @@ -1943,15 +1953,31 @@ void Render::HandleWindowsServiceIntegration() { WindowsServiceInteractiveStatus status; const bool status_ok = QueryWindowsServiceInteractiveStatus(&status); + WindowsServiceInteractiveStatus broadcast_status = status; const bool previous_secure_desktop_interaction = IsSecureDesktopInteractionRequired(local_interactive_stage_); + const bool optimistic_secure_desktop_active = + optimistic_windows_secure_desktop_until_tick_ != 0 && + static_cast(optimistic_windows_secure_desktop_until_tick_ - + now) > 0; + const bool keep_optimistic_secure_desktop = + status_ok && status.available && optimistic_secure_desktop_active && + status.sas_secure_desktop_grace_active && + status.interactive_stage == "user-desktop"; local_service_status_received_ = status_ok || previous_secure_desktop_interaction; local_service_available_ = status.available; if (status.available) { - local_interactive_stage_ = status.interactive_stage; + if (keep_optimistic_secure_desktop) { + local_interactive_stage_ = "secure-desktop"; + broadcast_status.interactive_stage = local_interactive_stage_; + } else { + local_interactive_stage_ = status.interactive_stage; + optimistic_windows_secure_desktop_until_tick_ = 0; + } } else if (!previous_secure_desktop_interaction) { local_interactive_stage_.clear(); + optimistic_windows_secure_desktop_until_tick_ = 0; } if (status_ok) { @@ -1990,7 +2016,7 @@ void Render::HandleWindowsServiceIntegration() { last_logged_service_error_code = 0; } - RemoteAction remote_action = BuildWindowsServiceStatusAction(status); + RemoteAction remote_action = BuildWindowsServiceStatusAction(broadcast_status); std::string msg = remote_action.to_json(); int ret = SendReliableDataFrame(peer_, msg.data(), msg.size(), control_data_label_.c_str()); @@ -2009,6 +2035,7 @@ void Render::ResetLocalWindowsServiceState(bool clear_pending_sas) { local_service_status_received_ = false; local_service_available_ = false; local_interactive_stage_.clear(); + optimistic_windows_secure_desktop_until_tick_ = 0; } #endif diff --git a/src/gui/render.h b/src/gui/render.h index 1ab3de0..8751af2 100644 --- a/src/gui/render.h +++ b/src/gui/render.h @@ -547,6 +547,7 @@ class Render { std::string local_interactive_stage_; uint32_t last_local_secure_input_block_log_tick_ = 0; uint32_t last_windows_service_status_tick_ = 0; + uint32_t optimistic_windows_secure_desktop_until_tick_ = 0; #endif // stream window render diff --git a/src/service/windows/service_host.cpp b/src/service/windows/service_host.cpp index 6a0bf76..987607d 100644 --- a/src/service/windows/service_host.cpp +++ b/src/service/windows/service_host.cpp @@ -31,6 +31,7 @@ constexpr char kSecureDesktopMouseIpcCommandPrefix[] = "secure-input-mouse:"; constexpr wchar_t kCrossDeskClientProcessName[] = L"crossdesk.exe"; constexpr DWORD kCrossDeskClientMonitorIntervalMs = 1000; constexpr ULONGLONG kCrossDeskClientMonitorStartupGraceMs = 5000; +constexpr ULONGLONG kSasSecureDesktopGraceMs = 15000; using SendSasFunction = VOID(WINAPI*)(BOOL); @@ -1027,12 +1028,14 @@ int CrossDeskServiceHost::InitializeRuntime() { session_helper_report_credential_ui_visible_ = false; session_helper_report_unlock_ui_visible_ = false; secure_input_helper_running_ = false; + sas_secure_desktop_seen_ = false; last_sas_error_code_ = 0; last_sas_success_ = false; session_helper_started_at_tick_ = 0; session_helper_report_state_age_ms_ = 0; session_helper_report_uptime_ms_ = 0; secure_input_helper_started_at_tick_ = 0; + sas_secure_desktop_until_tick_ = 0; session_helper_process_handle_ = nullptr; session_helper_stop_event_ = nullptr; secure_input_helper_process_handle_ = nullptr; @@ -1320,7 +1323,8 @@ bool CrossDeskServiceHost::IsHelperReportingLockScreenLocked() const { } bool CrossDeskServiceHost::HasSecureInputUiLocked() const { - return prelogin_ || secure_desktop_active_ || logon_ui_visible_ || + return IsSasSecureDesktopGraceActiveLocked() || prelogin_ || + secure_desktop_active_ || logon_ui_visible_ || session_helper_report_credential_ui_visible_ || session_helper_report_secure_desktop_active_ || session_helper_report_unlock_ui_visible_ || @@ -1328,6 +1332,30 @@ bool CrossDeskServiceHost::HasSecureInputUiLocked() const { session_helper_report_interactive_stage_ == "secure-desktop"; } +void CrossDeskServiceHost::UpdateSasSecureDesktopGraceLocked( + const std::string& observed_stage) { + if (sas_secure_desktop_until_tick_ == 0) { + sas_secure_desktop_seen_ = false; + return; + } + + if (observed_stage == "credential-ui" || observed_stage == "secure-desktop" || + observed_stage == "lock-screen") { + sas_secure_desktop_seen_ = true; + return; + } + + if (sas_secure_desktop_seen_ && observed_stage == "user-desktop") { + sas_secure_desktop_until_tick_ = 0; + sas_secure_desktop_seen_ = false; + } +} + +bool CrossDeskServiceHost::IsSasSecureDesktopGraceActiveLocked() const { + return last_sas_success_ && sas_secure_desktop_until_tick_ != 0 && + GetTickCount64() < sas_secure_desktop_until_tick_; +} + bool CrossDeskServiceHost::ShouldKeepSecureInputHelperLocked( DWORD target_session_id) const { if (target_session_id == 0xFFFFFFFF) { @@ -1339,6 +1367,12 @@ bool CrossDeskServiceHost::ShouldKeepSecureInputHelperLocked( } std::string CrossDeskServiceHost::ResolveInteractiveStageLocked() const { + if (IsSasSecureDesktopGraceActiveLocked() && + (session_helper_report_interactive_stage_.empty() || + session_helper_report_interactive_stage_ == "user-desktop")) { + return "secure-desktop"; + } + if (!session_helper_report_interactive_stage_.empty()) { return session_helper_report_interactive_stage_; } @@ -1818,6 +1852,7 @@ void CrossDeskServiceHost::RefreshSessionHelperReportedState() { json.value("interactive_stage", std::string()); session_helper_report_state_age_ms_ = json.value("state_age_ms", 0ull); session_helper_report_uptime_ms_ = json.value("uptime_ms", 0ull); + UpdateSasSecureDesktopGraceLocked(session_helper_report_interactive_stage_); } void CrossDeskServiceHost::RecordSessionEvent(DWORD event_type, @@ -1947,6 +1982,8 @@ std::string CrossDeskServiceHost::BuildStatusResponse() { std::string secure_input_helper_interactive_stage = EscapeJsonString(secure_input_helper_interactive_stage_); bool interactive_state_ready = session_helper_status_ok_; + const bool sas_secure_desktop_grace_active = + IsSasSecureDesktopGraceActiveLocked(); const char* interactive_state_source = interactive_state_ready ? "session-helper" : "service-host"; const bool effective_session_locked = GetEffectiveSessionLockedLocked(); @@ -1960,21 +1997,23 @@ std::string CrossDeskServiceHost::BuildStatusResponse() { bool unlock_ui_visible = interactive_state_ready ? session_helper_report_unlock_ui_visible_ : (logon_ui_visible_ || secure_desktop_active_); + unlock_ui_visible = unlock_ui_visible || sas_secure_desktop_grace_active; bool interactive_secure_desktop_active = interactive_state_ready ? session_helper_report_secure_desktop_active_ : secure_desktop_active_; + interactive_secure_desktop_active = + interactive_secure_desktop_active || sas_secure_desktop_grace_active; bool interactive_logon_ui_visible = interactive_state_ready ? session_helper_report_logon_ui_visible_ : logon_ui_visible_; bool interactive_session_locked = effective_session_locked || interactive_lock_screen_visible || - unlock_ui_visible; + unlock_ui_visible || + sas_secure_desktop_grace_active; std::string interactive_input_desktop = EscapeJsonString( interactive_state_ready ? session_helper_report_input_desktop_ : input_desktop_name_); - std::string raw_interactive_stage = DetermineInteractiveStage( - interactive_lock_screen_visible, credential_ui_visible, - interactive_secure_desktop_active); + std::string raw_interactive_stage = ResolveInteractiveStageLocked(); std::string interactive_stage = EscapeJsonString(raw_interactive_stage); std::ostringstream stream; stream << "{\"ok\":true,\"service\":\"CrossDeskService\"" @@ -1996,6 +2035,8 @@ std::string CrossDeskServiceHost::BuildStatusResponse() { << (interactive_logon_ui_visible ? "true" : "false") << ",\"interactive_secure_desktop_active\":" << (interactive_secure_desktop_active ? "true" : "false") + << ",\"sas_secure_desktop_grace_active\":" + << (sas_secure_desktop_grace_active ? "true" : "false") << ",\"unlock_ui_visible\":" << (unlock_ui_visible ? "true" : "false") << ",\"credential_ui_visible\":" << (credential_ui_visible ? "true" : "false") @@ -2100,10 +2141,14 @@ std::string CrossDeskServiceHost::SendSecureAttentionSequence() { SasResult result = SendSasNow(); { std::lock_guard lock(state_mutex_); - last_sas_tick_ = GetTickCount64(); + const ULONGLONG now = GetTickCount64(); + last_sas_tick_ = now; last_sas_success_ = result.success; last_sas_error_code_ = result.error_code; last_sas_error_ = result.error; + sas_secure_desktop_until_tick_ = + result.success ? now + kSasSecureDesktopGraceMs : 0; + sas_secure_desktop_seen_ = false; } if (!result.success) { diff --git a/src/service/windows/service_host.h b/src/service/windows/service_host.h index b99b7cc..e044f34 100644 --- a/src/service/windows/service_host.h +++ b/src/service/windows/service_host.h @@ -56,6 +56,8 @@ class CrossDeskServiceHost { bool GetEffectiveSessionLockedLocked() const; bool IsHelperReportingLockScreenLocked() const; bool HasSecureInputUiLocked() const; + void UpdateSasSecureDesktopGraceLocked(const std::string& observed_stage); + bool IsSasSecureDesktopGraceActiveLocked() const; bool ShouldKeepSecureInputHelperLocked(DWORD target_session_id) const; std::string ResolveInteractiveStageLocked() const; void RefreshSessionHelperReportedState(); @@ -103,6 +105,7 @@ class CrossDeskServiceHost { ULONGLONG session_helper_report_state_age_ms_ = 0; ULONGLONG session_helper_report_uptime_ms_ = 0; ULONGLONG secure_input_helper_started_at_tick_ = 0; + ULONGLONG sas_secure_desktop_until_tick_ = 0; bool session_locked_ = false; bool logon_ui_visible_ = false; bool prelogin_ = false; @@ -119,6 +122,7 @@ class CrossDeskServiceHost { bool session_helper_report_unlock_ui_visible_ = false; bool secure_input_helper_running_ = false; bool console_mode_ = false; + bool sas_secure_desktop_seen_ = false; DWORD last_sas_error_code_ = 0; bool last_sas_success_ = false; HANDLE session_helper_process_handle_ = nullptr; diff --git a/src/service/windows/session_helper_main.cpp b/src/service/windows/session_helper_main.cpp index 6532cf3..d392d24 100644 --- a/src/service/windows/session_helper_main.cpp +++ b/src/service/windows/session_helper_main.cpp @@ -31,6 +31,10 @@ using crossdesk::get_logger; using crossdesk::InitLogger; using Json = nlohmann::json; +constexpr DWORD kSessionHelperStatePollMs = 1000; + +std::atomic g_session_helper_desktop_switch_event{nullptr}; + struct InputDesktopInfo { bool available = false; DWORD error_code = 0; @@ -134,6 +138,54 @@ void EnablePerMonitorDpiAwareness() { DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); } +void CALLBACK SessionHelperDesktopSwitchWinEventProc( + HWINEVENTHOOK, DWORD event, HWND, LONG, LONG, DWORD, DWORD) { + if (event != EVENT_SYSTEM_DESKTOPSWITCH) { + return; + } + + HANDLE event_handle = + g_session_helper_desktop_switch_event.load(std::memory_order_relaxed); + if (event_handle != nullptr) { + SetEvent(event_handle); + } +} + +void EnsureSessionHelperMessageQueue() { + MSG message{}; + PeekMessageW(&message, nullptr, WM_USER, WM_USER, PM_NOREMOVE); +} + +void PumpSessionHelperMessages() { + MSG message{}; + while (PeekMessageW(&message, nullptr, 0, 0, PM_REMOVE)) { + TranslateMessage(&message); + DispatchMessageW(&message); + } +} + +DWORD WaitForSessionHelperStateChange(HANDLE stop_event, + HANDLE desktop_switch_event) { + std::vector wait_handles; + if (stop_event != nullptr) { + wait_handles.push_back(stop_event); + } + if (desktop_switch_event != nullptr) { + wait_handles.push_back(desktop_switch_event); + } + + const DWORD handle_count = static_cast(wait_handles.size()); + const DWORD wait_result = MsgWaitForMultipleObjects( + handle_count, wait_handles.empty() ? nullptr : wait_handles.data(), FALSE, + kSessionHelperStatePollMs, QS_ALLINPUT); + if (wait_result == WAIT_OBJECT_0 + handle_count) { + PumpSessionHelperMessages(); + return WAIT_TIMEOUT; + } + + return wait_result; +} + std::wstring Utf8ToWide(const std::string& value) { if (value.empty()) { return {}; @@ -308,8 +360,13 @@ void UpdateHelperState(HelperState* helper_state) { IsLockAppRunningInCurrentSession(helper_state->session_id); bool logon_ui_visible = IsLogonUiRunningInCurrentSession(helper_state->session_id); - const bool secure_desktop_active = + const bool input_desktop_is_winlogon = _stricmp(desktop_info.name.c_str(), "Winlogon") == 0; + const bool inaccessible_secure_input_desktop = + !desktop_info.available && + desktop_info.error_code == ERROR_ACCESS_DENIED && !logon_ui_visible; + const bool secure_desktop_active = input_desktop_is_winlogon || + inaccessible_secure_input_desktop; bool session_locked = false; if (!QuerySessionLockState(helper_state->session_id, &session_locked)) { session_locked = @@ -1796,6 +1853,26 @@ int main(int argc, char* argv[]) { helper_state.started_at_tick = GetTickCount64(); UpdateHelperState(&helper_state); + EnsureSessionHelperMessageQueue(); + HANDLE desktop_switch_event = CreateEventW(nullptr, FALSE, FALSE, nullptr); + HWINEVENTHOOK desktop_switch_hook = nullptr; + if (desktop_switch_event != nullptr) { + g_session_helper_desktop_switch_event.store( + desktop_switch_event, std::memory_order_relaxed); + desktop_switch_hook = SetWinEventHook( + EVENT_SYSTEM_DESKTOPSWITCH, EVENT_SYSTEM_DESKTOPSWITCH, nullptr, + SessionHelperDesktopSwitchWinEventProc, 0, 0, + WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS); + if (desktop_switch_hook == nullptr) { + LOG_WARN( + "SetWinEventHook(EVENT_SYSTEM_DESKTOPSWITCH) failed, error={}", + GetLastError()); + } + } else { + LOG_WARN("CreateEventW failed for desktop switch watcher, error={}", + GetLastError()); + } + std::thread ipc_thread(HelperIpcServerLoop, stop_event, current_session_id, &helper_state); @@ -1847,13 +1924,17 @@ int main(int argc, char* argv[]) { last_stage = stage; } - DWORD wait_result = stop_event != nullptr - ? WaitForSingleObject(stop_event, 1000) - : WAIT_TIMEOUT; - if (wait_result == WAIT_OBJECT_0) { + DWORD wait_result = + WaitForSessionHelperStateChange(stop_event, desktop_switch_event); + if (stop_event != nullptr && wait_result == WAIT_OBJECT_0) { break; } - if (wait_result != WAIT_TIMEOUT && wait_result != WAIT_FAILED) { + if (wait_result == WAIT_FAILED) { + LOG_WARN("Session helper wait failed, error={}", GetLastError()); + break; + } + if (wait_result != WAIT_TIMEOUT && wait_result != WAIT_OBJECT_0 && + wait_result != WAIT_OBJECT_0 + 1) { break; } } @@ -1862,6 +1943,15 @@ int main(int argc, char* argv[]) { ipc_thread.join(); } + if (desktop_switch_hook != nullptr) { + UnhookWinEvent(desktop_switch_hook); + } + g_session_helper_desktop_switch_event.store(nullptr, + std::memory_order_relaxed); + if (desktop_switch_event != nullptr) { + CloseHandle(desktop_switch_event); + } + if (stop_event != nullptr) { CloseHandle(stop_event); } diff --git a/tests/windows_sas_guard_test.cpp b/tests/windows_sas_guard_test.cpp new file mode 100644 index 0000000..3223d16 --- /dev/null +++ b/tests/windows_sas_guard_test.cpp @@ -0,0 +1,149 @@ +#include +#include +#include +#include +#include + +#include "interactive_state.h" + +namespace { + +std::filesystem::path FindRepoRoot() { + std::filesystem::path current = std::filesystem::current_path(); + while (!current.empty()) { + if (std::filesystem::exists(current / "xmake.lua") && + std::filesystem::exists( + current / "src/service/windows/service_host.cpp")) { + return current; + } + current = current.parent_path(); + } + return {}; +} + +std::string ReadFile(const std::filesystem::path& path) { + std::ifstream file(path, std::ios::binary); + if (!file) { + return {}; + } + + std::ostringstream stream; + stream << file.rdbuf(); + return stream.str(); +} + +bool ExpectContains(const char* name, const std::string& value, + const std::string& expected) { + if (value.find(expected) != std::string::npos) { + return true; + } + + std::cerr << name << " missing expected text: " << expected << "\n"; + return false; +} + +bool ExpectNotContains(const char* name, const std::string& value, + const std::string& unexpected) { + if (value.find(unexpected) == std::string::npos) { + return true; + } + + std::cerr << name << " contains unexpected text: " << unexpected << "\n"; + return false; +} + +bool ExpectTrue(const char* name, bool value) { + if (value) { + return true; + } + + std::cerr << name << " expected true\n"; + return false; +} + +} // namespace + +int main() { + const std::filesystem::path repo_root = FindRepoRoot(); + if (repo_root.empty()) { + std::cerr << "failed to locate repository root\n"; + return 1; + } + + const std::string control_bar = + ReadFile(repo_root / "src/gui/toolbars/control_bar.cpp"); + const std::string render = ReadFile(repo_root / "src/gui/render.cpp"); + const std::string render_h = ReadFile(repo_root / "src/gui/render.h"); + const std::string service_host = + ReadFile(repo_root / "src/service/windows/service_host.cpp"); + const std::string service_host_h = + ReadFile(repo_root / "src/service/windows/service_host.h"); + const std::string session_helper = + ReadFile(repo_root / "src/service/windows/session_helper_main.cpp"); + + bool ok = true; + ok &= ExpectTrue("secure desktop input routing", + crossdesk::IsSecureDesktopInteractionRequired( + "secure-desktop")); + ok &= ExpectNotContains("control_bar.cpp", control_bar, + "CanSendSecureAttentionSequence(" + "props->remote_interactive_stage_)"); + ok &= ExpectNotContains("control_bar.cpp", control_bar, + "ImGui::BeginDisabled();\n" + " }\n" + " if (ImGui::Selectable(sas_label.c_str()))"); + ok &= ExpectNotContains("render.cpp", render, "sas_requires_lock_screen"); + ok &= ExpectContains("render.h", render_h, + "optimistic_windows_secure_desktop_until_tick_"); + ok &= ExpectContains("render.cpp", render, + "kWindowsServiceSasSecureDesktopGraceMs"); + ok &= ExpectContains("render.cpp", render, + "status->sas_secure_desktop_grace_active"); + ok &= ExpectContains("render.cpp", render, + "json.value(\"sas_secure_desktop_grace_active\", false)"); + ok &= ExpectContains("render.cpp", render, + "status.sas_secure_desktop_grace_active"); + ok &= ExpectContains("render.cpp", render, + "local_interactive_stage_ = \"secure-desktop\""); + ok &= ExpectContains("service_host.h", service_host_h, + "sas_secure_desktop_until_tick_"); + ok &= ExpectContains("service_host.h", service_host_h, + "sas_secure_desktop_seen_"); + ok &= ExpectContains("service_host.cpp", service_host, + "kSasSecureDesktopGraceMs"); + ok &= ExpectContains("service_host.cpp", service_host, + "IsSasSecureDesktopGraceActiveLocked()"); + ok &= ExpectContains("service_host.cpp", service_host, + "UpdateSasSecureDesktopGraceLocked(" + "session_helper_report_interactive_stage_)"); + ok &= ExpectContains("service_host.cpp", service_host, + "sas_secure_desktop_seen_ = true"); + ok &= ExpectContains("service_host.cpp", service_host, + "sas_secure_desktop_until_tick_ = 0"); + ok &= ExpectContains("service_host.cpp", service_host, + "sas_secure_desktop_until_tick_ ="); + ok &= ExpectContains("service_host.cpp", service_host, + "now + kSasSecureDesktopGraceMs"); + ok &= ExpectContains("service_host.cpp", service_host, + "\\\"sas_secure_desktop_grace_active\\\""); + ok &= ExpectContains("service_host.cpp", service_host, + "raw_interactive_stage = ResolveInteractiveStageLocked()"); + ok &= ExpectContains("session_helper_main.cpp", session_helper, + "kSessionHelperStatePollMs = 1000"); + ok &= ExpectContains("session_helper_main.cpp", session_helper, + "EVENT_SYSTEM_DESKTOPSWITCH"); + ok &= ExpectContains("session_helper_main.cpp", session_helper, + "SetWinEventHook("); + ok &= ExpectContains("session_helper_main.cpp", session_helper, + "MsgWaitForMultipleObjects"); + ok &= ExpectContains("session_helper_main.cpp", session_helper, + "WaitForSessionHelperStateChange(stop_event, " + "desktop_switch_event)"); + ok &= ExpectContains("session_helper_main.cpp", session_helper, + "inaccessible_secure_input_desktop"); + ok &= ExpectContains("session_helper_main.cpp", session_helper, + "desktop_info.error_code == ERROR_ACCESS_DENIED"); + ok &= ExpectContains("session_helper_main.cpp", session_helper, + "secure_desktop_active = input_desktop_is_winlogon ||"); + return ok ? 0 : 1; +} diff --git a/xmake/targets.lua b/xmake/targets.lua index 985e3c3..443c79a 100644 --- a/xmake/targets.lua +++ b/xmake/targets.lua @@ -54,6 +54,12 @@ function setup_targets() set_default(false) add_files("tests/windows_mouse_controller_safety_test.cpp") + target("windows_sas_guard_test") + set_kind("binary") + set_default(false) + add_includedirs("src/service/windows") + add_files("tests/windows_sas_guard_test.cpp") + target("display_popup_hover_state_test") set_kind("binary") set_default(false)