[fix] repair Windows privacy screen startup and capture recovery

This commit is contained in:
kunkundi
2026-10-08 15:31:27 +08:00
parent 9550731681
commit 5af7cc35e9
3 changed files with 65 additions and 5 deletions
@@ -11,6 +11,7 @@
#include "../screen_capturer/dxgi_cursor_state.h"
#include "privacy_cursor_api.h"
#include "privacy_cursor_process.h"
#include "rd_log.h"
namespace crossdesk {
@@ -85,15 +86,13 @@ bool PrivacyCursorGuard::Start(std::string& error) {
startup.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
inherited, sizeof(inherited), nullptr, nullptr) != FALSE;
PROCESS_INFORMATION child{};
bool used_job_fallback = false;
if (ok) {
std::wstring command = L"\"" + helper.wstring() + L"\"";
for (HANDLE handle : inherited)
command += L" " + std::to_wstring(reinterpret_cast<uintptr_t>(handle));
ok =
CreateProcessW(helper.c_str(), command.data(), nullptr, nullptr, TRUE,
CREATE_NO_WINDOW | EXTENDED_STARTUPINFO_PRESENT |
CREATE_BREAKAWAY_FROM_JOB,
nullptr, nullptr, &startup.StartupInfo, &child) != FALSE;
ok = CreatePrivacyCursorProcess(helper.c_str(), command, startup, child,
used_job_fallback);
}
const DWORD start_error = GetLastError();
DeleteProcThreadAttributeList(startup.lpAttributeList);
@@ -102,6 +101,11 @@ bool PrivacyCursorGuard::Start(std::string& error) {
return Failed("Start crossdesk_privacy_cursor_helper.exe", error,
start_error);
}
if (used_job_fallback) {
LOG_INFO("Privacy cursor: launcher denied job breakaway; helper process={} "
"started without breakaway flag with parent-exit monitoring",
child.dwProcessId);
}
CloseHandle(child.hThread);
process_ = child.hProcess;
stop_event_ = stop.value;
@@ -0,0 +1,50 @@
/*
* @Author: DI JUNKUN
* @Date: 2026-10-08
* Copyright (c) 2026 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _PRIVACY_CURSOR_PROCESS_H_
#define _PRIVACY_CURSOR_PROCESS_H_
#include <Windows.h>
#include <string>
namespace crossdesk {
// Prefer an independent lifetime, so closing the launcher's job cannot kill
// the cursor helper before it restores the cursor. Some launchers (including
// scheduled tasks) forbid breakaway. In that case retain the inherited parent
// process handle/stop event and run in the job; parent-only exits still trigger
// cleanup, although terminating the entire job also terminates the helper.
inline bool CreatePrivacyCursorProcess(const wchar_t* executable,
const std::wstring& command,
STARTUPINFOEXW& startup,
PROCESS_INFORMATION& child,
bool& used_job_fallback) {
used_job_fallback = false;
const auto create = [&](DWORD flags) {
// CreateProcessW may modify its command line, including on failure.
auto mutable_command = command;
return CreateProcessW(executable, mutable_command.data(), nullptr, nullptr,
TRUE, CREATE_NO_WINDOW | EXTENDED_STARTUPINFO_PRESENT |
flags,
nullptr, nullptr, &startup.StartupInfo, &child) != FALSE;
};
if (create(CREATE_BREAKAWAY_FROM_JOB)) return true;
const DWORD error = GetLastError();
BOOL in_job = FALSE;
if (error != ERROR_ACCESS_DENIED ||
!IsProcessInJob(GetCurrentProcess(), nullptr, &in_job) || !in_job) {
SetLastError(error);
return false;
}
if (!create(0)) return false;
used_job_fallback = true;
return true;
}
} // namespace crossdesk
#endif // _PRIVACY_CURSOR_PROCESS_H_
@@ -932,6 +932,9 @@ int ScreenCapturerWin::Stop() {
// Drain management operations before stopping/replacing their backend.
virtual_display_.RequestCancel();
StopSecureCaptureThread();
// A recovery already in progress can publish "running" while Stop waits.
// Publish the final stopped state after that management thread has exited.
NotifyPrivacyCapture(false);
JoinVirtualDisplayThread();
ResetPostSecureDesktopState();
if (impl_) {
@@ -1238,6 +1241,7 @@ bool ScreenCapturerWin::RestartCaptureBackendAfterSecureDesktop() {
if (ret == 0) {
applied_show_cursor_ = show_cursor;
RestoreMonitor(current_monitor);
NotifyPrivacyCapture(true);
return true;
}
@@ -1253,11 +1257,13 @@ bool ScreenCapturerWin::RestartCaptureBackendAfterSecureDesktop() {
if (ret == 0) {
applied_show_cursor_ = show_cursor;
RestoreMonitor(current_monitor);
NotifyPrivacyCapture(true);
return true;
}
if (StartPreferredBackend(headless_compat_.load(std::memory_order_relaxed),
current_monitor, usbmmidd)) {
NotifyPrivacyCapture(true);
return true;
}