mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-10-26 23:33:02 +08:00
[fix] preserve Windows remote sessions during shutdown
This commit is contained in:
@@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||||
|
<assemblyIdentity version="1.0.0.0" name="CrossDesk.SessionHelper" type="win32" />
|
||||||
|
<description>CrossDesk Session Helper</description>
|
||||||
|
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<security>
|
||||||
|
<requestedPrivileges>
|
||||||
|
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
|
||||||
|
</requestedPrivileges>
|
||||||
|
</security>
|
||||||
|
</trustInfo>
|
||||||
|
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<windowsSettings>
|
||||||
|
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
|
||||||
|
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
|
||||||
|
</windowsSettings>
|
||||||
|
</application>
|
||||||
|
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||||
|
<application>
|
||||||
|
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
|
||||||
|
</application>
|
||||||
|
</compatibility>
|
||||||
|
</assembly>
|
||||||
@@ -4,13 +4,10 @@ IDI_ICON1 ICON "crossdesk.ico"
|
|||||||
#define CREATEPROCESS_MANIFEST_RESOURCE_ID 1
|
#define CREATEPROCESS_MANIFEST_RESOURCE_ID 1
|
||||||
#define RT_MANIFEST 24
|
#define RT_MANIFEST 24
|
||||||
|
|
||||||
#ifdef CROSSDESK_PORTABLE
|
// The service supplies either the user's token (desktop observer) or a SYSTEM
|
||||||
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "crossdesk_portable.manifest"
|
// token (secure capture/input). Requiring elevation here prevents the observer
|
||||||
#elif defined(CROSSDESK_DEBUG)
|
// from starting with CreateProcessAsUserW in installed release builds (740).
|
||||||
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "crossdesk_debug.manifest"
|
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "crossdesk_session_helper.manifest"
|
||||||
#else
|
|
||||||
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "crossdesk.manifest"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define CROSSDESK_FILE_DESCRIPTION "CrossDesk Session Helper"
|
#define CROSSDESK_FILE_DESCRIPTION "CrossDesk Session Helper"
|
||||||
#define CROSSDESK_ORIGINAL_FILENAME "crossdesk_session_helper.exe"
|
#define CROSSDESK_ORIGINAL_FILENAME "crossdesk_session_helper.exe"
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#include "platform/windows/gui/slint_backend.h"
|
#include "platform/windows/gui/slint_backend.h"
|
||||||
#include "service_host.h"
|
#include "service_host.h"
|
||||||
#include "service_lifetime.h"
|
#include "service_lifetime.h"
|
||||||
|
#include "shutdown_order.h"
|
||||||
#include "unattended_config.h"
|
#include "unattended_config.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -406,6 +407,16 @@ static int RunApplication(
|
|||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
// Apply in every process, including the daemon and --child. Windows does
|
||||||
|
// not inherit shutdown priority from a parent process.
|
||||||
|
const DWORD shutdown_error = crossdesk::ConfigureWindowsShutdownOrder(
|
||||||
|
crossdesk::WindowsShutdownRole::host);
|
||||||
|
if (shutdown_error != ERROR_SUCCESS) {
|
||||||
|
std::cerr << "Could not configure Windows host shutdown order, error="
|
||||||
|
<< shutdown_error << '\n';
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
// Run before GUI, configuration, logging or display initialization.
|
// Run before GUI, configuration, logging or display initialization.
|
||||||
if (argc == 2 &&
|
if (argc == 2 &&
|
||||||
|
|||||||
@@ -56,6 +56,7 @@
|
|||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
#include "platform/windows/gui/slint_backend.h"
|
#include "platform/windows/gui/slint_backend.h"
|
||||||
|
#include "platform/windows/gui/shutdown_session_guard.h"
|
||||||
#include "platform/windows/gui/tray/win_tray.h"
|
#include "platform/windows/gui/tray/win_tray.h"
|
||||||
#include "platform/windows/gui/window_icons.h"
|
#include "platform/windows/gui/window_icons.h"
|
||||||
#elif defined(__APPLE__)
|
#elif defined(__APPLE__)
|
||||||
@@ -923,6 +924,10 @@ void DragWindow(WindowHandle& component, int phase, float mouse_x,
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
struct GuiApplication::SlintUi {
|
struct GuiApplication::SlintUi {
|
||||||
|
#if _WIN32
|
||||||
|
std::unique_ptr<ShutdownSessionGuard> shutdown_guard;
|
||||||
|
std::wstring_view shutdown_reason;
|
||||||
|
#endif
|
||||||
gallery::Workspace gallery;
|
gallery::Workspace gallery;
|
||||||
std::filesystem::path gallery_path;
|
std::filesystem::path gallery_path;
|
||||||
std::vector<std::string> gallery_ids;
|
std::vector<std::string> gallery_ids;
|
||||||
@@ -1284,6 +1289,10 @@ void GuiApplication::InitializeUi() {
|
|||||||
slint::set_xdg_app_id("crossdesk");
|
slint::set_xdg_app_id("crossdesk");
|
||||||
#endif
|
#endif
|
||||||
ui_ = std::make_unique<SlintUi>();
|
ui_ = std::make_unique<SlintUi>();
|
||||||
|
#if _WIN32
|
||||||
|
ui_->shutdown_guard =
|
||||||
|
std::make_unique<ShutdownSessionGuard>(windows_rdp_handoff_connected_);
|
||||||
|
#endif
|
||||||
ui_->sponsorship_prompt_pending =
|
ui_->sponsorship_prompt_pending =
|
||||||
config_center_->GetSponsorshipPromptDismissedVersion() != CROSSDESK_VERSION;
|
config_center_->GetSponsorshipPromptDismissedVersion() != CROSSDESK_VERSION;
|
||||||
ui_->video_presenter =
|
ui_->video_presenter =
|
||||||
@@ -2258,6 +2267,11 @@ void GuiApplication::Tick() {
|
|||||||
slint::quit_event_loop();
|
slint::quit_event_loop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
#if _WIN32
|
||||||
|
if (ui_->shutdown_guard) {
|
||||||
|
ui_->shutdown_guard->Update(ui_->shutdown_reason);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
PollUpdateCheck();
|
PollUpdateCheck();
|
||||||
HandleSessionCleanup();
|
HandleSessionCleanup();
|
||||||
HandlePasswordChangeResult();
|
HandlePasswordChangeResult();
|
||||||
@@ -2815,6 +2829,15 @@ void GuiApplication::UpdateLocalization() {
|
|||||||
if (!ui_ || ui_->localized_language == language) {
|
if (!ui_ || ui_->localized_language == language) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
#if _WIN32
|
||||||
|
// All views refer to static literals; selecting the text needs no allocation
|
||||||
|
// and only runs when the language changes. Tick still checks connection state.
|
||||||
|
ui_->shutdown_reason = language == 0
|
||||||
|
? L"远程控制连接中,保留连接以确认或取消关机。"
|
||||||
|
: language == 2
|
||||||
|
? L"Удалённый сеанс активен. Соединение сохранено для подтверждения или отмены выключения."
|
||||||
|
: L"Remote session active. Keeping the connection available to confirm or cancel shutdown.";
|
||||||
|
#endif
|
||||||
ui_localization::ApplyMainWindowStrings(ui_->main, language);
|
ui_localization::ApplyMainWindowStrings(ui_->main, language);
|
||||||
ui_localization::ApplyStreamWindowStrings(ui_->main, language);
|
ui_localization::ApplyStreamWindowStrings(ui_->main, language);
|
||||||
ui_localization::ApplyMainWindowStrings(ui_->wall, language);
|
ui_localization::ApplyMainWindowStrings(ui_->wall, language);
|
||||||
@@ -6191,6 +6214,9 @@ void GuiApplication::Cleanup() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ui_->file_dialog_lifetime.reset();
|
ui_->file_dialog_lifetime.reset();
|
||||||
|
#if _WIN32
|
||||||
|
ui_->shutdown_guard.reset();
|
||||||
|
#endif
|
||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
CancelNativeFileDialog();
|
CancelNativeFileDialog();
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -0,0 +1,121 @@
|
|||||||
|
#include "shutdown_session_guard.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "rd_log.h"
|
||||||
|
|
||||||
|
namespace crossdesk {
|
||||||
|
namespace {
|
||||||
|
constexpr wchar_t kShutdownWindowClass[] = L"CrossDesk.ShutdownSessionGuard";
|
||||||
|
}
|
||||||
|
|
||||||
|
ShutdownSessionGuard::ShutdownSessionGuard(
|
||||||
|
std::shared_ptr<std::atomic<bool>> connected)
|
||||||
|
: connected_(std::move(connected)) {}
|
||||||
|
|
||||||
|
ShutdownSessionGuard::~ShutdownSessionGuard() {
|
||||||
|
ClearReason();
|
||||||
|
if (window_) DestroyWindow(window_);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShutdownSessionGuard::ReportError(const char* operation) {
|
||||||
|
const DWORD error = GetLastError();
|
||||||
|
const ULONGLONG now = GetTickCount64();
|
||||||
|
if (last_error_tick_ == 0 || now - last_error_tick_ >= 5000) {
|
||||||
|
LOG_WARN("Windows shutdown session guard {} failed, error={}", operation, error);
|
||||||
|
last_error_tick_ = now;
|
||||||
|
}
|
||||||
|
SetLastError(error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShutdownSessionGuard::Start() {
|
||||||
|
if (window_) return true;
|
||||||
|
WNDCLASSW klass{};
|
||||||
|
klass.lpfnWndProc = WindowProc;
|
||||||
|
klass.hInstance = GetModuleHandleW(nullptr);
|
||||||
|
klass.lpszClassName = kShutdownWindowClass;
|
||||||
|
if (!RegisterClassW(&klass) && GetLastError() != ERROR_CLASS_ALREADY_EXISTS)
|
||||||
|
return ReportError("register window");
|
||||||
|
// A top-level window receives session-end broadcasts even when the Slint
|
||||||
|
// windows are hidden in the tray. HWND_MESSAGE does not receive them.
|
||||||
|
window_ = CreateWindowExW(WS_EX_TOOLWINDOW, kShutdownWindowClass, L"CrossDesk",
|
||||||
|
WS_OVERLAPPED, 0, 0, 0, 0, nullptr, nullptr, klass.hInstance, this);
|
||||||
|
if (!window_) return ReportError("create window");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShutdownSessionGuard::ClearReason() {
|
||||||
|
if (!reason_registered_) return true;
|
||||||
|
if (window_ && !ShutdownBlockReasonDestroy(window_))
|
||||||
|
return ReportError("clear reason");
|
||||||
|
reason_registered_ = false;
|
||||||
|
LOG_INFO("Windows shutdown session guard released");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShutdownSessionGuard::SyncReason() {
|
||||||
|
if (shutdown_allowed_ || !connected_ ||
|
||||||
|
!connected_->load(std::memory_order_relaxed)) {
|
||||||
|
return ClearReason();
|
||||||
|
}
|
||||||
|
if (reason_registered_) return true;
|
||||||
|
if (!ShutdownBlockReasonCreate(window_, reason_.c_str()))
|
||||||
|
return ReportError("register reason");
|
||||||
|
reason_registered_ = true;
|
||||||
|
LOG_INFO("Windows shutdown session guard armed for connected controller");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShutdownSessionGuard::Update(std::wstring_view reason) {
|
||||||
|
if (!Start()) return false;
|
||||||
|
if (std::wstring_view(reason_) != reason) {
|
||||||
|
// Keep the old registration and text on failure so the next tick retries
|
||||||
|
// removal before registering the newly selected language.
|
||||||
|
if (!ClearReason()) return false;
|
||||||
|
reason_.assign(reason);
|
||||||
|
}
|
||||||
|
return SyncReason();
|
||||||
|
}
|
||||||
|
|
||||||
|
LRESULT CALLBACK ShutdownSessionGuard::WindowProc(
|
||||||
|
HWND window, UINT message, WPARAM wparam, LPARAM lparam) {
|
||||||
|
if (message == WM_NCCREATE) {
|
||||||
|
const auto* create = reinterpret_cast<CREATESTRUCTW*>(lparam);
|
||||||
|
auto* guard = static_cast<ShutdownSessionGuard*>(create->lpCreateParams);
|
||||||
|
guard->window_ = window;
|
||||||
|
SetWindowLongPtrW(window, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(guard));
|
||||||
|
}
|
||||||
|
auto* guard = reinterpret_cast<ShutdownSessionGuard*>(
|
||||||
|
GetWindowLongPtrW(window, GWLP_USERDATA));
|
||||||
|
if (guard && message == WM_QUERYENDSESSION) {
|
||||||
|
const auto flags = static_cast<DWORD>(lparam);
|
||||||
|
// "Shut down anyway", logoff and Restart Manager must remain available.
|
||||||
|
if ((flags & (ENDSESSION_CRITICAL | ENDSESSION_LOGOFF | ENDSESSION_CLOSEAPP)) != 0)
|
||||||
|
guard->shutdown_allowed_ = true;
|
||||||
|
guard->SyncReason();
|
||||||
|
const bool retain = !guard->shutdown_allowed_ && guard->reason_registered_ &&
|
||||||
|
guard->connected_ && guard->connected_->load(std::memory_order_relaxed);
|
||||||
|
LOG_INFO("Windows shutdown query: flags=0x{:x}, retain_remote_session={}", flags, retain);
|
||||||
|
return retain ? FALSE : TRUE;
|
||||||
|
}
|
||||||
|
if (guard && message == WM_ENDSESSION) {
|
||||||
|
LOG_INFO("Windows session end: confirmed={}, flags=0x{:x}",
|
||||||
|
wparam != FALSE, static_cast<DWORD>(lparam));
|
||||||
|
guard->shutdown_allowed_ = wparam != FALSE;
|
||||||
|
guard->SyncReason();
|
||||||
|
// A cancelled shutdown must leave transport, capture and input running.
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (guard && message == WM_NCDESTROY) {
|
||||||
|
guard->ClearReason();
|
||||||
|
// Destruction removes the native registration even if explicit removal
|
||||||
|
// failed. A later window must not inherit the destroyed window's state.
|
||||||
|
guard->reason_registered_ = false;
|
||||||
|
guard->window_ = nullptr;
|
||||||
|
SetWindowLongPtrW(window, GWLP_USERDATA, 0);
|
||||||
|
}
|
||||||
|
return DefWindowProcW(window, message, wparam, lparam);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace crossdesk
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* @Author: DI JUNKUN
|
||||||
|
* @Date: 2026-10-22
|
||||||
|
* Copyright (c) 2026 by DI JUNKUN, All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _SHUTDOWN_SESSION_GUARD_H_
|
||||||
|
#define _SHUTDOWN_SESSION_GUARD_H_
|
||||||
|
|
||||||
|
#include <Windows.h>
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
namespace crossdesk {
|
||||||
|
|
||||||
|
// Owned and pumped by the UI thread. The transport may revoke the shared
|
||||||
|
// connection flag from another thread, including between UI ticks.
|
||||||
|
class ShutdownSessionGuard {
|
||||||
|
public:
|
||||||
|
explicit ShutdownSessionGuard(std::shared_ptr<std::atomic<bool>> connected);
|
||||||
|
~ShutdownSessionGuard();
|
||||||
|
ShutdownSessionGuard(const ShutdownSessionGuard&) = delete;
|
||||||
|
ShutdownSessionGuard& operator=(const ShutdownSessionGuard&) = delete;
|
||||||
|
|
||||||
|
bool Update(std::wstring_view reason);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool Start();
|
||||||
|
bool SyncReason();
|
||||||
|
bool ClearReason();
|
||||||
|
bool ReportError(const char* operation);
|
||||||
|
static LRESULT CALLBACK WindowProc(HWND window, UINT message,
|
||||||
|
WPARAM wparam, LPARAM lparam);
|
||||||
|
|
||||||
|
std::shared_ptr<std::atomic<bool>> connected_;
|
||||||
|
HWND window_ = nullptr;
|
||||||
|
std::wstring reason_;
|
||||||
|
bool reason_registered_ = false;
|
||||||
|
bool shutdown_allowed_ = false;
|
||||||
|
ULONGLONG last_error_tick_ = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace crossdesk
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* @Author: DI JUNKUN
|
* @Author: DI JUNKUN
|
||||||
* @Date: 2026-09-07
|
* @Date: 2026-10-22
|
||||||
* Copyright (c) 2026 by DI JUNKUN, All Rights Reserved.
|
* Copyright (c) 2026 by DI JUNKUN, All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -19,6 +19,15 @@ inline bool IsDesktopTransitionInputError(DWORD error) {
|
|||||||
error == ERROR_REQUIRES_INTERACTIVE_WINDOWSTATION;
|
error == ERROR_REQUIRES_INTERACTIVE_WINDOWSTATION;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only call with an observation from the interactive session, never Session 0.
|
||||||
|
// Process presence (including LogonUI) cannot prove that Default is accessible:
|
||||||
|
// shutdown blocking UI may be active while WTS still reports an unlocked user.
|
||||||
|
inline bool IsSecureInputDesktop(bool available, const std::string& name,
|
||||||
|
DWORD error) {
|
||||||
|
if (!available) return error == ERROR_ACCESS_DENIED;
|
||||||
|
return !name.empty() && _stricmp(name.c_str(), "Default") != 0;
|
||||||
|
}
|
||||||
|
|
||||||
inline std::wstring GetDesktopNameW(HDESK desktop) {
|
inline std::wstring GetDesktopNameW(HDESK desktop) {
|
||||||
DWORD bytes_needed = 0;
|
DWORD bytes_needed = 0;
|
||||||
if (desktop == nullptr) {
|
if (desktop == nullptr) {
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
#include "secure_desktop_frame_pacer.h"
|
#include "secure_desktop_frame_pacer.h"
|
||||||
#include "secure_desktop_nv12_cache.h"
|
#include "secure_desktop_nv12_cache.h"
|
||||||
#include "service_lifetime.h"
|
#include "service_lifetime.h"
|
||||||
|
#include "shutdown_order.h"
|
||||||
#include "shared_capture_lifecycle.h"
|
#include "shared_capture_lifecycle.h"
|
||||||
#include "shared_capture_configuration.h"
|
#include "shared_capture_configuration.h"
|
||||||
#include "usbmmidd_policy.h"
|
#include "usbmmidd_policy.h"
|
||||||
@@ -398,13 +399,8 @@ void UpdateHelperState(HelperState* helper_state) {
|
|||||||
// Process enumeration can straddle an unlock. Observe the input desktop
|
// Process enumeration can straddle an unlock. Observe the input desktop
|
||||||
// afterwards, and expose the sample's start time for service-side rejection.
|
// afterwards, and expose the sample's start time for service-side rejection.
|
||||||
InputDesktopInfo desktop_info = GetInputDesktopInfo();
|
InputDesktopInfo desktop_info = GetInputDesktopInfo();
|
||||||
const bool input_desktop_is_winlogon =
|
const bool secure_desktop_active = crossdesk::IsSecureInputDesktop(
|
||||||
_stricmp(desktop_info.name.c_str(), "Winlogon") == 0;
|
desktop_info.available, desktop_info.name, desktop_info.error_code);
|
||||||
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;
|
bool session_locked = false;
|
||||||
if (!QuerySessionLockState(helper_state->session_id, &session_locked)) {
|
if (!QuerySessionLockState(helper_state->session_id, &session_locked)) {
|
||||||
session_locked =
|
session_locked =
|
||||||
@@ -2043,9 +2039,15 @@ void PrintUsage() {
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
|
const DWORD shutdown_error = crossdesk::ConfigureWindowsShutdownOrder(
|
||||||
|
crossdesk::WindowsShutdownRole::helper);
|
||||||
EnablePerMonitorDpiAwareness();
|
EnablePerMonitorDpiAwareness();
|
||||||
|
|
||||||
InitializeHelperLogger();
|
InitializeHelperLogger();
|
||||||
|
if (shutdown_error != ERROR_SUCCESS) {
|
||||||
|
LOG_WARN("Could not configure Windows helper shutdown order, error={}",
|
||||||
|
shutdown_error);
|
||||||
|
}
|
||||||
|
|
||||||
bool run_helper = false;
|
bool run_helper = false;
|
||||||
bool run_secure_input_helper = false;
|
bool run_secure_input_helper = false;
|
||||||
@@ -2158,6 +2160,8 @@ int main(int argc, char* argv[]) {
|
|||||||
bool last_consent_ui = false;
|
bool last_consent_ui = false;
|
||||||
bool last_secure_desktop = false;
|
bool last_secure_desktop = false;
|
||||||
bool last_session_locked = false;
|
bool last_session_locked = false;
|
||||||
|
bool last_input_desktop_available = false;
|
||||||
|
DWORD last_input_desktop_error = ERROR_SUCCESS;
|
||||||
std::string last_stage;
|
std::string last_stage;
|
||||||
while (true) {
|
while (true) {
|
||||||
UpdateHelperState(&helper_state);
|
UpdateHelperState(&helper_state);
|
||||||
@@ -2165,6 +2169,7 @@ int main(int argc, char* argv[]) {
|
|||||||
std::string desktop_name;
|
std::string desktop_name;
|
||||||
bool session_locked = false;
|
bool session_locked = false;
|
||||||
bool input_desktop_available = false;
|
bool input_desktop_available = false;
|
||||||
|
DWORD input_desktop_error = ERROR_SUCCESS;
|
||||||
bool lock_app_visible = false;
|
bool lock_app_visible = false;
|
||||||
bool logon_ui_running = false;
|
bool logon_ui_running = false;
|
||||||
bool consent_ui_visible = false;
|
bool consent_ui_visible = false;
|
||||||
@@ -2174,6 +2179,7 @@ int main(int argc, char* argv[]) {
|
|||||||
desktop_name = helper_state.input_desktop_name;
|
desktop_name = helper_state.input_desktop_name;
|
||||||
session_locked = helper_state.session_locked;
|
session_locked = helper_state.session_locked;
|
||||||
input_desktop_available = helper_state.input_desktop_available;
|
input_desktop_available = helper_state.input_desktop_available;
|
||||||
|
input_desktop_error = helper_state.input_desktop_error_code;
|
||||||
lock_app_visible = helper_state.lock_app_visible;
|
lock_app_visible = helper_state.lock_app_visible;
|
||||||
logon_ui_running = helper_state.logon_ui_visible;
|
logon_ui_running = helper_state.logon_ui_visible;
|
||||||
consent_ui_visible = helper_state.consent_ui_visible;
|
consent_ui_visible = helper_state.consent_ui_visible;
|
||||||
@@ -2188,6 +2194,8 @@ int main(int argc, char* argv[]) {
|
|||||||
lock_app_visible, credential_ui_visible, secure_desktop_active);
|
lock_app_visible, credential_ui_visible, secure_desktop_active);
|
||||||
|
|
||||||
if (desktop_name != last_desktop_name ||
|
if (desktop_name != last_desktop_name ||
|
||||||
|
input_desktop_available != last_input_desktop_available ||
|
||||||
|
input_desktop_error != last_input_desktop_error ||
|
||||||
session_locked != last_session_locked ||
|
session_locked != last_session_locked ||
|
||||||
lock_app_visible != last_lock_app ||
|
lock_app_visible != last_lock_app ||
|
||||||
logon_ui_running != last_logon_ui ||
|
logon_ui_running != last_logon_ui ||
|
||||||
@@ -2195,11 +2203,15 @@ int main(int argc, char* argv[]) {
|
|||||||
secure_desktop_active != last_secure_desktop || stage != last_stage) {
|
secure_desktop_active != last_secure_desktop || stage != last_stage) {
|
||||||
LOG_INFO(
|
LOG_INFO(
|
||||||
"Session helper state: session_id={}, input_desktop='{}', "
|
"Session helper state: session_id={}, input_desktop='{}', "
|
||||||
|
"input_desktop_available={}, input_desktop_error={}, "
|
||||||
"session_locked={}, lock_app_visible={}, logon_ui_running={}, "
|
"session_locked={}, lock_app_visible={}, logon_ui_running={}, "
|
||||||
"consent_ui_visible={}, secure_desktop_active={}, stage={}",
|
"consent_ui_visible={}, secure_desktop_active={}, stage={}",
|
||||||
current_session_id, desktop_name, session_locked, lock_app_visible,
|
current_session_id, desktop_name, input_desktop_available,
|
||||||
|
input_desktop_error, session_locked, lock_app_visible,
|
||||||
logon_ui_running, consent_ui_visible, secure_desktop_active, stage);
|
logon_ui_running, consent_ui_visible, secure_desktop_active, stage);
|
||||||
last_desktop_name = desktop_name;
|
last_desktop_name = desktop_name;
|
||||||
|
last_input_desktop_available = input_desktop_available;
|
||||||
|
last_input_desktop_error = input_desktop_error;
|
||||||
last_session_locked = session_locked;
|
last_session_locked = session_locked;
|
||||||
last_lock_app = lock_app_visible;
|
last_lock_app = lock_app_visible;
|
||||||
last_logon_ui = logon_ui_running;
|
last_logon_ui = logon_ui_running;
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* @Author: DI JUNKUN
|
||||||
|
* @Date: 2026-10-22
|
||||||
|
* Copyright (c) 2026 by DI JUNKUN, All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _SHUTDOWN_ORDER_H_
|
||||||
|
#define _SHUTDOWN_ORDER_H_
|
||||||
|
|
||||||
|
#include <Windows.h>
|
||||||
|
|
||||||
|
namespace crossdesk {
|
||||||
|
|
||||||
|
enum class WindowsShutdownRole { host, helper };
|
||||||
|
|
||||||
|
// Windows shuts processes down from high to low (the default is 0x280).
|
||||||
|
// Keep the remote host alive while ordinary applications ask the user to
|
||||||
|
// resolve a blocked shutdown. The helpers must outlive the host. Stay inside
|
||||||
|
// the application range; 0x000-0x0ff is reserved for the operating system.
|
||||||
|
inline constexpr DWORD WindowsShutdownLevel(WindowsShutdownRole role) {
|
||||||
|
return role == WindowsShutdownRole::host ? 0x101 : 0x100;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline DWORD ConfigureWindowsShutdownOrder(WindowsShutdownRole role) {
|
||||||
|
// Change only the order. The GUI's session guard separately defers shutdown
|
||||||
|
// while a controller is connected; a confirmed shutdown still ends CrossDesk.
|
||||||
|
if (SetProcessShutdownParameters(WindowsShutdownLevel(role), 0))
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
const DWORD error = GetLastError();
|
||||||
|
return error != ERROR_SUCCESS ? error : ERROR_GEN_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace crossdesk
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -20,10 +20,6 @@ function setup_targets()
|
|||||||
end
|
end
|
||||||
table.insert(windows_resource_defines,
|
table.insert(windows_resource_defines,
|
||||||
"CROSSDESK_VERSION_NUMERIC=" .. table.concat(version_parts, ","))
|
"CROSSDESK_VERSION_NUMERIC=" .. table.concat(version_parts, ","))
|
||||||
local windows_helper_resource_defines = table.join(windows_resource_defines)
|
|
||||||
if is_config("CROSSDESK_PORTABLE", true) then
|
|
||||||
table.insert(windows_helper_resource_defines, "CROSSDESK_PORTABLE=1")
|
|
||||||
end
|
|
||||||
|
|
||||||
local function copy_slint_runtime(target)
|
local function copy_slint_runtime(target)
|
||||||
if not target:is_plat("windows") then
|
if not target:is_plat("windows") then
|
||||||
@@ -607,6 +603,7 @@ function setup_targets()
|
|||||||
add_files("apps/desktop/src/platform/windows/gui/tray/win_tray.cpp",
|
add_files("apps/desktop/src/platform/windows/gui/tray/win_tray.cpp",
|
||||||
"apps/desktop/src/platform/windows/gui/slint_backend.cpp",
|
"apps/desktop/src/platform/windows/gui/slint_backend.cpp",
|
||||||
"apps/desktop/src/platform/windows/gui/window_icons.cpp",
|
"apps/desktop/src/platform/windows/gui/window_icons.cpp",
|
||||||
|
"apps/desktop/src/platform/windows/gui/shutdown_session_guard.cpp",
|
||||||
"apps/desktop/src/platform/windows/gui/slint_renderer_probe.cpp",
|
"apps/desktop/src/platform/windows/gui/slint_renderer_probe.cpp",
|
||||||
"apps/desktop/src/platform/common/gui/opengl_video_renderer.cpp",
|
"apps/desktop/src/platform/common/gui/opengl_video_renderer.cpp",
|
||||||
"apps/desktop/src/platform/common/gui/cuda_driver.cpp",
|
"apps/desktop/src/platform/common/gui/cuda_driver.cpp",
|
||||||
@@ -681,7 +678,7 @@ function setup_targets()
|
|||||||
add_files("apps/desktop/src/platform/windows/service/session_helper_main.cpp")
|
add_files("apps/desktop/src/platform/windows/service/session_helper_main.cpp")
|
||||||
add_files("apps/desktop/src/platform/windows/virtual_display/usbmmidd_virtual_display.cpp")
|
add_files("apps/desktop/src/platform/windows/virtual_display/usbmmidd_virtual_display.cpp")
|
||||||
add_files("apps/desktop/resources/windows/crossdesk_session_helper.rc",
|
add_files("apps/desktop/resources/windows/crossdesk_session_helper.rc",
|
||||||
{defines = windows_helper_resource_defines})
|
{defines = windows_resource_defines})
|
||||||
add_includedirs("apps/desktop/src/common",
|
add_includedirs("apps/desktop/src/common",
|
||||||
"apps/desktop/src/platform/windows/service",
|
"apps/desktop/src/platform/windows/service",
|
||||||
"apps/desktop/src/platform/windows/screen_capturer",
|
"apps/desktop/src/platform/windows/screen_capturer",
|
||||||
|
|||||||
Reference in New Issue
Block a user