mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-11-11 02:06:38 +08:00
[fix] reconcile Windows autostart and preserve applied settings
This commit is contained in:
@@ -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<unsigned long> 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) {
|
||||
|
||||
@@ -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<std::pair<const char*, std::string>> values);
|
||||
mutable std::mutex file_path_mutex_;
|
||||
std::string config_path_;
|
||||
|
||||
@@ -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__)
|
||||
|
||||
@@ -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<std::wstring>& 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<IRegisteredTaskCollection> 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<std::wstring> 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<std::wstring> 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<std::wstring> 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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<QUERY_SERVICE_CONFIGW*>(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";
|
||||
|
||||
@@ -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
|
||||
#endif
|
||||
|
||||
@@ -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
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user