From 8370bf95d1cb74aaf660c5b303def3049e4ee697 Mon Sep 17 00:00:00 2001 From: dijunkun Date: Tue, 10 Nov 2026 22:53:32 +0800 Subject: [PATCH] [fix] reconcile Windows autostart and preserve applied settings --- .../src/config_center/config_center.cpp | 43 +++++++---- .../desktop/src/config_center/config_center.h | 2 +- .../src/gui/application/gui_application.cpp | 8 +- .../src/platform/windows/autostart.cpp | 53 +++++++++---- .../src/platform/windows/autostart_task.h | 5 ++ .../windows/service/autostart_control.cpp | 77 ++++++------------- .../windows/service/autostart_control.h | 10 ++- .../windows/service/autostart_state.cpp | 53 +++++++++++++ .../platform/windows/service/service_host.cpp | 21 ++++- .../platform/windows/service/service_host.h | 8 +- .../windows/service/unattended_config.cpp | 20 +++-- .../windows/service/unattended_config.h | 3 + apps/desktop/xmake/targets.lua | 3 +- 13 files changed, 210 insertions(+), 96 deletions(-) create mode 100644 apps/desktop/src/platform/windows/service/autostart_state.cpp diff --git a/apps/desktop/src/config_center/config_center.cpp b/apps/desktop/src/config_center/config_center.cpp index 55ea17e1..7ffbb487 100644 --- a/apps/desktop/src/config_center/config_center.cpp +++ b/apps/desktop/src/config_center/config_center.cpp @@ -187,16 +187,20 @@ int ConfigCenter::Load() { return 0; } -int ConfigCenter::CommitIni() { +int ConfigCenter::CommitIni(bool warn_only) { // Write beside the destination and rename only after a complete save. A // failed write must not truncate the last usable configuration. + const auto log_failure = [warn_only](const std::string& message) { + if (warn_only) LOG_WARN("{}", message); + else LOG_ERROR("{}", message); + }; static std::atomic sequence{0}; const std::string temporary = config_path_ + ".tmp-" + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()) + "-" + std::to_string(sequence++); std::error_code error; if (ini_->SaveFile(temporary.c_str()) < 0) { - LOG_ERROR("Failed to write configuration: {}", config_path_); + log_failure("Failed to write configuration: " + config_path_); std::filesystem::remove(temporary, error); return -1; } @@ -204,14 +208,14 @@ int ConfigCenter::CommitIni() { if (!error && std::filesystem::is_regular_file(previous)) { std::filesystem::permissions(temporary, previous.permissions(), error); if (error) { - LOG_ERROR("Failed to preserve configuration permissions: {}", error.message()); + log_failure("Failed to preserve configuration permissions: " + error.message()); std::filesystem::remove(temporary, error); return -1; } } std::filesystem::rename(temporary, config_path_, error); if (!error) return 0; - LOG_ERROR("Failed to replace configuration {}: {}", config_path_, error.message()); + log_failure("Failed to replace configuration " + config_path_ + ": " + error.message()); std::filesystem::remove(temporary, error); return -1; } @@ -428,15 +432,26 @@ int ConfigCenter::SetSelfHosted(bool enabled) { } int ConfigCenter::SetAutostart(bool enabled) { - std::string error = "Operating system startup registration failed"; #ifdef _WIN32 - if (!SupportsServiceAutostart()) return enabled ? -1 : 0; - const bool previous = IsServiceAutostartEnabled(); - const bool update_registration = true; - const auto configure = [&error](bool value) { return SetServiceAutostart(value, error); }; + // System registration determines success; INI only mirrors the last + // successfully applied preference. Unsupported builds never touch services. + if (!SupportsServiceAutostart()) { + if (enabled) return -1; + } else if (!ServiceAutostartMatches(enabled)) { + std::string error; + if (!SetServiceAutostart(enabled, error)) { + LOG_ERROR("Cannot configure autostart: {}", error); + return -1; + } + } + enable_autostart_ = enabled; + ini_->SetBoolValue(section_, "enable_autostart", enabled); + // Keep both in-memory copies even if the file is temporarily unwritable, + // so a later settings save can persist them. Never roll back the service. + CommitIni(true); + return 0; #else const bool previous = IsAutostartEnabled("CrossDesk"); - const bool update_registration = enabled != previous; const auto configure = [](bool value) { return value ? EnableAutostart("CrossDesk") : DisableAutostart("CrossDesk"); }; @@ -448,18 +463,18 @@ int ConfigCenter::SetAutostart(bool enabled) { return -1; } #endif -#endif - if (update_registration && !configure(enabled)) { - LOG_ERROR("Cannot configure autostart: {}", error); + if (enabled != previous && !configure(enabled)) { + LOG_ERROR("Cannot configure autostart"); return -1; } if (StoreValues({{"enable_autostart", enabled ? "true" : "false"}}) != 0) { if (enabled != previous && !configure(previous)) - LOG_ERROR("Cannot restore autostart: {}", error); + LOG_ERROR("Cannot restore autostart"); return -1; } enable_autostart_ = enabled; return 0; +#endif } int ConfigCenter::SetDaemon(bool enabled) { diff --git a/apps/desktop/src/config_center/config_center.h b/apps/desktop/src/config_center/config_center.h index 42be575b..3cb2e015 100644 --- a/apps/desktop/src/config_center/config_center.h +++ b/apps/desktop/src/config_center/config_center.h @@ -93,7 +93,7 @@ class ConfigCenter { int ApplySharedHostConfig(const std::string& contents); private: - int CommitIni(); + int CommitIni(bool warn_only = false); int StoreValues(std::initializer_list> values); mutable std::mutex file_path_mutex_; std::string config_path_; diff --git a/apps/desktop/src/gui/application/gui_application.cpp b/apps/desktop/src/gui/application/gui_application.cpp index 6aeb53ed..dadce98f 100644 --- a/apps/desktop/src/gui/application/gui_application.cpp +++ b/apps/desktop/src/gui/application/gui_application.cpp @@ -1216,9 +1216,13 @@ int GuiApplication::Run() { #endif #ifdef _WIN32 // Upgrade an already-enabled standalone host before the GUI opens its peer. + // Until an identity is attached the GUI can still run on its own identity, + // e.g. before its first connection has obtained a device ID. if (SupportsServiceAutostart() && IsUnattendedEnabled() && !shared_identity_) { - if (!PrepareSharedGuiIdentity(false) || config_center_->SetAutostart(true) != 0) - return -1; + if (!PrepareSharedGuiIdentity(false)) + LOG_WARN("Could not upgrade the unattended host; boot startup stays off"); + else if (config_center_->SetAutostart(true) != 0) + return -1; // Attached to a host that could not be configured. } #endif #if defined(__linux__) && !defined(__OHOS__) diff --git a/apps/desktop/src/platform/windows/autostart.cpp b/apps/desktop/src/platform/windows/autostart.cpp index 20705175..697f9e78 100644 --- a/apps/desktop/src/platform/windows/autostart.cpp +++ b/apps/desktop/src/platform/windows/autostart.cpp @@ -251,11 +251,17 @@ bool MigrateLegacyAutostart(const std::string& app_name) { return EnableAutostart(app_name, executable); } -bool RemoveInstalledAutostart(const std::string& app_name) { +namespace { + +bool CollectInstalledAutostart(const std::string& app_name, + TaskScheduler& scheduler, + std::vector& matching_tasks, + bool& matching_run_entry) { + matching_tasks.clear(); + matching_run_entry = false; const auto name = FromUtf8(app_name); const auto executable = FromUtf8(GetAutostartExecutablePath()); if (name.empty() || executable.empty()) return false; - TaskScheduler scheduler; if (!scheduler.Open()) return false; ComPtr tasks; if (!Check(scheduler.folder->GetTasks(TASK_ENUM_HIDDEN, tasks.GetAddressOf()), @@ -264,7 +270,6 @@ bool RemoveInstalledAutostart(const std::string& app_name) { LONG count = 0; if (!Check(tasks->get_Count(&count), "count logon tasks")) return false; const auto prefix = name + L" Autostart "; - std::vector remove; for (LONG index = 1; index <= count; ++index) { VARIANT item{}; item.vt = VT_I4; @@ -291,19 +296,14 @@ bool RemoveInstalledAutostart(const std::string& app_name) { if (action_count != 1) continue; if (!Check(actions->get_Item(1, action.GetAddressOf()), "read task action")) return false; - if (FAILED(action.As(&exec))) continue; + const HRESULT queried = action.As(&exec); + if (queried == E_NOINTERFACE) continue; + if (!Check(queried, "read executable action")) return false; BSTR raw_path = nullptr; if (!Check(exec->get_Path(&raw_path), "read task executable")) return false; Bstr path(raw_path, SysFreeString); if (path && _wcsicmp(path.get(), executable.c_str()) == 0) - remove.push_back(task_text); - } - // Collect names first; deleting during enumeration can change its indices. - for (const auto& task : remove) { - auto task_name = MakeBstr(task); - if (!task_name || !Check(scheduler.folder->DeleteTask(task_name.get(), 0), - "remove installed logon task")) - return false; + matching_tasks.push_back(task_text); } std::wstring command; const LONG result = ReadRunEntry(name, &command); @@ -312,8 +312,33 @@ bool RemoveInstalledAutostart(const std::string& app_name) { if (result != ERROR_SUCCESS) return false; if (command.size() >= 2 && command.front() == L'"' && command.back() == L'"') command = command.substr(1, command.size() - 2); - return _wcsicmp(command.c_str(), executable.c_str()) != 0 || - RemoveRunEntry(name); + matching_run_entry = _wcsicmp(command.c_str(), executable.c_str()) == 0; + return true; +} + +} // namespace + +bool IsInstalledAutostartAbsent(const std::string& app_name) { + TaskScheduler scheduler; + std::vector tasks; + bool run_entry = false; + return CollectInstalledAutostart(app_name, scheduler, tasks, run_entry) && + tasks.empty() && !run_entry; +} + +bool RemoveInstalledAutostart(const std::string& app_name) { + TaskScheduler scheduler; + std::vector tasks; + bool run_entry = false; + if (!CollectInstalledAutostart(app_name, scheduler, tasks, run_entry)) return false; + // Collect names first; deleting during enumeration can change its indices. + for (const auto& task : tasks) { + auto task_name = MakeBstr(task); + if (!task_name || !Check(scheduler.folder->DeleteTask(task_name.get(), 0), + "remove installed logon task")) + return false; + } + return !run_entry || RemoveRunEntry(FromUtf8(app_name)); } } // namespace crossdesk::platform diff --git a/apps/desktop/src/platform/windows/autostart_task.h b/apps/desktop/src/platform/windows/autostart_task.h index 366f7f68..e162f9f9 100644 --- a/apps/desktop/src/platform/windows/autostart_task.h +++ b/apps/desktop/src/platform/windows/autostart_task.h @@ -83,6 +83,11 @@ bool MigrateLegacyAutostart(const std::string& app_name); // registrations belonging to other installation/portable directories. bool RemoveInstalledAutostart(const std::string& app_name); +// Read-only counterpart of RemoveInstalledAutostart. Includes disabled tasks +// across users and this user's legacy Run entry for the current executable. +// False means either a matching registration remains or a query failed. +bool IsInstalledAutostartAbsent(const std::string& app_name); + } // namespace crossdesk::platform #endif diff --git a/apps/desktop/src/platform/windows/service/autostart_control.cpp b/apps/desktop/src/platform/windows/service/autostart_control.cpp index d5bb79fa..c63bfe98 100644 --- a/apps/desktop/src/platform/windows/service/autostart_control.cpp +++ b/apps/desktop/src/platform/windows/service/autostart_control.cpp @@ -6,64 +6,30 @@ #include "config_center.h" #include "path_manager.h" +#include "platform/autostart_backend.h" #include "platform/single_instance.h" #include "platform/windows/autostart_task.h" +#include "platform/windows/executable_path.h" #include "platform/windows/virtual_display/usbmmidd_virtual_display.h" -#include "platform/autostart_backend.h" +#include "rd_log.h" #include "service_host.h" #include "shared_identity.h" -#include "rd_log.h" #include "unattended_config.h" namespace crossdesk { -namespace { -struct ServiceHandle { - SC_HANDLE value; - ~ServiceHandle() { if (value) CloseServiceHandle(value); } -}; - -bool ReadServiceStartType(DWORD& start_type) { - start_type = SERVICE_DEMAND_START; - ServiceHandle manager{OpenSCManagerW(nullptr, nullptr, SC_MANAGER_CONNECT)}; - if (!manager.value) return false; - ServiceHandle service{OpenServiceW(manager.value, L"CrossDeskService", SERVICE_QUERY_CONFIG)}; - const DWORD open_error = service.value ? ERROR_SUCCESS : GetLastError(); - bool read = open_error == ERROR_SERVICE_DOES_NOT_EXIST; - if (service.value) { - alignas(QUERY_SERVICE_CONFIGW) BYTE buffer[8192]{}; - auto* config = reinterpret_cast(buffer); - DWORD size = 0; - read = QueryServiceConfigW(service.value, config, sizeof(buffer), &size) != FALSE; - if (read) start_type = config->dwStartType; - } - return read; -} - -bool ConfigureServiceAutostart(bool enabled) { - ServiceHandle manager{OpenSCManagerW(nullptr, nullptr, SC_MANAGER_CONNECT)}; - if (!manager.value) return false; - ServiceHandle service{OpenServiceW(manager.value, L"CrossDeskService", SERVICE_CHANGE_CONFIG)}; - if (!service.value) return !enabled && GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST; - return ChangeServiceConfigW(service.value, SERVICE_NO_CHANGE, - enabled ? SERVICE_AUTO_START : SERVICE_DEMAND_START, SERVICE_NO_CHANGE, - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr) != FALSE; -} -} // namespace bool SupportsServiceAutostart() { return platform::kAutostartRequiresElevation; } -bool IsServiceAutostartEnabled() { - if (!SupportsServiceAutostart() || !IsUnattendedEnabled() || - !IsSharedIdentityServiceConfigured() || !SharedHostIdentity::Configured()) return false; - DWORD start_type = 0; - return ReadServiceStartType(start_type) && start_type == SERVICE_AUTO_START; -} - bool CleanupLegacyServiceAutostart() { - return !SupportsServiceAutostart() || !IsServiceAutostartEnabled() || - platform::MigrateLegacyAutostart("CrossDesk"); + if (!SupportsServiceAutostart()) return true; + // Enabling sets the flag and disabling removes every task, so a task without + // the flag predates service startup. Remove it rather than silently turning + // it into pre-login access. With the flag set, only fold a Run entry back + // into the elevated handoff task. + return IsUnattendedEnabled() ? platform::MigrateLegacyAutostart("CrossDesk") + : platform::RemoveInstalledAutostart("CrossDesk"); } bool SetServiceAutostart(bool enabled, std::string& error) { @@ -84,22 +50,17 @@ bool SetServiceAutostart(bool enabled, std::string& error) { if (error.empty()) error = "Another service configuration is in progress"; return false; } - DWORD start_type = 0; - if (!ReadServiceStartType(start_type)) { - error = "Could not query Windows service startup configuration"; - return false; - } if (!enabled) { // Disarm the host without stopping the lock-screen service used by GUI // sessions. A missing service/binary must not prevent disabling startup. - if (SetUnattendedEnabled(false) && ConfigureServiceAutostart(false) && + if (SetUnattendedEnabled(false) && SetCrossDeskServiceAutoStart(false) && platform::RemoveInstalledAutostart("CrossDesk")) return true; error = "Could not disable unattended startup"; return false; } const auto executable = platform::GetAutostartExecutablePath(); - const auto service = std::filesystem::u8path(executable).parent_path() / L"crossdesk_service.exe"; + const auto service = platform::SiblingExecutablePath(L"crossdesk_service.exe"); std::error_code file_error; if (executable.empty() || !std::filesystem::is_regular_file(service, file_error) || !std::filesystem::is_regular_file(service.parent_path() / L"crossdesk_session_helper.exe", file_error)) { @@ -113,9 +74,17 @@ bool SetServiceAutostart(bool enabled, std::string& error) { error = "Enable boot startup from the running GUI settings, or close the GUI first"; return false; } + DWORD start_type = SERVICE_DEMAND_START; + const auto registration = + QueryCrossDeskServiceRegistration(service.wstring(), nullptr, &start_type); + if (registration == ServiceRegistrationState::query_failed) { + error = "Could not query Windows service startup configuration"; + return false; + } const bool current = IsSharedIdentityServiceConfigured() && - QueryCrossDeskServiceRegistration(service.wstring()) == ServiceRegistrationState::current; - if (!current && IsCrossDeskServiceInstalled() && !StopCrossDeskService(20000)) { + registration == ServiceRegistrationState::current; + if (!current && registration != ServiceRegistrationState::not_installed && + !StopCrossDeskService(20000)) { error = "Could not stop the old service to configure shared identity"; return false; } @@ -130,7 +99,7 @@ bool SetServiceAutostart(bool enabled, std::string& error) { StartCrossDeskService() && platform::EnableAutostart("CrossDesk", executable)) return true; const bool restored_flag = SetUnattendedEnabled(was_enabled); - const bool restored_start = ConfigureServiceAutostart(start_type == SERVICE_AUTO_START); + const bool restored_start = SetCrossDeskServiceAutoStart(start_type == SERVICE_AUTO_START); if (!restored_flag || !restored_start) LOG_ERROR("Could not restore the previous unattended startup configuration"); error = "Could not enable persistent host startup and the user-session GUI"; diff --git a/apps/desktop/src/platform/windows/service/autostart_control.h b/apps/desktop/src/platform/windows/service/autostart_control.h index 443ef181..2c079cd6 100644 --- a/apps/desktop/src/platform/windows/service/autostart_control.h +++ b/apps/desktop/src/platform/windows/service/autostart_control.h @@ -13,15 +13,21 @@ namespace crossdesk { // Windows startup preferences control the machine host and its GUI handoff. // Portable and Debug builds keep the existing unattended-mode restriction. +// Evaluate in the application target: callers such as config_center do not +// carry the application's CROSSDESK_PORTABLE define. bool SupportsServiceAutostart(); bool IsServiceAutostartEnabled(); +// True when the registration already matches `enabled`, so saving settings +// does not stop, reinstall or re-register anything. +bool ServiceAutostartMatches(bool enabled); // Shared GUI/CLI entry point; serializes service changes and preserves a // running host when the installed service already matches this executable. bool SetServiceAutostart(bool enabled, std::string& error); -// Keep a legacy Run entry from bypassing the elevated GUI handoff task. +// Logon startup only exists as the GUI handoff of an enabled machine host. +// Remove registrations left by versions before the service-based startup. bool CleanupLegacyServiceAutostart(); } // namespace crossdesk -#endif \ No newline at end of file +#endif diff --git a/apps/desktop/src/platform/windows/service/autostart_state.cpp b/apps/desktop/src/platform/windows/service/autostart_state.cpp new file mode 100644 index 00000000..7e78d5dd --- /dev/null +++ b/apps/desktop/src/platform/windows/service/autostart_state.cpp @@ -0,0 +1,53 @@ +#include "platform/windows/service/autostart_control.h" + +#include "platform/autostart_backend.h" +#include "platform/windows/autostart_task.h" +#include "platform/windows/executable_path.h" +#include "service_host.h" +#include "shared_identity.h" +#include "unattended_config.h" + +namespace crossdesk { +namespace { + +std::wstring ServiceExecutable() { + return platform::SiblingExecutablePath(L"crossdesk_service.exe").wstring(); +} + +bool SharedHostArmed() { + return SupportsServiceAutostart() && IsUnattendedEnabled() && + IsSharedIdentityServiceConfigured() && SharedHostIdentity::Configured(); +} + +} // namespace + +bool IsServiceAutostartEnabled() { + DWORD start_type = SERVICE_DEMAND_START; + return SharedHostArmed() && + QueryCrossDeskServiceRegistration(ServiceExecutable(), nullptr, &start_type) != + ServiceRegistrationState::query_failed && + start_type == SERVICE_AUTO_START; +} + +bool ServiceAutostartMatches(bool enabled) { + DWORD start_type = SERVICE_DEMAND_START; + if (!enabled) { + bool unattended = false; + if (!QueryUnattendedEnabled(unattended) || unattended) return false; + const auto registration = + QueryCrossDeskServiceRegistration(ServiceExecutable(), nullptr, &start_type); + if (registration == ServiceRegistrationState::query_failed) return false; + if (registration != ServiceRegistrationState::not_installed && + start_type != SERVICE_DEMAND_START && start_type != SERVICE_DISABLED) + return false; + // The flag can already be off after a partially failed disable. Only skip + // the retry after checking the same registrations that cleanup removes. + return platform::IsInstalledAutostartAbsent("CrossDesk"); + } + return SharedHostArmed() && + QueryCrossDeskServiceRegistration(ServiceExecutable(), nullptr, &start_type) == + ServiceRegistrationState::current && + start_type == SERVICE_AUTO_START && platform::IsAutostartEnabled("CrossDesk"); +} + +} // namespace crossdesk diff --git a/apps/desktop/src/platform/windows/service/service_host.cpp b/apps/desktop/src/platform/windows/service/service_host.cpp index e1f0c81b..ad25859c 100644 --- a/apps/desktop/src/platform/windows/service/service_host.cpp +++ b/apps/desktop/src/platform/windows/service/service_host.cpp @@ -2644,8 +2644,9 @@ std::string CrossDeskServiceHost::SendSecureDesktopTextInput( } ServiceRegistrationState QueryCrossDeskServiceRegistration( - const std::wstring& binary_path, DWORD* error_code) { + const std::wstring& binary_path, DWORD* error_code, DWORD* start_type) { if (error_code) *error_code = ERROR_SUCCESS; + if (start_type) *start_type = SERVICE_DEMAND_START; const auto failed = [error_code](DWORD error) { if (error_code) *error_code = error; return ServiceRegistrationState::query_failed; @@ -2672,6 +2673,7 @@ ServiceRegistrationState QueryCrossDeskServiceRegistration( const DWORD query_error = queried ? ERROR_SUCCESS : GetLastError(); CloseServiceHandle(service); if (!queried) return failed(query_error); + if (start_type) *start_type = config->dwStartType; if (!config->lpBinaryPathName || binary_path.empty() || config->dwStartType == SERVICE_DISABLED) { @@ -2782,6 +2784,23 @@ bool InstallCrossDeskService(const std::wstring& binary_path) { return true; } +bool SetCrossDeskServiceAutoStart(bool enabled) { + SC_HANDLE manager = OpenSCManagerW(nullptr, nullptr, SC_MANAGER_CONNECT); + if (!manager) return false; + SC_HANDLE service = + OpenServiceW(manager, kCrossDeskServiceName, SERVICE_CHANGE_CONFIG); + const DWORD open_error = service ? ERROR_SUCCESS : GetLastError(); + CloseServiceHandle(manager); + if (!service) return !enabled && open_error == ERROR_SERVICE_DOES_NOT_EXIST; + const bool changed = + ChangeServiceConfigW(service, SERVICE_NO_CHANGE, + enabled ? SERVICE_AUTO_START : SERVICE_DEMAND_START, + SERVICE_NO_CHANGE, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr) != FALSE; + CloseServiceHandle(service); + return changed; +} + bool IsCrossDeskServiceInstalled() { SC_HANDLE manager = OpenSCManagerW(nullptr, nullptr, SC_MANAGER_CONNECT); if (manager == nullptr) { diff --git a/apps/desktop/src/platform/windows/service/service_host.h b/apps/desktop/src/platform/windows/service/service_host.h index 51e12ff4..f4b6bceb 100644 --- a/apps/desktop/src/platform/windows/service/service_host.h +++ b/apps/desktop/src/platform/windows/service/service_host.h @@ -192,9 +192,15 @@ enum class ServiceRegistrationState { repair_required, query_failed, }; +// start_type receives the registered start type, or SERVICE_DEMAND_START when +// the service is not installed. ServiceRegistrationState QueryCrossDeskServiceRegistration( - const std::wstring& binary_path, DWORD* error_code = nullptr); + const std::wstring& binary_path, DWORD* error_code = nullptr, + DWORD* start_type = nullptr); bool InstallCrossDeskService(const std::wstring& binary_path); +// Switch between boot and on-demand start; a missing service counts as +// disabled. +bool SetCrossDeskServiceAutoStart(bool enabled); bool RepairCrossDeskService(const std::wstring& binary_path); bool UninstallCrossDeskService(); bool StartCrossDeskService(); diff --git a/apps/desktop/src/platform/windows/service/unattended_config.cpp b/apps/desktop/src/platform/windows/service/unattended_config.cpp index 568bc281..11e49ef4 100644 --- a/apps/desktop/src/platform/windows/service/unattended_config.cpp +++ b/apps/desktop/src/platform/windows/service/unattended_config.cpp @@ -65,13 +65,21 @@ std::filesystem::path UnattendedDataPath() { return path; } +bool QueryUnattendedEnabled(bool& enabled) { + enabled = false; + DWORD value = 0; + DWORD size = sizeof(value); + const LONG result = RegGetValueW(HKEY_LOCAL_MACHINE, kRegistryKey, L"Enabled", + RRF_RT_REG_DWORD | RRF_SUBKEY_WOW6464KEY, nullptr, &value, &size); + if (result == ERROR_FILE_NOT_FOUND || result == ERROR_PATH_NOT_FOUND) return true; + if (result != ERROR_SUCCESS) return false; + enabled = value == 1; + return true; +} + bool IsUnattendedEnabled() { - DWORD enabled = 0; - DWORD size = sizeof(enabled); - return RegGetValueW(HKEY_LOCAL_MACHINE, kRegistryKey, L"Enabled", - RRF_RT_REG_DWORD | RRF_SUBKEY_WOW6464KEY, nullptr, - &enabled, &size) == ERROR_SUCCESS && - enabled == 1; + bool enabled = false; + return QueryUnattendedEnabled(enabled) && enabled; } bool SetUnattendedEnabled(bool enabled) { diff --git a/apps/desktop/src/platform/windows/service/unattended_config.h b/apps/desktop/src/platform/windows/service/unattended_config.h index 3e654547..8a38bc4e 100644 --- a/apps/desktop/src/platform/windows/service/unattended_config.h +++ b/apps/desktop/src/platform/windows/service/unattended_config.h @@ -9,6 +9,9 @@ namespace crossdesk { // This directory is deliberately separate from every user's GUI profile. std::filesystem::path UnattendedDataPath(); +// Missing configuration is a successful disabled result; other read failures +// must not be mistaken for a completed disable operation. +bool QueryUnattendedEnabled(bool& enabled); bool IsUnattendedEnabled(); bool IsSharedIdentityServiceConfigured(); bool SetUnattendedEnabled(bool enabled); diff --git a/apps/desktop/xmake/targets.lua b/apps/desktop/xmake/targets.lua index 4270cf34..cc4bc3fc 100644 --- a/apps/desktop/xmake/targets.lua +++ b/apps/desktop/xmake/targets.lua @@ -786,7 +786,8 @@ function setup_targets() add_files("apps/desktop/src/app/*.cpp") add_includedirs("apps/desktop/src", "apps/desktop/src/app", {public = true}) if is_os("windows") then - add_files("apps/desktop/src/platform/windows/service/autostart_control.cpp") + add_files("apps/desktop/src/platform/windows/service/autostart_control.cpp", + "apps/desktop/src/platform/windows/service/autostart_state.cpp") add_files("apps/desktop/src/platform/windows/service/service_host.cpp", "apps/desktop/src/platform/windows/service/rdp_session_handoff.cpp") add_includedirs("apps/desktop/src/platform/windows/service",