mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-08-07 02:29:31 +08:00
[feat] improve Windows secure desktop capture and input handling, refs #77
This commit is contained in:
+20
-3
@@ -88,9 +88,14 @@ struct WindowsServiceInteractiveStatus {
|
|||||||
};
|
};
|
||||||
|
|
||||||
constexpr uint32_t kWindowsServiceStatusIntervalMs = 1000;
|
constexpr uint32_t kWindowsServiceStatusIntervalMs = 1000;
|
||||||
constexpr DWORD kWindowsServiceQueryTimeoutMs = 100;
|
constexpr DWORD kWindowsServiceQueryTimeoutMs = 500;
|
||||||
constexpr DWORD kWindowsServiceSasTimeoutMs = 500;
|
constexpr DWORD kWindowsServiceSasTimeoutMs = 500;
|
||||||
|
|
||||||
|
bool IsTransientWindowsServiceStatusError(const std::string& error) {
|
||||||
|
return error == "pipe_unavailable" || error == "pipe_connect_failed" ||
|
||||||
|
error == "pipe_read_failed";
|
||||||
|
}
|
||||||
|
|
||||||
RemoteAction BuildWindowsServiceStatusAction(
|
RemoteAction BuildWindowsServiceStatusAction(
|
||||||
const WindowsServiceInteractiveStatus& status) {
|
const WindowsServiceInteractiveStatus& status) {
|
||||||
RemoteAction action{};
|
RemoteAction action{};
|
||||||
@@ -1938,9 +1943,16 @@ void Render::HandleWindowsServiceIntegration() {
|
|||||||
|
|
||||||
WindowsServiceInteractiveStatus status;
|
WindowsServiceInteractiveStatus status;
|
||||||
const bool status_ok = QueryWindowsServiceInteractiveStatus(&status);
|
const bool status_ok = QueryWindowsServiceInteractiveStatus(&status);
|
||||||
local_service_status_received_ = status_ok;
|
const bool previous_secure_desktop_interaction =
|
||||||
|
IsSecureDesktopInteractionRequired(local_interactive_stage_);
|
||||||
|
local_service_status_received_ =
|
||||||
|
status_ok || previous_secure_desktop_interaction;
|
||||||
local_service_available_ = status.available;
|
local_service_available_ = status.available;
|
||||||
local_interactive_stage_ = status.available ? status.interactive_stage : "";
|
if (status.available) {
|
||||||
|
local_interactive_stage_ = status.interactive_stage;
|
||||||
|
} else if (!previous_secure_desktop_interaction) {
|
||||||
|
local_interactive_stage_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
if (status_ok) {
|
if (status_ok) {
|
||||||
const bool availability_changed =
|
const bool availability_changed =
|
||||||
@@ -1953,6 +1965,11 @@ void Render::HandleWindowsServiceIntegration() {
|
|||||||
if (status.available) {
|
if (status.available) {
|
||||||
LOG_INFO(
|
LOG_INFO(
|
||||||
"Local Windows service available for secure desktop integration");
|
"Local Windows service available for secure desktop integration");
|
||||||
|
} else if (IsTransientWindowsServiceStatusError(status.error)) {
|
||||||
|
LOG_INFO(
|
||||||
|
"Local Windows service temporarily unavailable, keeping last "
|
||||||
|
"secure desktop state: error={}, code={}",
|
||||||
|
status.error, status.error_code);
|
||||||
} else {
|
} else {
|
||||||
LOG_WARN(
|
LOG_WARN(
|
||||||
"Local Windows service unavailable, secure desktop integration "
|
"Local Windows service unavailable, secure desktop integration "
|
||||||
|
|||||||
@@ -317,6 +317,22 @@ void LogSecureDesktopInputBlocked(uint32_t* last_tick, const char* side,
|
|||||||
"cannot drive the Windows password UI",
|
"cannot drive the Windows password UI",
|
||||||
side != nullptr ? side : "unknown", stage != nullptr ? stage : "");
|
side != nullptr ? side : "unknown", stage != nullptr ? stage : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsTransientSecureDesktopInputFailure(const nlohmann::json& response,
|
||||||
|
const RemoteAction& action) {
|
||||||
|
if (!response.is_object()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (response.value("error", std::string()) != "send_input_failed") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (response.value("code", 0u) != ERROR_ACCESS_DENIED) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return action.type == ControlType::keyboard &&
|
||||||
|
action.k.flag == KeyFlag::key_up;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
@@ -492,7 +508,7 @@ int Render::ProcessKeyboardEvent(const SDL_Event& event) {
|
|||||||
|
|
||||||
int Render::ProcessMouseEvent(const SDL_Event& event) {
|
int Render::ProcessMouseEvent(const SDL_Event& event) {
|
||||||
controlled_remote_id_ = "";
|
controlled_remote_id_ = "";
|
||||||
RemoteAction remote_action;
|
RemoteAction remote_action{};
|
||||||
float cursor_x = last_mouse_event.motion.x;
|
float cursor_x = last_mouse_event.motion.x;
|
||||||
float cursor_y = last_mouse_event.motion.y;
|
float cursor_y = last_mouse_event.motion.y;
|
||||||
|
|
||||||
@@ -1104,7 +1120,6 @@ void Render::OnReceiveDataBufferCb(const char* data, size_t size,
|
|||||||
// remote
|
// remote
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
if (render->local_service_status_received_ &&
|
if (render->local_service_status_received_ &&
|
||||||
render->local_service_available_ &&
|
|
||||||
IsSecureDesktopInteractionRequired(render->local_interactive_stage_)) {
|
IsSecureDesktopInteractionRequired(render->local_interactive_stage_)) {
|
||||||
if (remote_action.type == ControlType::mouse) {
|
if (remote_action.type == ControlType::mouse) {
|
||||||
int absolute_x = 0;
|
int absolute_x = 0;
|
||||||
@@ -1145,6 +1160,14 @@ void Render::OnReceiveDataBufferCb(const char* data, size_t size,
|
|||||||
remote_action.k.extended, 1000);
|
remote_action.k.extended, 1000);
|
||||||
auto json = nlohmann::json::parse(response, nullptr, false);
|
auto json = nlohmann::json::parse(response, nullptr, false);
|
||||||
if (json.is_discarded() || !json.value("ok", false)) {
|
if (json.is_discarded() || !json.value("ok", false)) {
|
||||||
|
if (!json.is_discarded() &&
|
||||||
|
IsTransientSecureDesktopInputFailure(json, remote_action)) {
|
||||||
|
LOG_INFO(
|
||||||
|
"Secure desktop keyboard injection transient failure, "
|
||||||
|
"key_code={}, is_down={}, response={}",
|
||||||
|
key_code, is_down, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
LogSecureDesktopInputBlocked(
|
LogSecureDesktopInputBlocked(
|
||||||
&render->last_local_secure_input_block_log_tick_, "local",
|
&render->last_local_secure_input_block_log_tick_, "local",
|
||||||
render->local_interactive_stage_.c_str());
|
render->local_interactive_stage_.c_str());
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ namespace {
|
|||||||
using Json = nlohmann::json;
|
using Json = nlohmann::json;
|
||||||
|
|
||||||
constexpr DWORD kSecureDesktopStatusIntervalMs = 250;
|
constexpr DWORD kSecureDesktopStatusIntervalMs = 250;
|
||||||
constexpr DWORD kSecureDesktopStatusPipeTimeoutMs = 150;
|
constexpr DWORD kSecureDesktopStatusPipeTimeoutMs = 500;
|
||||||
constexpr DWORD kSecureDesktopHelperPipeTimeoutMs = 120;
|
constexpr DWORD kSecureDesktopHelperPipeTimeoutMs = 120;
|
||||||
constexpr DWORD kSecureDesktopTransientErrorGraceMs = 1500;
|
constexpr DWORD kSecureDesktopTransientErrorGraceMs = 1500;
|
||||||
constexpr DWORD kSecureDesktopTransientErrorLogIntervalMs = 5000;
|
constexpr DWORD kSecureDesktopTransientErrorLogIntervalMs = 5000;
|
||||||
@@ -131,20 +131,28 @@ class WgcPluginCapturer final : public ScreenCapturer {
|
|||||||
};
|
};
|
||||||
|
|
||||||
std::string BuildSecureCaptureCommand(int left, int top, int width, int height,
|
std::string BuildSecureCaptureCommand(int left, int top, int width, int height,
|
||||||
bool show_cursor) {
|
bool show_cursor,
|
||||||
|
const std::string& stage) {
|
||||||
std::ostringstream stream;
|
std::ostringstream stream;
|
||||||
stream << kCrossDeskSecureInputCaptureCommandPrefix << left << ":" << top
|
stream << kCrossDeskSecureInputCaptureCommandPrefix << left << ":" << top
|
||||||
<< ":" << width << ":" << height << ":" << (show_cursor ? 1 : 0);
|
<< ":" << width << ":" << height << ":" << (show_cursor ? 1 : 0);
|
||||||
|
if (!stage.empty()) {
|
||||||
|
stream << ":" << stage;
|
||||||
|
}
|
||||||
return stream.str();
|
return stream.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string BuildSecureCaptureStartCommand(int left, int top, int width,
|
std::string BuildSecureCaptureStartCommand(int left, int top, int width,
|
||||||
int height, bool show_cursor,
|
int height, bool show_cursor,
|
||||||
int fps) {
|
int fps,
|
||||||
|
const std::string& stage) {
|
||||||
std::ostringstream stream;
|
std::ostringstream stream;
|
||||||
stream << kCrossDeskSecureInputCaptureStartCommandPrefix << left << ":" << top
|
stream << kCrossDeskSecureInputCaptureStartCommandPrefix << left << ":" << top
|
||||||
<< ":" << width << ":" << height << ":" << (show_cursor ? 1 : 0)
|
<< ":" << width << ":" << height << ":" << (show_cursor ? 1 : 0)
|
||||||
<< ":" << fps;
|
<< ":" << fps;
|
||||||
|
if (!stage.empty()) {
|
||||||
|
stream << ":" << stage;
|
||||||
|
}
|
||||||
return stream.str();
|
return stream.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,6 +168,11 @@ bool IsTransientSecureDesktopFrameError(const std::string& error_message) {
|
|||||||
error_message.find("\"error\":\"bitblt_failed\"") != std::string::npos;
|
error_message.find("\"error\":\"bitblt_failed\"") != std::string::npos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsTransientWindowsServiceStatusError(const std::string& error) {
|
||||||
|
return error == "pipe_unavailable" || error == "pipe_connect_failed" ||
|
||||||
|
error == "pipe_read_failed";
|
||||||
|
}
|
||||||
|
|
||||||
bool ReadPipeMessage(HANDLE pipe, std::vector<uint8_t>* response_out,
|
bool ReadPipeMessage(HANDLE pipe, std::vector<uint8_t>* response_out,
|
||||||
DWORD* error_code_out = nullptr) {
|
DWORD* error_code_out = nullptr) {
|
||||||
if (response_out == nullptr) {
|
if (response_out == nullptr) {
|
||||||
@@ -342,6 +355,7 @@ bool QuerySecureDesktopHelperCommand(DWORD session_id,
|
|||||||
|
|
||||||
bool QuerySecureDesktopHelperFrame(DWORD session_id, int left, int top,
|
bool QuerySecureDesktopHelperFrame(DWORD session_id, int left, int top,
|
||||||
int width, int height, bool show_cursor,
|
int width, int height, bool show_cursor,
|
||||||
|
const std::string& stage,
|
||||||
std::vector<uint8_t>* nv12_frame_out,
|
std::vector<uint8_t>* nv12_frame_out,
|
||||||
int* captured_width_out,
|
int* captured_width_out,
|
||||||
int* captured_height_out,
|
int* captured_height_out,
|
||||||
@@ -352,7 +366,7 @@ bool QuerySecureDesktopHelperFrame(DWORD session_id, int left, int top,
|
|||||||
}
|
}
|
||||||
|
|
||||||
const std::string command =
|
const std::string command =
|
||||||
BuildSecureCaptureCommand(left, top, width, height, show_cursor);
|
BuildSecureCaptureCommand(left, top, width, height, show_cursor, stage);
|
||||||
std::vector<uint8_t> response;
|
std::vector<uint8_t> response;
|
||||||
if (!QuerySecureDesktopHelperCommand(session_id, command, &response,
|
if (!QuerySecureDesktopHelperCommand(session_id, command, &response,
|
||||||
error_out)) {
|
error_out)) {
|
||||||
@@ -686,6 +700,7 @@ void ScreenCapturerWin::StopSecureDesktopSharedCapture(DWORD session_id) {
|
|||||||
secure_shared_height_ = 0;
|
secure_shared_height_ = 0;
|
||||||
secure_shared_fps_ = 0;
|
secure_shared_fps_ = 0;
|
||||||
secure_shared_show_cursor_ = true;
|
secure_shared_show_cursor_ = true;
|
||||||
|
secure_shared_stage_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ScreenCapturerWin::OpenSecureDesktopSharedFrame(DWORD session_id,
|
bool ScreenCapturerWin::OpenSecureDesktopSharedFrame(DWORD session_id,
|
||||||
@@ -815,7 +830,8 @@ bool ScreenCapturerWin::ReadSecureDesktopSharedFrame(
|
|||||||
|
|
||||||
bool ScreenCapturerWin::StartSecureDesktopSharedCapture(
|
bool ScreenCapturerWin::StartSecureDesktopSharedCapture(
|
||||||
DWORD session_id, int left, int top, int width, int height,
|
DWORD session_id, int left, int top, int width, int height,
|
||||||
bool show_cursor, int fps, std::string* error_out) {
|
const std::string& stage, bool show_cursor, int fps,
|
||||||
|
std::string* error_out) {
|
||||||
const size_t payload_size = static_cast<size_t>(width) * height * 3 / 2;
|
const size_t payload_size = static_cast<size_t>(width) * height * 3 / 2;
|
||||||
const size_t mapping_size =
|
const size_t mapping_size =
|
||||||
sizeof(CrossDeskSecureDesktopSharedFrameHeader) + payload_size;
|
sizeof(CrossDeskSecureDesktopSharedFrameHeader) + payload_size;
|
||||||
@@ -830,6 +846,7 @@ bool ScreenCapturerWin::StartSecureDesktopSharedCapture(
|
|||||||
secure_shared_session_id_ == session_id &&
|
secure_shared_session_id_ == session_id &&
|
||||||
secure_shared_left_ == left && secure_shared_top_ == top &&
|
secure_shared_left_ == left && secure_shared_top_ == top &&
|
||||||
secure_shared_width_ == width && secure_shared_height_ == height &&
|
secure_shared_width_ == width && secure_shared_height_ == height &&
|
||||||
|
secure_shared_stage_ == stage &&
|
||||||
secure_shared_show_cursor_ == show_cursor && secure_shared_fps_ == fps &&
|
secure_shared_show_cursor_ == show_cursor && secure_shared_fps_ == fps &&
|
||||||
OpenSecureDesktopSharedFrame(session_id, mapping_size, error_out)) {
|
OpenSecureDesktopSharedFrame(session_id, mapping_size, error_out)) {
|
||||||
return true;
|
return true;
|
||||||
@@ -838,7 +855,8 @@ bool ScreenCapturerWin::StartSecureDesktopSharedCapture(
|
|||||||
StopSecureDesktopSharedCapture(secure_shared_session_id_);
|
StopSecureDesktopSharedCapture(secure_shared_session_id_);
|
||||||
|
|
||||||
const std::string command =
|
const std::string command =
|
||||||
BuildSecureCaptureStartCommand(left, top, width, height, show_cursor, fps);
|
BuildSecureCaptureStartCommand(left, top, width, height, show_cursor, fps,
|
||||||
|
stage);
|
||||||
std::vector<uint8_t> response;
|
std::vector<uint8_t> response;
|
||||||
if (!QuerySecureDesktopHelperCommand(session_id, command, &response,
|
if (!QuerySecureDesktopHelperCommand(session_id, command, &response,
|
||||||
error_out)) {
|
error_out)) {
|
||||||
@@ -861,6 +879,7 @@ bool ScreenCapturerWin::StartSecureDesktopSharedCapture(
|
|||||||
secure_shared_height_ = height;
|
secure_shared_height_ = height;
|
||||||
secure_shared_show_cursor_ = show_cursor;
|
secure_shared_show_cursor_ = show_cursor;
|
||||||
secure_shared_fps_ = fps;
|
secure_shared_fps_ = fps;
|
||||||
|
secure_shared_stage_ = stage;
|
||||||
|
|
||||||
if (!OpenSecureDesktopSharedFrame(session_id, mapping_size, error_out)) {
|
if (!OpenSecureDesktopSharedFrame(session_id, mapping_size, error_out)) {
|
||||||
StopSecureDesktopSharedCapture(session_id);
|
StopSecureDesktopSharedCapture(session_id);
|
||||||
@@ -907,6 +926,11 @@ void ScreenCapturerWin::SecureDesktopCaptureLoop() {
|
|||||||
"Windows capturer secure desktop service available, polling "
|
"Windows capturer secure desktop service available, polling "
|
||||||
"session_id={}",
|
"session_id={}",
|
||||||
status.active_session_id);
|
status.active_session_id);
|
||||||
|
} else if (IsTransientWindowsServiceStatusError(status.error)) {
|
||||||
|
LOG_INFO(
|
||||||
|
"Windows capturer secure desktop service temporarily unavailable: "
|
||||||
|
"error={}, code={}",
|
||||||
|
status.error, status.error_code);
|
||||||
} else {
|
} else {
|
||||||
LOG_WARN(
|
LOG_WARN(
|
||||||
"Windows capturer secure desktop service unavailable: "
|
"Windows capturer secure desktop service unavailable: "
|
||||||
@@ -973,8 +997,9 @@ void ScreenCapturerWin::SecureDesktopCaptureLoop() {
|
|||||||
: kSecureDesktopCaptureMinFps;
|
: kSecureDesktopCaptureMinFps;
|
||||||
|
|
||||||
if (StartSecureDesktopSharedCapture(status.active_session_id, left, top,
|
if (StartSecureDesktopSharedCapture(status.active_session_id, left, top,
|
||||||
width, height, show_cursor, shared_fps,
|
width, height,
|
||||||
&error_message) &&
|
status.interactive_stage, show_cursor,
|
||||||
|
shared_fps, &error_message) &&
|
||||||
ReadSecureDesktopSharedFrame(
|
ReadSecureDesktopSharedFrame(
|
||||||
static_cast<DWORD>(frame_interval_ms + 20), &secure_frame,
|
static_cast<DWORD>(frame_interval_ms + 20), &secure_frame,
|
||||||
&captured_width, &captured_height, &error_message)) {
|
&captured_width, &captured_height, &error_message)) {
|
||||||
@@ -988,6 +1013,7 @@ void ScreenCapturerWin::SecureDesktopCaptureLoop() {
|
|||||||
if (!frame_delivered &&
|
if (!frame_delivered &&
|
||||||
QuerySecureDesktopHelperFrame(status.active_session_id, left, top,
|
QuerySecureDesktopHelperFrame(status.active_session_id, left, top,
|
||||||
width, height, show_cursor,
|
width, height, show_cursor,
|
||||||
|
status.interactive_stage,
|
||||||
&secure_frame, &captured_width,
|
&secure_frame, &captured_width,
|
||||||
&captured_height, &error_message)) {
|
&captured_height, &error_message)) {
|
||||||
if (cb_orig_ && !secure_frame.empty()) {
|
if (cb_orig_ && !secure_frame.empty()) {
|
||||||
@@ -1011,10 +1037,19 @@ void ScreenCapturerWin::SecureDesktopCaptureLoop() {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (now - last_error_tick >= log_interval) {
|
if (now - last_error_tick >= log_interval) {
|
||||||
LOG_WARN(
|
if (transient_error) {
|
||||||
"Windows capturer secure desktop frame query failed, stage='{}', "
|
LOG_INFO(
|
||||||
"session_id={}, error={}",
|
"Windows capturer secure desktop transient frame query failed, "
|
||||||
status.interactive_stage, status.active_session_id, error_message);
|
"stage='{}', session_id={}, error={}",
|
||||||
|
status.interactive_stage, status.active_session_id,
|
||||||
|
error_message);
|
||||||
|
} else {
|
||||||
|
LOG_WARN(
|
||||||
|
"Windows capturer secure desktop frame query failed, stage='{}', "
|
||||||
|
"session_id={}, error={}",
|
||||||
|
status.interactive_stage, status.active_session_id,
|
||||||
|
error_message);
|
||||||
|
}
|
||||||
last_error_tick = now;
|
last_error_tick = now;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ class ScreenCapturerWin : public ScreenCapturer {
|
|||||||
int secure_shared_height_ = 0;
|
int secure_shared_height_ = 0;
|
||||||
int secure_shared_fps_ = 0;
|
int secure_shared_fps_ = 0;
|
||||||
bool secure_shared_show_cursor_ = true;
|
bool secure_shared_show_cursor_ = true;
|
||||||
|
std::string secure_shared_stage_;
|
||||||
bool secure_shared_capture_started_ = false;
|
bool secure_shared_capture_started_ = false;
|
||||||
|
|
||||||
void BuildCanonicalFromImpl();
|
void BuildCanonicalFromImpl();
|
||||||
@@ -81,6 +82,7 @@ class ScreenCapturerWin : public ScreenCapturer {
|
|||||||
std::string* display_name);
|
std::string* display_name);
|
||||||
bool StartSecureDesktopSharedCapture(DWORD session_id, int left, int top,
|
bool StartSecureDesktopSharedCapture(DWORD session_id, int left, int top,
|
||||||
int width, int height,
|
int width, int height,
|
||||||
|
const std::string& stage,
|
||||||
bool show_cursor, int fps,
|
bool show_cursor, int fps,
|
||||||
std::string* error_out);
|
std::string* error_out);
|
||||||
void StopSecureDesktopSharedCapture(DWORD session_id);
|
void StopSecureDesktopSharedCapture(DWORD session_id);
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ namespace crossdesk {
|
|||||||
|
|
||||||
inline bool IsSecureDesktopInteractionRequired(
|
inline bool IsSecureDesktopInteractionRequired(
|
||||||
const std::string& interactive_stage) {
|
const std::string& interactive_stage) {
|
||||||
return interactive_stage == "credential-ui" ||
|
return interactive_stage == "lock-screen" ||
|
||||||
|
interactive_stage == "credential-ui" ||
|
||||||
interactive_stage == "secure-desktop";
|
interactive_stage == "secure-desktop";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,4 +39,4 @@ inline bool ShouldNormalizeUnlockToUserDesktop(
|
|||||||
|
|
||||||
} // namespace crossdesk
|
} // namespace crossdesk
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -262,8 +262,8 @@ bool GrantCrossDeskServiceStartAccessToAuthenticatedUsers(SC_HANDLE service) {
|
|||||||
std::string QueryNamedPipeMessage(const std::wstring& pipe_name,
|
std::string QueryNamedPipeMessage(const std::wstring& pipe_name,
|
||||||
const std::string& command,
|
const std::string& command,
|
||||||
DWORD timeout_ms) {
|
DWORD timeout_ms) {
|
||||||
constexpr int kPipeConnectRetryCount = 3;
|
|
||||||
constexpr DWORD kPipeConnectRetryDelayMs = 15;
|
constexpr DWORD kPipeConnectRetryDelayMs = 15;
|
||||||
|
const ULONGLONG deadline_tick = GetTickCount64() + timeout_ms;
|
||||||
|
|
||||||
auto is_transient_pipe_error = [](DWORD error) {
|
auto is_transient_pipe_error = [](DWORD error) {
|
||||||
return error == ERROR_FILE_NOT_FOUND || error == ERROR_PIPE_BUSY ||
|
return error == ERROR_FILE_NOT_FOUND || error == ERROR_PIPE_BUSY ||
|
||||||
@@ -271,12 +271,23 @@ std::string QueryNamedPipeMessage(const std::wstring& pipe_name,
|
|||||||
};
|
};
|
||||||
|
|
||||||
HANDLE pipe = INVALID_HANDLE_VALUE;
|
HANDLE pipe = INVALID_HANDLE_VALUE;
|
||||||
for (int attempt = 0; attempt < kPipeConnectRetryCount; ++attempt) {
|
DWORD last_error = ERROR_SEM_TIMEOUT;
|
||||||
if (!WaitNamedPipeW(pipe_name.c_str(), timeout_ms)) {
|
while (GetTickCount64() <= deadline_tick) {
|
||||||
|
const ULONGLONG now = GetTickCount64();
|
||||||
|
const DWORD wait_timeout =
|
||||||
|
deadline_tick > now
|
||||||
|
? static_cast<DWORD>((std::min)(
|
||||||
|
deadline_tick - now, static_cast<ULONGLONG>(MAXDWORD)))
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
if (!WaitNamedPipeW(pipe_name.c_str(), wait_timeout)) {
|
||||||
const DWORD error = GetLastError();
|
const DWORD error = GetLastError();
|
||||||
if (attempt + 1 < kPipeConnectRetryCount &&
|
last_error = error;
|
||||||
is_transient_pipe_error(error)) {
|
const ULONGLONG retry_tick = GetTickCount64();
|
||||||
Sleep(kPipeConnectRetryDelayMs);
|
if (is_transient_pipe_error(error) && retry_tick < deadline_tick) {
|
||||||
|
Sleep(static_cast<DWORD>((std::min)(
|
||||||
|
static_cast<ULONGLONG>(kPipeConnectRetryDelayMs),
|
||||||
|
deadline_tick - retry_tick)));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
return BuildErrorJson("pipe_unavailable", error);
|
return BuildErrorJson("pipe_unavailable", error);
|
||||||
@@ -289,14 +300,21 @@ std::string QueryNamedPipeMessage(const std::wstring& pipe_name,
|
|||||||
}
|
}
|
||||||
|
|
||||||
const DWORD error = GetLastError();
|
const DWORD error = GetLastError();
|
||||||
if (attempt + 1 < kPipeConnectRetryCount &&
|
last_error = error;
|
||||||
is_transient_pipe_error(error)) {
|
const ULONGLONG retry_tick = GetTickCount64();
|
||||||
Sleep(kPipeConnectRetryDelayMs);
|
if (is_transient_pipe_error(error) && retry_tick < deadline_tick) {
|
||||||
|
Sleep(static_cast<DWORD>((std::min)(
|
||||||
|
static_cast<ULONGLONG>(kPipeConnectRetryDelayMs),
|
||||||
|
deadline_tick - retry_tick)));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
return BuildErrorJson("pipe_connect_failed", error);
|
return BuildErrorJson("pipe_connect_failed", error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pipe == INVALID_HANDLE_VALUE) {
|
||||||
|
return BuildErrorJson("pipe_unavailable", last_error);
|
||||||
|
}
|
||||||
|
|
||||||
DWORD pipe_mode = PIPE_READMODE_MESSAGE;
|
DWORD pipe_mode = PIPE_READMODE_MESSAGE;
|
||||||
SetNamedPipeHandleState(pipe, &pipe_mode, nullptr, nullptr);
|
SetNamedPipeHandleState(pipe, &pipe_mode, nullptr, nullptr);
|
||||||
|
|
||||||
@@ -337,20 +355,27 @@ std::string BuildSecureDesktopMouseIpcCommand(int x, int y, int wheel,
|
|||||||
return stream.str();
|
return stream.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string BuildSecureInputHelperKeyboardCommand(int key_code, bool is_down,
|
std::string BuildSecureInputHelperKeyboardCommand(
|
||||||
uint32_t scan_code,
|
int key_code, bool is_down, uint32_t scan_code, bool extended,
|
||||||
bool extended) {
|
const std::string& interactive_stage) {
|
||||||
std::ostringstream stream;
|
std::ostringstream stream;
|
||||||
stream << kCrossDeskSecureInputKeyboardCommandPrefix << key_code << ":"
|
stream << kCrossDeskSecureInputKeyboardCommandPrefix << key_code << ":"
|
||||||
<< (is_down ? 1 : 0) << ":" << scan_code << ":" << (extended ? 1 : 0);
|
<< (is_down ? 1 : 0) << ":" << scan_code << ":" << (extended ? 1 : 0);
|
||||||
|
if (!interactive_stage.empty()) {
|
||||||
|
stream << ":" << interactive_stage;
|
||||||
|
}
|
||||||
return stream.str();
|
return stream.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string BuildSecureInputHelperMouseCommand(int x, int y, int wheel,
|
std::string BuildSecureInputHelperMouseCommand(
|
||||||
int flag) {
|
int x, int y, int wheel, int flag,
|
||||||
|
const std::string& interactive_stage) {
|
||||||
std::ostringstream stream;
|
std::ostringstream stream;
|
||||||
stream << kCrossDeskSecureInputMouseCommandPrefix << x << ":" << y << ":"
|
stream << kCrossDeskSecureInputMouseCommandPrefix << x << ":" << y << ":"
|
||||||
<< wheel << ":" << flag;
|
<< wheel << ":" << flag;
|
||||||
|
if (!interactive_stage.empty()) {
|
||||||
|
stream << ":" << interactive_stage;
|
||||||
|
}
|
||||||
return stream.str();
|
return stream.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -565,6 +590,15 @@ const char* DetermineInteractiveStage(bool lock_app_visible,
|
|||||||
return "user-desktop";
|
return "user-desktop";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::wstring SecureInputHelperDesktopForStage(
|
||||||
|
const std::string& interactive_stage) {
|
||||||
|
if (interactive_stage == "credential-ui" ||
|
||||||
|
interactive_stage == "secure-desktop") {
|
||||||
|
return L"winsta0\\Winlogon";
|
||||||
|
}
|
||||||
|
return L"winsta0\\default";
|
||||||
|
}
|
||||||
|
|
||||||
bool GetSessionUserName(DWORD session_id, std::wstring* username_out) {
|
bool GetSessionUserName(DWORD session_id, std::wstring* username_out) {
|
||||||
if (username_out == nullptr) {
|
if (username_out == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
@@ -1010,6 +1044,7 @@ int CrossDeskServiceHost::InitializeRuntime() {
|
|||||||
session_helper_report_input_desktop_.clear();
|
session_helper_report_input_desktop_.clear();
|
||||||
session_helper_report_interactive_stage_.clear();
|
session_helper_report_interactive_stage_.clear();
|
||||||
secure_input_helper_last_error_.clear();
|
secure_input_helper_last_error_.clear();
|
||||||
|
secure_input_helper_interactive_stage_.clear();
|
||||||
last_session_event_type_ = 0;
|
last_session_event_type_ = 0;
|
||||||
last_session_event_session_id_ = active_session_id_;
|
last_session_event_session_id_ = active_session_id_;
|
||||||
RefreshSessionState();
|
RefreshSessionState();
|
||||||
@@ -1303,6 +1338,17 @@ bool CrossDeskServiceHost::ShouldKeepSecureInputHelperLocked(
|
|||||||
IsHelperReportingLockScreenLocked());
|
IsHelperReportingLockScreenLocked());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string CrossDeskServiceHost::ResolveInteractiveStageLocked() const {
|
||||||
|
if (!session_helper_report_interactive_stage_.empty()) {
|
||||||
|
return session_helper_report_interactive_stage_;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DetermineInteractiveStage(
|
||||||
|
IsHelperReportingLockScreenLocked(),
|
||||||
|
session_helper_report_credential_ui_visible_ || logon_ui_visible_,
|
||||||
|
session_helper_report_secure_desktop_active_ || secure_desktop_active_);
|
||||||
|
}
|
||||||
|
|
||||||
std::wstring CrossDeskServiceHost::GetSessionHelperPath() const {
|
std::wstring CrossDeskServiceHost::GetSessionHelperPath() const {
|
||||||
std::wstring current_executable = GetCurrentExecutablePathW();
|
std::wstring current_executable = GetCurrentExecutablePathW();
|
||||||
if (current_executable.empty()) {
|
if (current_executable.empty()) {
|
||||||
@@ -1392,6 +1438,7 @@ void CrossDeskServiceHost::ReapSecureInputHelper() {
|
|||||||
secure_input_helper_process_id_ = 0;
|
secure_input_helper_process_id_ = 0;
|
||||||
secure_input_helper_exit_code_ = exit_code;
|
secure_input_helper_exit_code_ = exit_code;
|
||||||
secure_input_helper_started_at_tick_ = 0;
|
secure_input_helper_started_at_tick_ = 0;
|
||||||
|
secure_input_helper_interactive_stage_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process_handle != nullptr) {
|
if (process_handle != nullptr) {
|
||||||
@@ -1450,6 +1497,7 @@ void CrossDeskServiceHost::StopSecureInputHelper() {
|
|||||||
secure_input_helper_running_ = false;
|
secure_input_helper_running_ = false;
|
||||||
secure_input_helper_process_id_ = 0;
|
secure_input_helper_process_id_ = 0;
|
||||||
secure_input_helper_started_at_tick_ = 0;
|
secure_input_helper_started_at_tick_ = 0;
|
||||||
|
secure_input_helper_interactive_stage_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stop_event_handle != nullptr) {
|
if (stop_event_handle != nullptr) {
|
||||||
@@ -1577,7 +1625,8 @@ bool CrossDeskServiceHost::LaunchSessionHelper(DWORD session_id) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CrossDeskServiceHost::LaunchSecureInputHelper(DWORD session_id) {
|
bool CrossDeskServiceHost::LaunchSecureInputHelper(
|
||||||
|
DWORD session_id, const std::string& interactive_stage) {
|
||||||
std::wstring helper_path = GetSecureInputHelperPath();
|
std::wstring helper_path = GetSecureInputHelperPath();
|
||||||
if (helper_path.empty() || !std::filesystem::exists(helper_path)) {
|
if (helper_path.empty() || !std::filesystem::exists(helper_path)) {
|
||||||
std::lock_guard<std::mutex> lock(state_mutex_);
|
std::lock_guard<std::mutex> lock(state_mutex_);
|
||||||
@@ -1611,7 +1660,10 @@ bool CrossDeskServiceHost::LaunchSecureInputHelper(DWORD session_id) {
|
|||||||
|
|
||||||
STARTUPINFOW startup_info{};
|
STARTUPINFOW startup_info{};
|
||||||
startup_info.cb = sizeof(startup_info);
|
startup_info.cb = sizeof(startup_info);
|
||||||
startup_info.lpDesktop = const_cast<LPWSTR>(L"winsta0\\Winlogon");
|
std::wstring secure_input_helper_desktop =
|
||||||
|
SecureInputHelperDesktopForStage(interactive_stage);
|
||||||
|
startup_info.lpDesktop =
|
||||||
|
const_cast<LPWSTR>(secure_input_helper_desktop.c_str());
|
||||||
PROCESS_INFORMATION process_info{};
|
PROCESS_INFORMATION process_info{};
|
||||||
BOOL created = FALSE;
|
BOOL created = FALSE;
|
||||||
|
|
||||||
@@ -1660,10 +1712,14 @@ bool CrossDeskServiceHost::LaunchSecureInputHelper(DWORD session_id) {
|
|||||||
secure_input_helper_last_error_.clear();
|
secure_input_helper_last_error_.clear();
|
||||||
secure_input_helper_running_ = true;
|
secure_input_helper_running_ = true;
|
||||||
secure_input_helper_started_at_tick_ = GetTickCount64();
|
secure_input_helper_started_at_tick_ = GetTickCount64();
|
||||||
|
secure_input_helper_interactive_stage_ = interactive_stage;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_INFO("Secure input helper started: session_id={}, pid={}", session_id,
|
LOG_INFO(
|
||||||
process_info.dwProcessId);
|
"Secure input helper started: session_id={}, pid={}, stage='{}', "
|
||||||
|
"desktop='{}'",
|
||||||
|
session_id, process_info.dwProcessId, interactive_stage,
|
||||||
|
WideToUtf8(secure_input_helper_desktop));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1845,21 +1901,26 @@ std::string CrossDeskServiceHost::BuildStatusResponse() {
|
|||||||
bool keep_secure_input_helper = false;
|
bool keep_secure_input_helper = false;
|
||||||
bool launch_secure_input_helper = false;
|
bool launch_secure_input_helper = false;
|
||||||
DWORD secure_input_target_session_id = 0xFFFFFFFF;
|
DWORD secure_input_target_session_id = 0xFFFFFFFF;
|
||||||
|
std::string secure_input_interactive_stage;
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(state_mutex_);
|
std::lock_guard<std::mutex> lock(state_mutex_);
|
||||||
secure_input_target_session_id = active_session_id_;
|
secure_input_target_session_id = active_session_id_;
|
||||||
|
secure_input_interactive_stage = ResolveInteractiveStageLocked();
|
||||||
keep_secure_input_helper =
|
keep_secure_input_helper =
|
||||||
ShouldKeepSecureInputHelperLocked(secure_input_target_session_id);
|
ShouldKeepSecureInputHelperLocked(secure_input_target_session_id);
|
||||||
launch_secure_input_helper =
|
launch_secure_input_helper =
|
||||||
keep_secure_input_helper &&
|
keep_secure_input_helper &&
|
||||||
(!secure_input_helper_running_ ||
|
(!secure_input_helper_running_ ||
|
||||||
secure_input_helper_session_id_ != secure_input_target_session_id);
|
secure_input_helper_session_id_ != secure_input_target_session_id ||
|
||||||
|
secure_input_helper_interactive_stage_ !=
|
||||||
|
secure_input_interactive_stage);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keep_secure_input_helper) {
|
if (keep_secure_input_helper) {
|
||||||
if (launch_secure_input_helper) {
|
if (launch_secure_input_helper) {
|
||||||
StopSecureInputHelper();
|
StopSecureInputHelper();
|
||||||
LaunchSecureInputHelper(secure_input_target_session_id);
|
LaunchSecureInputHelper(secure_input_target_session_id,
|
||||||
|
secure_input_interactive_stage);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
StopSecureInputHelper();
|
StopSecureInputHelper();
|
||||||
@@ -1883,6 +1944,8 @@ std::string CrossDeskServiceHost::BuildStatusResponse() {
|
|||||||
EscapeJsonString(session_helper_report_input_desktop_);
|
EscapeJsonString(session_helper_report_input_desktop_);
|
||||||
std::string secure_input_helper_last_error =
|
std::string secure_input_helper_last_error =
|
||||||
EscapeJsonString(secure_input_helper_last_error_);
|
EscapeJsonString(secure_input_helper_last_error_);
|
||||||
|
std::string secure_input_helper_interactive_stage =
|
||||||
|
EscapeJsonString(secure_input_helper_interactive_stage_);
|
||||||
bool interactive_state_ready = session_helper_status_ok_;
|
bool interactive_state_ready = session_helper_status_ok_;
|
||||||
const char* interactive_state_source =
|
const char* interactive_state_source =
|
||||||
interactive_state_ready ? "session-helper" : "service-host";
|
interactive_state_ready ? "session-helper" : "service-host";
|
||||||
@@ -1909,9 +1972,10 @@ std::string CrossDeskServiceHost::BuildStatusResponse() {
|
|||||||
std::string interactive_input_desktop = EscapeJsonString(
|
std::string interactive_input_desktop = EscapeJsonString(
|
||||||
interactive_state_ready ? session_helper_report_input_desktop_
|
interactive_state_ready ? session_helper_report_input_desktop_
|
||||||
: input_desktop_name_);
|
: input_desktop_name_);
|
||||||
std::string interactive_stage = EscapeJsonString(DetermineInteractiveStage(
|
std::string raw_interactive_stage = DetermineInteractiveStage(
|
||||||
interactive_lock_screen_visible, credential_ui_visible,
|
interactive_lock_screen_visible, credential_ui_visible,
|
||||||
interactive_secure_desktop_active));
|
interactive_secure_desktop_active);
|
||||||
|
std::string interactive_stage = EscapeJsonString(raw_interactive_stage);
|
||||||
std::ostringstream stream;
|
std::ostringstream stream;
|
||||||
stream << "{\"ok\":true,\"service\":\"CrossDeskService\""
|
stream << "{\"ok\":true,\"service\":\"CrossDeskService\""
|
||||||
<< ",\"active_session_id\":" << active_session_id_
|
<< ",\"active_session_id\":" << active_session_id_
|
||||||
@@ -2005,6 +2069,8 @@ std::string CrossDeskServiceHost::BuildStatusResponse() {
|
|||||||
<< secure_input_helper_last_error << "\""
|
<< secure_input_helper_last_error << "\""
|
||||||
<< ",\"secure_input_helper_last_error_code\":"
|
<< ",\"secure_input_helper_last_error_code\":"
|
||||||
<< secure_input_helper_last_error_code_
|
<< secure_input_helper_last_error_code_
|
||||||
|
<< ",\"secure_input_helper_stage\":\""
|
||||||
|
<< secure_input_helper_interactive_stage << "\""
|
||||||
<< ",\"secure_input_helper_uptime_ms\":"
|
<< ",\"secure_input_helper_uptime_ms\":"
|
||||||
<< (secure_input_helper_started_at_tick_ >= started_at_tick_
|
<< (secure_input_helper_started_at_tick_ >= started_at_tick_
|
||||||
? (GetTickCount64() - secure_input_helper_started_at_tick_)
|
? (GetTickCount64() - secure_input_helper_started_at_tick_)
|
||||||
@@ -2051,15 +2117,21 @@ std::string CrossDeskServiceHost::SendSecureDesktopKeyboardInput(
|
|||||||
RefreshSessionState();
|
RefreshSessionState();
|
||||||
ReapSecureInputHelper();
|
ReapSecureInputHelper();
|
||||||
EnsureSessionHelper();
|
EnsureSessionHelper();
|
||||||
|
RefreshSessionHelperReportedState();
|
||||||
|
|
||||||
DWORD target_session_id = 0xFFFFFFFF;
|
DWORD target_session_id = 0xFFFFFFFF;
|
||||||
bool helper_running = false;
|
bool helper_running = false;
|
||||||
bool can_inject = false;
|
bool can_inject = false;
|
||||||
|
std::string interactive_stage;
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(state_mutex_);
|
std::lock_guard<std::mutex> lock(state_mutex_);
|
||||||
target_session_id = active_session_id_;
|
target_session_id = active_session_id_;
|
||||||
|
interactive_stage = ResolveInteractiveStageLocked();
|
||||||
|
const bool helper_stage_matches =
|
||||||
|
secure_input_helper_interactive_stage_ == interactive_stage;
|
||||||
helper_running = secure_input_helper_running_ &&
|
helper_running = secure_input_helper_running_ &&
|
||||||
secure_input_helper_session_id_ == target_session_id;
|
secure_input_helper_session_id_ == target_session_id &&
|
||||||
|
helper_stage_matches;
|
||||||
can_inject = GetEffectiveSessionLockedLocked() || HasSecureInputUiLocked();
|
can_inject = GetEffectiveSessionLockedLocked() || HasSecureInputUiLocked();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2072,7 +2144,7 @@ std::string CrossDeskServiceHost::SendSecureDesktopKeyboardInput(
|
|||||||
|
|
||||||
if (!helper_running) {
|
if (!helper_running) {
|
||||||
StopSecureInputHelper();
|
StopSecureInputHelper();
|
||||||
if (!LaunchSecureInputHelper(target_session_id)) {
|
if (!LaunchSecureInputHelper(target_session_id, interactive_stage)) {
|
||||||
std::lock_guard<std::mutex> lock(state_mutex_);
|
std::lock_guard<std::mutex> lock(state_mutex_);
|
||||||
return BuildErrorJson(secure_input_helper_last_error_.c_str(),
|
return BuildErrorJson(secure_input_helper_last_error_.c_str(),
|
||||||
secure_input_helper_last_error_code_);
|
secure_input_helper_last_error_code_);
|
||||||
@@ -2082,7 +2154,7 @@ std::string CrossDeskServiceHost::SendSecureDesktopKeyboardInput(
|
|||||||
return QueryNamedPipeMessage(
|
return QueryNamedPipeMessage(
|
||||||
GetCrossDeskSecureInputHelperPipeName(target_session_id),
|
GetCrossDeskSecureInputHelperPipeName(target_session_id),
|
||||||
BuildSecureInputHelperKeyboardCommand(key_code, is_down, scan_code,
|
BuildSecureInputHelperKeyboardCommand(key_code, is_down, scan_code,
|
||||||
extended),
|
extended, interactive_stage),
|
||||||
1000);
|
1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2092,15 +2164,21 @@ std::string CrossDeskServiceHost::SendSecureDesktopMouseInput(int x, int y,
|
|||||||
RefreshSessionState();
|
RefreshSessionState();
|
||||||
ReapSecureInputHelper();
|
ReapSecureInputHelper();
|
||||||
EnsureSessionHelper();
|
EnsureSessionHelper();
|
||||||
|
RefreshSessionHelperReportedState();
|
||||||
|
|
||||||
DWORD target_session_id = 0xFFFFFFFF;
|
DWORD target_session_id = 0xFFFFFFFF;
|
||||||
bool helper_running = false;
|
bool helper_running = false;
|
||||||
bool can_inject = false;
|
bool can_inject = false;
|
||||||
|
std::string interactive_stage;
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(state_mutex_);
|
std::lock_guard<std::mutex> lock(state_mutex_);
|
||||||
target_session_id = active_session_id_;
|
target_session_id = active_session_id_;
|
||||||
|
interactive_stage = ResolveInteractiveStageLocked();
|
||||||
|
const bool helper_stage_matches =
|
||||||
|
secure_input_helper_interactive_stage_ == interactive_stage;
|
||||||
helper_running = secure_input_helper_running_ &&
|
helper_running = secure_input_helper_running_ &&
|
||||||
secure_input_helper_session_id_ == target_session_id;
|
secure_input_helper_session_id_ == target_session_id &&
|
||||||
|
helper_stage_matches;
|
||||||
can_inject = GetEffectiveSessionLockedLocked() || HasSecureInputUiLocked();
|
can_inject = GetEffectiveSessionLockedLocked() || HasSecureInputUiLocked();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2113,7 +2191,7 @@ std::string CrossDeskServiceHost::SendSecureDesktopMouseInput(int x, int y,
|
|||||||
|
|
||||||
if (!helper_running) {
|
if (!helper_running) {
|
||||||
StopSecureInputHelper();
|
StopSecureInputHelper();
|
||||||
if (!LaunchSecureInputHelper(target_session_id)) {
|
if (!LaunchSecureInputHelper(target_session_id, interactive_stage)) {
|
||||||
std::lock_guard<std::mutex> lock(state_mutex_);
|
std::lock_guard<std::mutex> lock(state_mutex_);
|
||||||
return BuildErrorJson(secure_input_helper_last_error_.c_str(),
|
return BuildErrorJson(secure_input_helper_last_error_.c_str(),
|
||||||
secure_input_helper_last_error_code_);
|
secure_input_helper_last_error_code_);
|
||||||
@@ -2122,7 +2200,8 @@ std::string CrossDeskServiceHost::SendSecureDesktopMouseInput(int x, int y,
|
|||||||
|
|
||||||
return QueryNamedPipeMessage(
|
return QueryNamedPipeMessage(
|
||||||
GetCrossDeskSecureInputHelperPipeName(target_session_id),
|
GetCrossDeskSecureInputHelperPipeName(target_session_id),
|
||||||
BuildSecureInputHelperMouseCommand(x, y, wheel, flag), 1000);
|
BuildSecureInputHelperMouseCommand(x, y, wheel, flag, interactive_stage),
|
||||||
|
1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InstallCrossDeskService(const std::wstring& binary_path) {
|
bool InstallCrossDeskService(const std::wstring& binary_path) {
|
||||||
|
|||||||
@@ -45,7 +45,8 @@ class CrossDeskServiceHost {
|
|||||||
bool LaunchSessionHelper(DWORD session_id);
|
bool LaunchSessionHelper(DWORD session_id);
|
||||||
void ReapSecureInputHelper();
|
void ReapSecureInputHelper();
|
||||||
void StopSecureInputHelper();
|
void StopSecureInputHelper();
|
||||||
bool LaunchSecureInputHelper(DWORD session_id);
|
bool LaunchSecureInputHelper(DWORD session_id,
|
||||||
|
const std::string& interactive_stage);
|
||||||
std::wstring GetSessionHelperPath() const;
|
std::wstring GetSessionHelperPath() const;
|
||||||
std::wstring GetSessionHelperStopEventName(DWORD session_id) const;
|
std::wstring GetSessionHelperStopEventName(DWORD session_id) const;
|
||||||
std::wstring GetSecureInputHelperPath() const;
|
std::wstring GetSecureInputHelperPath() const;
|
||||||
@@ -56,6 +57,7 @@ class CrossDeskServiceHost {
|
|||||||
bool IsHelperReportingLockScreenLocked() const;
|
bool IsHelperReportingLockScreenLocked() const;
|
||||||
bool HasSecureInputUiLocked() const;
|
bool HasSecureInputUiLocked() const;
|
||||||
bool ShouldKeepSecureInputHelperLocked(DWORD target_session_id) const;
|
bool ShouldKeepSecureInputHelperLocked(DWORD target_session_id) const;
|
||||||
|
std::string ResolveInteractiveStageLocked() const;
|
||||||
void RefreshSessionHelperReportedState();
|
void RefreshSessionHelperReportedState();
|
||||||
void RecordSessionEvent(DWORD event_type, DWORD session_id);
|
void RecordSessionEvent(DWORD event_type, DWORD session_id);
|
||||||
std::string HandleIpcCommand(const std::string& command);
|
std::string HandleIpcCommand(const std::string& command);
|
||||||
@@ -130,6 +132,7 @@ class CrossDeskServiceHost {
|
|||||||
std::string session_helper_report_input_desktop_;
|
std::string session_helper_report_input_desktop_;
|
||||||
std::string session_helper_report_interactive_stage_;
|
std::string session_helper_report_interactive_stage_;
|
||||||
std::string secure_input_helper_last_error_;
|
std::string secure_input_helper_last_error_;
|
||||||
|
std::string secure_input_helper_interactive_stage_;
|
||||||
|
|
||||||
static CrossDeskServiceHost* instance_;
|
static CrossDeskServiceHost* instance_;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "path_manager.h"
|
#include "path_manager.h"
|
||||||
@@ -58,6 +59,7 @@ struct SecureCaptureRequest {
|
|||||||
int height = 0;
|
int height = 0;
|
||||||
bool show_cursor = true;
|
bool show_cursor = true;
|
||||||
int fps = 30;
|
int fps = 30;
|
||||||
|
std::string interactive_stage;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SecureMouseRequest {
|
struct SecureMouseRequest {
|
||||||
@@ -65,6 +67,7 @@ struct SecureMouseRequest {
|
|||||||
int y = 0;
|
int y = 0;
|
||||||
int wheel = 0;
|
int wheel = 0;
|
||||||
int flag = 0;
|
int flag = 0;
|
||||||
|
std::string interactive_stage;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SecureCaptureBuffers {
|
struct SecureCaptureBuffers {
|
||||||
@@ -126,6 +129,11 @@ void InitializeHelperLogger() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EnablePerMonitorDpiAwareness() {
|
||||||
|
SetProcessDpiAwarenessContext(
|
||||||
|
DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
|
||||||
|
}
|
||||||
|
|
||||||
std::wstring Utf8ToWide(const std::string& value) {
|
std::wstring Utf8ToWide(const std::string& value) {
|
||||||
if (value.empty()) {
|
if (value.empty()) {
|
||||||
return {};
|
return {};
|
||||||
@@ -447,8 +455,7 @@ void HelperIpcServerLoop(HANDLE stop_event, DWORD session_id,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::wstring GetCurrentThreadDesktopNameW() {
|
std::wstring GetDesktopNameW(HDESK desktop) {
|
||||||
HDESK desktop = GetThreadDesktop(GetCurrentThreadId());
|
|
||||||
if (desktop == nullptr) {
|
if (desktop == nullptr) {
|
||||||
return L"";
|
return L"";
|
||||||
}
|
}
|
||||||
@@ -471,6 +478,14 @@ std::wstring GetCurrentThreadDesktopNameW() {
|
|||||||
return desktop_name;
|
return desktop_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::wstring GetCurrentThreadDesktopNameW() {
|
||||||
|
return GetDesktopNameW(GetThreadDesktop(GetCurrentThreadId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr ACCESS_MASK kCrossDeskInteractiveDesktopAccess =
|
||||||
|
DESKTOP_CREATEWINDOW | DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS |
|
||||||
|
DESKTOP_SWITCHDESKTOP;
|
||||||
|
|
||||||
bool EnsureThreadDesktop(const wchar_t* desktop_name,
|
bool EnsureThreadDesktop(const wchar_t* desktop_name,
|
||||||
HDESK* opened_desktop_out = nullptr) {
|
HDESK* opened_desktop_out = nullptr) {
|
||||||
if (desktop_name == nullptr) {
|
if (desktop_name == nullptr) {
|
||||||
@@ -483,9 +498,8 @@ bool EnsureThreadDesktop(const wchar_t* desktop_name,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
HDESK desktop = OpenDesktopW(desktop_name, 0, FALSE,
|
HDESK desktop =
|
||||||
DESKTOP_CREATEWINDOW | DESKTOP_WRITEOBJECTS |
|
OpenDesktopW(desktop_name, 0, FALSE, kCrossDeskInteractiveDesktopAccess);
|
||||||
DESKTOP_READOBJECTS | DESKTOP_SWITCHDESKTOP);
|
|
||||||
if (desktop == nullptr) {
|
if (desktop == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -503,6 +517,196 @@ bool EnsureThreadDesktop(const wchar_t* desktop_name,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool EnsureThreadInputDesktop(HDESK* opened_desktop_out = nullptr) {
|
||||||
|
HDESK desktop =
|
||||||
|
OpenInputDesktop(0, FALSE, kCrossDeskInteractiveDesktopAccess);
|
||||||
|
if (desktop == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::wstring input_desktop = GetDesktopNameW(desktop);
|
||||||
|
const std::wstring current_desktop = GetCurrentThreadDesktopNameW();
|
||||||
|
if (!input_desktop.empty() && !current_desktop.empty() &&
|
||||||
|
_wcsicmp(input_desktop.c_str(), current_desktop.c_str()) == 0) {
|
||||||
|
if (opened_desktop_out != nullptr) {
|
||||||
|
*opened_desktop_out = desktop;
|
||||||
|
} else {
|
||||||
|
CloseDesktop(desktop);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SetThreadDesktop(desktop)) {
|
||||||
|
CloseDesktop(desktop);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opened_desktop_out != nullptr) {
|
||||||
|
*opened_desktop_out = desktop;
|
||||||
|
} else {
|
||||||
|
CloseDesktop(desktop);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EnsureThreadInteractiveDesktop(HDESK* opened_desktop_out = nullptr) {
|
||||||
|
if (opened_desktop_out != nullptr) {
|
||||||
|
*opened_desktop_out = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EnsureThreadInputDesktop(opened_desktop_out)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const DWORD input_desktop_error = GetLastError();
|
||||||
|
|
||||||
|
if (EnsureThreadDesktop(L"Winlogon", opened_desktop_out)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const DWORD winlogon_error = GetLastError();
|
||||||
|
|
||||||
|
LOG_WARN(
|
||||||
|
"Failed to switch secure input helper desktop, input_error={}, "
|
||||||
|
"winlogon_error={}, current='{}'",
|
||||||
|
input_desktop_error, winlogon_error,
|
||||||
|
WideToUtf8(GetCurrentThreadDesktopNameW()));
|
||||||
|
SetLastError(winlogon_error != ERROR_SUCCESS ? winlogon_error
|
||||||
|
: input_desktop_error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const wchar_t* DesktopNameForInteractiveStage(
|
||||||
|
const std::string& interactive_stage) {
|
||||||
|
if (interactive_stage == "credential-ui" ||
|
||||||
|
interactive_stage == "secure-desktop") {
|
||||||
|
return L"Winlogon";
|
||||||
|
}
|
||||||
|
if (interactive_stage == "lock-screen") {
|
||||||
|
return L"Default";
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DesktopSwitchDetails {
|
||||||
|
std::string stage;
|
||||||
|
std::string target_desktop;
|
||||||
|
std::string current_desktop;
|
||||||
|
DWORD error_code = ERROR_SUCCESS;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct InputInjectionResult {
|
||||||
|
bool ok = true;
|
||||||
|
std::string error;
|
||||||
|
DWORD error_code = ERROR_SUCCESS;
|
||||||
|
DesktopSwitchDetails desktop;
|
||||||
|
};
|
||||||
|
|
||||||
|
DesktopSwitchDetails BuildDesktopSwitchDetails(
|
||||||
|
const std::string& interactive_stage) {
|
||||||
|
DesktopSwitchDetails details;
|
||||||
|
details.stage = interactive_stage;
|
||||||
|
const wchar_t* desktop_name =
|
||||||
|
DesktopNameForInteractiveStage(interactive_stage);
|
||||||
|
details.target_desktop =
|
||||||
|
desktop_name != nullptr ? WideToUtf8(std::wstring(desktop_name))
|
||||||
|
: "input-or-Winlogon";
|
||||||
|
details.current_desktop = WideToUtf8(GetCurrentThreadDesktopNameW());
|
||||||
|
return details;
|
||||||
|
}
|
||||||
|
|
||||||
|
InputInjectionResult BuildInputSuccess() {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
InputInjectionResult BuildInputFailure(const char* error, DWORD error_code,
|
||||||
|
DesktopSwitchDetails desktop) {
|
||||||
|
desktop.error_code = error_code;
|
||||||
|
desktop.current_desktop = WideToUtf8(GetCurrentThreadDesktopNameW());
|
||||||
|
|
||||||
|
InputInjectionResult result;
|
||||||
|
result.ok = false;
|
||||||
|
result.error = error != nullptr ? error : "input_failed";
|
||||||
|
result.error_code =
|
||||||
|
error_code != ERROR_SUCCESS ? error_code : ERROR_GEN_FAILURE;
|
||||||
|
result.desktop = std::move(desktop);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Json BuildInputFailureJson(const InputInjectionResult& result) {
|
||||||
|
Json json;
|
||||||
|
json["ok"] = false;
|
||||||
|
json["error"] = result.error;
|
||||||
|
json["code"] = result.error_code;
|
||||||
|
json["stage"] = result.desktop.stage;
|
||||||
|
json["target_desktop"] = result.desktop.target_desktop;
|
||||||
|
json["current_desktop"] = result.desktop.current_desktop;
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EnsureThreadInteractiveDesktopForStage(
|
||||||
|
const std::string& interactive_stage,
|
||||||
|
HDESK* opened_desktop_out = nullptr,
|
||||||
|
DesktopSwitchDetails* switch_details = nullptr) {
|
||||||
|
if (opened_desktop_out != nullptr) {
|
||||||
|
*opened_desktop_out = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
DesktopSwitchDetails local_details =
|
||||||
|
BuildDesktopSwitchDetails(interactive_stage);
|
||||||
|
if (switch_details != nullptr) {
|
||||||
|
*switch_details = local_details;
|
||||||
|
}
|
||||||
|
|
||||||
|
const wchar_t* desktop_name =
|
||||||
|
DesktopNameForInteractiveStage(interactive_stage);
|
||||||
|
if (desktop_name != nullptr) {
|
||||||
|
if (EnsureThreadDesktop(desktop_name, opened_desktop_out)) {
|
||||||
|
if (switch_details != nullptr) {
|
||||||
|
switch_details->current_desktop =
|
||||||
|
WideToUtf8(GetCurrentThreadDesktopNameW());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DWORD error = GetLastError();
|
||||||
|
if (switch_details != nullptr) {
|
||||||
|
switch_details->error_code = error;
|
||||||
|
switch_details->current_desktop =
|
||||||
|
WideToUtf8(GetCurrentThreadDesktopNameW());
|
||||||
|
}
|
||||||
|
LOG_WARN(
|
||||||
|
"Failed to switch secure input helper to stage desktop, stage='{}', "
|
||||||
|
"desktop='{}', error={}, current='{}'",
|
||||||
|
interactive_stage, WideToUtf8(std::wstring(desktop_name)),
|
||||||
|
error,
|
||||||
|
WideToUtf8(GetCurrentThreadDesktopNameW()));
|
||||||
|
SetLastError(error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EnsureThreadInteractiveDesktop(opened_desktop_out)) {
|
||||||
|
if (switch_details != nullptr) {
|
||||||
|
switch_details->current_desktop = WideToUtf8(GetCurrentThreadDesktopNameW());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DWORD error = GetLastError();
|
||||||
|
if (switch_details != nullptr) {
|
||||||
|
switch_details->error_code = error;
|
||||||
|
switch_details->current_desktop = WideToUtf8(GetCurrentThreadDesktopNameW());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ScopedDesktopHandle {
|
||||||
|
HDESK handle = nullptr;
|
||||||
|
~ScopedDesktopHandle() {
|
||||||
|
if (handle != nullptr) {
|
||||||
|
CloseDesktop(handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
bool PreferSideSpecificVkInjection(int key_code) {
|
bool PreferSideSpecificVkInjection(int key_code) {
|
||||||
switch (key_code) {
|
switch (key_code) {
|
||||||
case VK_LSHIFT:
|
case VK_LSHIFT:
|
||||||
@@ -519,8 +723,20 @@ bool PreferSideSpecificVkInjection(int key_code) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int InjectKeyboardInput(int key_code, bool is_down, uint32_t scan_code,
|
InputInjectionResult InjectKeyboardInput(
|
||||||
bool extended) {
|
int key_code, bool is_down, uint32_t scan_code, bool extended,
|
||||||
|
const std::string& interactive_stage) {
|
||||||
|
ScopedDesktopHandle desktop;
|
||||||
|
DesktopSwitchDetails desktop_switch;
|
||||||
|
if (!EnsureThreadInteractiveDesktopForStage(interactive_stage,
|
||||||
|
&desktop.handle,
|
||||||
|
&desktop_switch)) {
|
||||||
|
const DWORD error = GetLastError();
|
||||||
|
return BuildInputFailure("switch_interactive_desktop_failed",
|
||||||
|
error != ERROR_SUCCESS ? error : ERROR_GEN_FAILURE,
|
||||||
|
desktop_switch);
|
||||||
|
}
|
||||||
|
|
||||||
INPUT input = {0};
|
INPUT input = {0};
|
||||||
input.type = INPUT_KEYBOARD;
|
input.type = INPUT_KEYBOARD;
|
||||||
|
|
||||||
@@ -561,16 +777,21 @@ int InjectKeyboardInput(int key_code, bool is_down, uint32_t scan_code,
|
|||||||
|
|
||||||
UINT sent = SendInput(1, &input, sizeof(INPUT));
|
UINT sent = SendInput(1, &input, sizeof(INPUT));
|
||||||
if (sent != 1) {
|
if (sent != 1) {
|
||||||
return static_cast<int>(GetLastError());
|
const DWORD error = GetLastError();
|
||||||
|
return BuildInputFailure("send_input_failed",
|
||||||
|
error != ERROR_SUCCESS ? error : ERROR_GEN_FAILURE,
|
||||||
|
desktop_switch);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return BuildInputSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ParseSecureInputKeyboardCommand(const std::string& command,
|
bool ParseSecureInputKeyboardCommand(const std::string& command,
|
||||||
int* key_code_out, bool* is_down_out,
|
int* key_code_out, bool* is_down_out,
|
||||||
uint32_t* scan_code_out,
|
uint32_t* scan_code_out,
|
||||||
bool* extended_out) {
|
bool* extended_out,
|
||||||
|
std::string* interactive_stage_out =
|
||||||
|
nullptr) {
|
||||||
if (key_code_out == nullptr || is_down_out == nullptr ||
|
if (key_code_out == nullptr || is_down_out == nullptr ||
|
||||||
scan_code_out == nullptr || extended_out == nullptr) {
|
scan_code_out == nullptr || extended_out == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
@@ -578,6 +799,9 @@ bool ParseSecureInputKeyboardCommand(const std::string& command,
|
|||||||
|
|
||||||
*scan_code_out = 0;
|
*scan_code_out = 0;
|
||||||
*extended_out = false;
|
*extended_out = false;
|
||||||
|
if (interactive_stage_out != nullptr) {
|
||||||
|
interactive_stage_out->clear();
|
||||||
|
}
|
||||||
|
|
||||||
if (command.rfind(crossdesk::kCrossDeskSecureInputKeyboardCommandPrefix, 0) !=
|
if (command.rfind(crossdesk::kCrossDeskSecureInputKeyboardCommandPrefix, 0) !=
|
||||||
0) {
|
0) {
|
||||||
@@ -630,7 +854,16 @@ bool ParseSecureInputKeyboardCommand(const std::string& command,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string extended_str = command.substr(extended_separator + 1);
|
const size_t stage_separator = command.find(':', extended_separator + 1);
|
||||||
|
const std::string extended_str =
|
||||||
|
stage_separator == std::string::npos
|
||||||
|
? command.substr(extended_separator + 1)
|
||||||
|
: command.substr(extended_separator + 1,
|
||||||
|
stage_separator - extended_separator - 1);
|
||||||
|
if (stage_separator != std::string::npos &&
|
||||||
|
interactive_stage_out != nullptr) {
|
||||||
|
*interactive_stage_out = command.substr(stage_separator + 1);
|
||||||
|
}
|
||||||
if (extended_str == "1" || extended_str == "true") {
|
if (extended_str == "1" || extended_str == "true") {
|
||||||
*extended_out = true;
|
*extended_out = true;
|
||||||
return true;
|
return true;
|
||||||
@@ -652,6 +885,7 @@ bool ParseSecureInputMouseCommand(const std::string& command,
|
|||||||
0) {
|
0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
request_out->interactive_stage.clear();
|
||||||
|
|
||||||
const size_t x_begin =
|
const size_t x_begin =
|
||||||
std::strlen(crossdesk::kCrossDeskSecureInputMouseCommandPrefix);
|
std::strlen(crossdesk::kCrossDeskSecureInputMouseCommandPrefix);
|
||||||
@@ -687,7 +921,15 @@ bool ParseSecureInputMouseCommand(const std::string& command,
|
|||||||
try {
|
try {
|
||||||
request_out->wheel =
|
request_out->wheel =
|
||||||
std::stoi(command.substr(wheel_begin, separator - wheel_begin));
|
std::stoi(command.substr(wheel_begin, separator - wheel_begin));
|
||||||
request_out->flag = std::stoi(command.substr(separator + 1));
|
const size_t flag_begin = separator + 1;
|
||||||
|
const size_t stage_separator = command.find(':', flag_begin);
|
||||||
|
request_out->flag = std::stoi(
|
||||||
|
stage_separator == std::string::npos
|
||||||
|
? command.substr(flag_begin)
|
||||||
|
: command.substr(flag_begin, stage_separator - flag_begin));
|
||||||
|
if (stage_separator != std::string::npos) {
|
||||||
|
request_out->interactive_stage = command.substr(stage_separator + 1);
|
||||||
|
}
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -705,15 +947,17 @@ bool ParseSecureInputCaptureCommand(const std::string& command,
|
|||||||
0) {
|
0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
request_out->interactive_stage.clear();
|
||||||
|
|
||||||
const size_t values_begin =
|
const size_t values_begin =
|
||||||
std::strlen(crossdesk::kCrossDeskSecureInputCaptureCommandPrefix);
|
std::strlen(crossdesk::kCrossDeskSecureInputCaptureCommandPrefix);
|
||||||
int parsed_values[5] = {0};
|
int parsed_values[5] = {0};
|
||||||
size_t token_begin = values_begin;
|
size_t token_begin = values_begin;
|
||||||
|
size_t separator = std::string::npos;
|
||||||
for (int index = 0; index < 5; ++index) {
|
for (int index = 0; index < 5; ++index) {
|
||||||
const size_t separator = command.find(':', token_begin);
|
separator = command.find(':', token_begin);
|
||||||
const bool is_last = index == 4;
|
const size_t token_end =
|
||||||
const size_t token_end = is_last ? command.size() : separator;
|
separator == std::string::npos ? command.size() : separator;
|
||||||
if (token_end == std::string::npos || token_end <= token_begin) {
|
if (token_end == std::string::npos || token_end <= token_begin) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -733,6 +977,9 @@ bool ParseSecureInputCaptureCommand(const std::string& command,
|
|||||||
request_out->width = parsed_values[2] & ~1;
|
request_out->width = parsed_values[2] & ~1;
|
||||||
request_out->height = parsed_values[3] & ~1;
|
request_out->height = parsed_values[3] & ~1;
|
||||||
request_out->show_cursor = parsed_values[4] != 0;
|
request_out->show_cursor = parsed_values[4] != 0;
|
||||||
|
request_out->interactive_stage =
|
||||||
|
separator == std::string::npos ? std::string()
|
||||||
|
: command.substr(token_begin);
|
||||||
return request_out->width > 0 && request_out->height > 0;
|
return request_out->width > 0 && request_out->height > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -746,15 +993,17 @@ bool ParseSecureInputCaptureStartCommand(const std::string& command,
|
|||||||
crossdesk::kCrossDeskSecureInputCaptureStartCommandPrefix, 0) != 0) {
|
crossdesk::kCrossDeskSecureInputCaptureStartCommandPrefix, 0) != 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
request_out->interactive_stage.clear();
|
||||||
|
|
||||||
const size_t values_begin = std::strlen(
|
const size_t values_begin = std::strlen(
|
||||||
crossdesk::kCrossDeskSecureInputCaptureStartCommandPrefix);
|
crossdesk::kCrossDeskSecureInputCaptureStartCommandPrefix);
|
||||||
int parsed_values[6] = {0};
|
int parsed_values[6] = {0};
|
||||||
size_t token_begin = values_begin;
|
size_t token_begin = values_begin;
|
||||||
|
size_t separator = std::string::npos;
|
||||||
for (int index = 0; index < 6; ++index) {
|
for (int index = 0; index < 6; ++index) {
|
||||||
const size_t separator = command.find(':', token_begin);
|
separator = command.find(':', token_begin);
|
||||||
const bool is_last = index == 5;
|
const size_t token_end =
|
||||||
const size_t token_end = is_last ? command.size() : separator;
|
separator == std::string::npos ? command.size() : separator;
|
||||||
if (token_end == std::string::npos || token_end <= token_begin) {
|
if (token_end == std::string::npos || token_end <= token_begin) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -775,56 +1024,107 @@ bool ParseSecureInputCaptureStartCommand(const std::string& command,
|
|||||||
request_out->height = parsed_values[3] & ~1;
|
request_out->height = parsed_values[3] & ~1;
|
||||||
request_out->show_cursor = parsed_values[4] != 0;
|
request_out->show_cursor = parsed_values[4] != 0;
|
||||||
request_out->fps = parsed_values[5] > 0 ? parsed_values[5] : 30;
|
request_out->fps = parsed_values[5] > 0 ? parsed_values[5] : 30;
|
||||||
|
request_out->interactive_stage =
|
||||||
|
separator == std::string::npos ? std::string()
|
||||||
|
: command.substr(token_begin);
|
||||||
return request_out->width > 0 && request_out->height > 0;
|
return request_out->width > 0 && request_out->height > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int InjectMouseInput(const SecureMouseRequest& request) {
|
LONG NormalizeAbsoluteMouseCoordinate(int value, int origin, int size) {
|
||||||
SetCursorPos(request.x, request.y);
|
if (size <= 1) {
|
||||||
|
|
||||||
INPUT input = {0};
|
|
||||||
input.type = INPUT_MOUSE;
|
|
||||||
switch (request.flag) {
|
|
||||||
case 1:
|
|
||||||
input.mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
input.mi.dwFlags = MOUSEEVENTF_LEFTUP;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
input.mi.dwFlags = MOUSEEVENTF_RIGHTDOWN;
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
input.mi.dwFlags = MOUSEEVENTF_RIGHTUP;
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
input.mi.dwFlags = MOUSEEVENTF_MIDDLEDOWN;
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
input.mi.dwFlags = MOUSEEVENTF_MIDDLEUP;
|
|
||||||
break;
|
|
||||||
case 7:
|
|
||||||
input.mi.dwFlags = MOUSEEVENTF_WHEEL;
|
|
||||||
input.mi.mouseData = request.wheel * 120;
|
|
||||||
break;
|
|
||||||
case 8:
|
|
||||||
input.mi.dwFlags = MOUSEEVENTF_HWHEEL;
|
|
||||||
input.mi.mouseData = request.wheel * 120;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
input.mi.dwFlags = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.mi.dwFlags == 0) {
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
UINT sent = SendInput(1, &input, sizeof(INPUT));
|
const int clamped_value =
|
||||||
if (sent != 1) {
|
(std::max)(origin, (std::min)(value, origin + size - 1));
|
||||||
return static_cast<int>(GetLastError());
|
const long long relative_value =
|
||||||
|
static_cast<long long>(clamped_value - origin) * 65535;
|
||||||
|
return static_cast<LONG>(relative_value / (size - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
INPUT BuildAbsoluteMouseMoveInput(int x, int y) {
|
||||||
|
INPUT input = {0};
|
||||||
|
input.type = INPUT_MOUSE;
|
||||||
|
|
||||||
|
const int virtual_left = GetSystemMetrics(SM_XVIRTUALSCREEN);
|
||||||
|
const int virtual_top = GetSystemMetrics(SM_YVIRTUALSCREEN);
|
||||||
|
const int virtual_width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
|
||||||
|
const int virtual_height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
|
||||||
|
|
||||||
|
input.mi.dx =
|
||||||
|
NormalizeAbsoluteMouseCoordinate(x, virtual_left, virtual_width);
|
||||||
|
input.mi.dy =
|
||||||
|
NormalizeAbsoluteMouseCoordinate(y, virtual_top, virtual_height);
|
||||||
|
input.mi.dwFlags =
|
||||||
|
MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK;
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
InputInjectionResult InjectMouseInput(const SecureMouseRequest& request) {
|
||||||
|
ScopedDesktopHandle desktop;
|
||||||
|
DesktopSwitchDetails desktop_switch;
|
||||||
|
if (!EnsureThreadInteractiveDesktopForStage(request.interactive_stage,
|
||||||
|
&desktop.handle,
|
||||||
|
&desktop_switch)) {
|
||||||
|
const DWORD error = GetLastError();
|
||||||
|
return BuildInputFailure("switch_interactive_desktop_failed",
|
||||||
|
error != ERROR_SUCCESS ? error : ERROR_GEN_FAILURE,
|
||||||
|
desktop_switch);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
std::vector<INPUT> inputs;
|
||||||
|
inputs.push_back(BuildAbsoluteMouseMoveInput(request.x, request.y));
|
||||||
|
|
||||||
|
INPUT action_input = {0};
|
||||||
|
action_input.type = INPUT_MOUSE;
|
||||||
|
switch (request.flag) {
|
||||||
|
case 0:
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
action_input.mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
action_input.mi.dwFlags = MOUSEEVENTF_LEFTUP;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
action_input.mi.dwFlags = MOUSEEVENTF_RIGHTDOWN;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
action_input.mi.dwFlags = MOUSEEVENTF_RIGHTUP;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
action_input.mi.dwFlags = MOUSEEVENTF_MIDDLEDOWN;
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
action_input.mi.dwFlags = MOUSEEVENTF_MIDDLEUP;
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
action_input.mi.dwFlags = MOUSEEVENTF_WHEEL;
|
||||||
|
action_input.mi.mouseData = request.wheel * 120;
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
action_input.mi.dwFlags = MOUSEEVENTF_HWHEEL;
|
||||||
|
action_input.mi.mouseData = request.wheel * 120;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
action_input.mi.dwFlags = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action_input.mi.dwFlags != 0) {
|
||||||
|
inputs.push_back(action_input);
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT sent =
|
||||||
|
SendInput(static_cast<UINT>(inputs.size()), inputs.data(), sizeof(INPUT));
|
||||||
|
if (sent != inputs.size()) {
|
||||||
|
const DWORD error = GetLastError();
|
||||||
|
return BuildInputFailure("send_input_failed",
|
||||||
|
error != ERROR_SUCCESS ? error : ERROR_GEN_FAILURE,
|
||||||
|
desktop_switch);
|
||||||
|
}
|
||||||
|
|
||||||
|
return BuildInputSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint8_t> BuildTextResponseBytes(const std::string& response) {
|
std::vector<uint8_t> BuildTextResponseBytes(const std::string& response) {
|
||||||
@@ -838,6 +1138,15 @@ std::vector<uint8_t> CaptureSecureDesktopFrame(
|
|||||||
return BuildTextResponseBytes(BuildErrorJson("invalid_capture_buffers"));
|
return BuildTextResponseBytes(BuildErrorJson("invalid_capture_buffers"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ScopedDesktopHandle desktop;
|
||||||
|
if (!EnsureThreadInteractiveDesktopForStage(request.interactive_stage,
|
||||||
|
&desktop.handle)) {
|
||||||
|
const DWORD error = GetLastError();
|
||||||
|
return BuildTextResponseBytes(BuildErrorJson(
|
||||||
|
"switch_interactive_desktop_failed",
|
||||||
|
error != ERROR_SUCCESS ? error : ERROR_GEN_FAILURE));
|
||||||
|
}
|
||||||
|
|
||||||
HDC screen_dc = GetDC(nullptr);
|
HDC screen_dc = GetDC(nullptr);
|
||||||
if (screen_dc == nullptr) {
|
if (screen_dc == nullptr) {
|
||||||
return BuildTextResponseBytes(
|
return BuildTextResponseBytes(
|
||||||
@@ -979,6 +1288,14 @@ void SecureDesktopSharedCaptureThread(
|
|||||||
static_cast<size_t>(request.width) * request.height * 3 / 2;
|
static_cast<size_t>(request.width) * request.height * 3 / 2;
|
||||||
std::vector<uint8_t> nv12_frame(nv12_size);
|
std::vector<uint8_t> nv12_frame(nv12_size);
|
||||||
|
|
||||||
|
ScopedDesktopHandle desktop;
|
||||||
|
if (!EnsureThreadInteractiveDesktopForStage(request.interactive_stage,
|
||||||
|
&desktop.handle)) {
|
||||||
|
LOG_ERROR("Secure shared capture desktop switch failed, error={}",
|
||||||
|
GetLastError());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
HDC screen_dc = GetDC(nullptr);
|
HDC screen_dc = GetDC(nullptr);
|
||||||
if (screen_dc == nullptr) {
|
if (screen_dc == nullptr) {
|
||||||
LOG_ERROR("Secure shared capture GetDC failed, error={}", GetLastError());
|
LOG_ERROR("Secure shared capture GetDC failed, error={}", GetLastError());
|
||||||
@@ -1224,17 +1541,21 @@ std::vector<uint8_t> HandleSecureInputHelperCommand(
|
|||||||
bool is_down = false;
|
bool is_down = false;
|
||||||
uint32_t scan_code = 0;
|
uint32_t scan_code = 0;
|
||||||
bool extended = false;
|
bool extended = false;
|
||||||
|
std::string interactive_stage;
|
||||||
if (ParseSecureInputKeyboardCommand(command, &key_code, &is_down, &scan_code,
|
if (ParseSecureInputKeyboardCommand(command, &key_code, &is_down, &scan_code,
|
||||||
&extended)) {
|
&extended, &interactive_stage)) {
|
||||||
const int inject_result =
|
const InputInjectionResult inject_result =
|
||||||
InjectKeyboardInput(key_code, is_down, scan_code, extended);
|
InjectKeyboardInput(key_code, is_down, scan_code, extended,
|
||||||
if (inject_result != 0) {
|
interactive_stage);
|
||||||
|
if (!inject_result.ok) {
|
||||||
LOG_WARN(
|
LOG_WARN(
|
||||||
"Secure input helper SendInput failed for key_code={}, is_down={}, "
|
"Secure input helper input failed for key_code={}, is_down={}, "
|
||||||
"scan_code={}, extended={}, err={}",
|
"scan_code={}, extended={}, error='{}', stage='{}', target='{}', "
|
||||||
key_code, is_down, scan_code, extended, inject_result);
|
"current='{}', code={}",
|
||||||
return BuildTextResponseBytes(BuildErrorJson(
|
key_code, is_down, scan_code, extended, inject_result.error,
|
||||||
"send_input_failed", static_cast<DWORD>(inject_result)));
|
interactive_stage, inject_result.desktop.target_desktop,
|
||||||
|
inject_result.desktop.current_desktop, inject_result.error_code);
|
||||||
|
return BuildTextResponseBytes(BuildInputFailureJson(inject_result).dump());
|
||||||
}
|
}
|
||||||
|
|
||||||
Json json;
|
Json json;
|
||||||
@@ -1244,21 +1565,24 @@ std::vector<uint8_t> HandleSecureInputHelperCommand(
|
|||||||
json["is_down"] = is_down;
|
json["is_down"] = is_down;
|
||||||
json["scan_code"] = scan_code;
|
json["scan_code"] = scan_code;
|
||||||
json["extended"] = extended;
|
json["extended"] = extended;
|
||||||
|
json["stage"] = interactive_stage;
|
||||||
json["desktop"] = WideToUtf8(GetCurrentThreadDesktopNameW());
|
json["desktop"] = WideToUtf8(GetCurrentThreadDesktopNameW());
|
||||||
return BuildTextResponseBytes(json.dump());
|
return BuildTextResponseBytes(json.dump());
|
||||||
}
|
}
|
||||||
|
|
||||||
SecureMouseRequest mouse_request;
|
SecureMouseRequest mouse_request;
|
||||||
if (ParseSecureInputMouseCommand(command, &mouse_request)) {
|
if (ParseSecureInputMouseCommand(command, &mouse_request)) {
|
||||||
const int inject_result = InjectMouseInput(mouse_request);
|
const InputInjectionResult inject_result = InjectMouseInput(mouse_request);
|
||||||
if (inject_result != 0) {
|
if (!inject_result.ok) {
|
||||||
LOG_WARN(
|
LOG_WARN(
|
||||||
"Secure input helper SendInput failed for mouse x={}, y={}, "
|
"Secure input helper input failed for mouse x={}, y={}, "
|
||||||
"wheel={}, flag={}, err={}",
|
"wheel={}, flag={}, error='{}', stage='{}', target='{}', "
|
||||||
|
"current='{}', code={}",
|
||||||
mouse_request.x, mouse_request.y, mouse_request.wheel,
|
mouse_request.x, mouse_request.y, mouse_request.wheel,
|
||||||
mouse_request.flag, inject_result);
|
mouse_request.flag, inject_result.error,
|
||||||
return BuildTextResponseBytes(BuildErrorJson(
|
mouse_request.interactive_stage, inject_result.desktop.target_desktop,
|
||||||
"send_input_failed", static_cast<DWORD>(inject_result)));
|
inject_result.desktop.current_desktop, inject_result.error_code);
|
||||||
|
return BuildTextResponseBytes(BuildInputFailureJson(inject_result).dump());
|
||||||
}
|
}
|
||||||
|
|
||||||
Json json;
|
Json json;
|
||||||
@@ -1268,6 +1592,7 @@ std::vector<uint8_t> HandleSecureInputHelperCommand(
|
|||||||
json["y"] = mouse_request.y;
|
json["y"] = mouse_request.y;
|
||||||
json["wheel"] = mouse_request.wheel;
|
json["wheel"] = mouse_request.wheel;
|
||||||
json["flag"] = mouse_request.flag;
|
json["flag"] = mouse_request.flag;
|
||||||
|
json["stage"] = mouse_request.interactive_stage;
|
||||||
json["desktop"] = WideToUtf8(GetCurrentThreadDesktopNameW());
|
json["desktop"] = WideToUtf8(GetCurrentThreadDesktopNameW());
|
||||||
return BuildTextResponseBytes(json.dump());
|
return BuildTextResponseBytes(json.dump());
|
||||||
}
|
}
|
||||||
@@ -1394,6 +1719,8 @@ void PrintUsage() {
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
|
EnablePerMonitorDpiAwareness();
|
||||||
|
|
||||||
InitializeHelperLogger();
|
InitializeHelperLogger();
|
||||||
|
|
||||||
bool run_helper = false;
|
bool run_helper = false;
|
||||||
@@ -1447,24 +1774,10 @@ int main(int argc, char* argv[]) {
|
|||||||
expected_session_id, current_session_id);
|
expected_session_id, current_session_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
HDESK secure_desktop = nullptr;
|
LOG_INFO("Secure input helper initial desktop: '{}'",
|
||||||
if (!EnsureThreadDesktop(L"Winlogon", &secure_desktop)) {
|
|
||||||
LOG_ERROR(
|
|
||||||
"Failed to switch secure input helper to Winlogon desktop, error={}",
|
|
||||||
GetLastError());
|
|
||||||
if (stop_event != nullptr) {
|
|
||||||
CloseHandle(stop_event);
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG_INFO("Secure input helper desktop: '{}'",
|
|
||||||
WideToUtf8(GetCurrentThreadDesktopNameW()));
|
WideToUtf8(GetCurrentThreadDesktopNameW()));
|
||||||
SecureInputHelperIpcServerLoop(stop_event, current_session_id);
|
SecureInputHelperIpcServerLoop(stop_event, current_session_id);
|
||||||
|
|
||||||
if (secure_desktop != nullptr) {
|
|
||||||
CloseDesktop(secure_desktop);
|
|
||||||
}
|
|
||||||
if (stop_event != nullptr) {
|
if (stop_event != nullptr) {
|
||||||
CloseHandle(stop_event);
|
CloseHandle(stop_event);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,16 @@ bool ExpectContains(const char* name, const std::string& value,
|
|||||||
return false;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
@@ -50,13 +60,166 @@ int main() {
|
|||||||
|
|
||||||
const std::string service_host =
|
const std::string service_host =
|
||||||
ReadFile(repo_root / "src/service/windows/service_host.cpp");
|
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");
|
||||||
|
const std::string targets =
|
||||||
|
ReadFile(repo_root / "xmake/targets.lua");
|
||||||
|
const std::string interactive_state =
|
||||||
|
ReadFile(repo_root / "src/service/windows/interactive_state.h");
|
||||||
|
const std::string render_callback =
|
||||||
|
ReadFile(repo_root / "src/gui/render_callback.cpp");
|
||||||
|
const std::string render = ReadFile(repo_root / "src/gui/render.cpp");
|
||||||
|
const std::string screen_capturer_h =
|
||||||
|
ReadFile(repo_root / "src/screen_capturer/windows/screen_capturer_win.h");
|
||||||
|
const std::string screen_capturer_cpp =
|
||||||
|
ReadFile(repo_root / "src/screen_capturer/windows/screen_capturer_win.cpp");
|
||||||
|
|
||||||
bool ok = true;
|
bool ok = true;
|
||||||
ok &= ExpectContains("service_host.cpp", service_host,
|
ok &= ExpectContains("service_host.cpp", service_host,
|
||||||
"ParseSecureDesktopMouseIpcCommand");
|
"ParseSecureDesktopMouseIpcCommand");
|
||||||
ok &= ExpectContains("service_host.cpp", service_host,
|
ok &= ExpectContains("service_host.cpp", service_host,
|
||||||
"BuildSecureInputHelperMouseCommand");
|
"BuildSecureInputHelperMouseCommand");
|
||||||
|
ok &= ExpectContains("targets.lua", targets,
|
||||||
|
"target(\"crossdesk_session_helper\")");
|
||||||
|
ok &= ExpectContains("targets.lua", targets,
|
||||||
|
"add_files(\"scripts/windows/crossdesk.rc\")");
|
||||||
|
ok &= ExpectContains("session_helper_main.cpp", session_helper,
|
||||||
|
"EnablePerMonitorDpiAwareness");
|
||||||
|
ok &= ExpectContains("session_helper_main.cpp", session_helper,
|
||||||
|
"SetProcessDpiAwarenessContext(\n"
|
||||||
|
" DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)");
|
||||||
|
ok &= ExpectContains("session_helper_main.cpp", session_helper,
|
||||||
|
"EnablePerMonitorDpiAwareness();\n\n"
|
||||||
|
" InitializeHelperLogger();");
|
||||||
|
ok &= ExpectContains("service_host.cpp", service_host,
|
||||||
|
"const ULONGLONG deadline_tick = GetTickCount64() + timeout_ms");
|
||||||
|
ok &= ExpectContains("service_host.cpp", service_host,
|
||||||
|
"while (GetTickCount64() <= deadline_tick)");
|
||||||
|
ok &= ExpectNotContains("service_host.cpp", service_host,
|
||||||
|
"constexpr int kPipeConnectRetryCount = 3");
|
||||||
|
ok &= ExpectContains("service_host.cpp", service_host,
|
||||||
|
"BuildSecureInputHelperKeyboardCommand(");
|
||||||
|
ok &= ExpectContains("service_host.cpp", service_host,
|
||||||
|
"const std::string& interactive_stage");
|
||||||
|
ok &= ExpectContains("service_host.h", service_host_h,
|
||||||
|
"bool LaunchSecureInputHelper(DWORD session_id,\n"
|
||||||
|
" const std::string& interactive_stage)");
|
||||||
|
ok &= ExpectContains("service_host.h", service_host_h,
|
||||||
|
"std::string secure_input_helper_interactive_stage_");
|
||||||
|
ok &= ExpectContains("service_host.cpp", service_host,
|
||||||
|
"SecureInputHelperDesktopForStage");
|
||||||
|
ok &= ExpectContains("service_host.cpp", service_host,
|
||||||
|
"return L\"winsta0\\\\Winlogon\"");
|
||||||
|
ok &= ExpectContains("service_host.cpp", service_host,
|
||||||
|
"return L\"winsta0\\\\default\"");
|
||||||
|
ok &= ExpectContains("service_host.cpp", service_host,
|
||||||
|
"secure_input_helper_interactive_stage_ == interactive_stage");
|
||||||
|
ok &= ExpectContains("service_host.cpp", service_host,
|
||||||
|
"secure_input_helper_interactive_stage_ = interactive_stage");
|
||||||
|
ok &= ExpectContains("service_host.cpp", service_host,
|
||||||
|
"secure_input_helper_interactive_stage_.clear()");
|
||||||
|
ok &= ExpectContains("service_host.cpp", service_host,
|
||||||
|
"LaunchSecureInputHelper(target_session_id, interactive_stage)");
|
||||||
|
ok &= ExpectContains("service_host.cpp", service_host,
|
||||||
|
"\\\"secure_input_helper_stage\\\":\\\"");
|
||||||
|
ok &= ExpectContains("service_host.cpp", service_host,
|
||||||
|
"session_helper_report_interactive_stage_");
|
||||||
ok &= ExpectContains("service_host.cpp", service_host,
|
ok &= ExpectContains("service_host.cpp", service_host,
|
||||||
"return SendSecureDesktopMouseInput");
|
"return SendSecureDesktopMouseInput");
|
||||||
|
ok &= ExpectContains("render.cpp", render,
|
||||||
|
"constexpr DWORD kWindowsServiceQueryTimeoutMs = 500");
|
||||||
|
ok &= ExpectContains("screen_capturer_win.cpp", screen_capturer_cpp,
|
||||||
|
"constexpr DWORD kSecureDesktopStatusPipeTimeoutMs = 500");
|
||||||
|
ok &= ExpectContains("render.cpp", render,
|
||||||
|
"IsTransientWindowsServiceStatusError(status.error)");
|
||||||
|
ok &= ExpectContains("screen_capturer_win.cpp", screen_capturer_cpp,
|
||||||
|
"IsTransientWindowsServiceStatusError(status.error)");
|
||||||
|
ok &= ExpectContains("render.cpp", render,
|
||||||
|
"Local Windows service temporarily unavailable");
|
||||||
|
ok &= ExpectContains("screen_capturer_win.cpp", screen_capturer_cpp,
|
||||||
|
"Windows capturer secure desktop service temporarily unavailable");
|
||||||
|
ok &= ExpectContains("screen_capturer_win.cpp", screen_capturer_cpp,
|
||||||
|
"Windows capturer secure desktop transient frame query failed");
|
||||||
|
ok &= ExpectContains("screen_capturer_win.cpp", screen_capturer_cpp,
|
||||||
|
"if (transient_error) {\n"
|
||||||
|
" LOG_INFO(");
|
||||||
|
ok &= ExpectContains("render_callback.cpp", render_callback,
|
||||||
|
"IsTransientSecureDesktopInputFailure");
|
||||||
|
ok &= ExpectContains("render_callback.cpp", render_callback,
|
||||||
|
"Secure desktop keyboard injection transient failure");
|
||||||
|
ok &= ExpectContains("session_helper_main.cpp", session_helper,
|
||||||
|
"MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE");
|
||||||
|
ok &= ExpectContains("session_helper_main.cpp", session_helper,
|
||||||
|
"MOUSEEVENTF_VIRTUALDESK");
|
||||||
|
ok &= ExpectContains("session_helper_main.cpp", session_helper,
|
||||||
|
"std::vector<INPUT> inputs");
|
||||||
|
ok &= ExpectContains("session_helper_main.cpp", session_helper,
|
||||||
|
"SendInput(static_cast<UINT>(inputs.size())");
|
||||||
|
ok &= ExpectNotContains("session_helper_main.cpp", session_helper,
|
||||||
|
"SetCursorPos(request.x, request.y)");
|
||||||
|
ok &= ExpectContains("session_helper_main.cpp", session_helper,
|
||||||
|
"NormalizeAbsoluteMouseCoordinate");
|
||||||
|
ok &= ExpectContains("session_helper_main.cpp", session_helper,
|
||||||
|
"EnsureThreadInteractiveDesktop");
|
||||||
|
ok &= ExpectContains("session_helper_main.cpp", session_helper,
|
||||||
|
"OpenInputDesktop");
|
||||||
|
ok &= ExpectContains("session_helper_main.cpp", session_helper,
|
||||||
|
"DesktopNameForInteractiveStage");
|
||||||
|
ok &= ExpectContains("session_helper_main.cpp", session_helper,
|
||||||
|
"interactive_stage == \"credential-ui\"");
|
||||||
|
ok &= ExpectContains("session_helper_main.cpp", session_helper,
|
||||||
|
"return L\"Winlogon\"");
|
||||||
|
ok &= ExpectContains("session_helper_main.cpp", session_helper,
|
||||||
|
"interactive_stage == \"lock-screen\"");
|
||||||
|
ok &= ExpectContains("session_helper_main.cpp", session_helper,
|
||||||
|
"return L\"Default\"");
|
||||||
|
ok &= ExpectContains("session_helper_main.cpp", session_helper,
|
||||||
|
"EnsureThreadInteractiveDesktopForStage");
|
||||||
|
ok &= ExpectContains("session_helper_main.cpp", session_helper,
|
||||||
|
"switch_interactive_desktop_failed");
|
||||||
|
ok &= ExpectContains("session_helper_main.cpp", session_helper,
|
||||||
|
"Json BuildInputFailureJson");
|
||||||
|
ok &= ExpectContains("session_helper_main.cpp", session_helper,
|
||||||
|
"json[\"target_desktop\"]");
|
||||||
|
ok &= ExpectContains("session_helper_main.cpp", session_helper,
|
||||||
|
"json[\"current_desktop\"]");
|
||||||
|
ok &= ExpectContains("session_helper_main.cpp", session_helper,
|
||||||
|
"json[\"stage\"]");
|
||||||
|
ok &= ExpectContains("session_helper_main.cpp", session_helper,
|
||||||
|
"ParseSecureInputKeyboardCommand(command, &key_code, &is_down, &scan_code,\n"
|
||||||
|
" &extended, &interactive_stage)");
|
||||||
|
ok &= ExpectContains("session_helper_main.cpp", session_helper,
|
||||||
|
"InjectKeyboardInput(key_code, is_down, scan_code, extended,\n"
|
||||||
|
" interactive_stage)");
|
||||||
|
ok &= ExpectContains("session_helper_main.cpp", session_helper,
|
||||||
|
"InjectMouseInput(mouse_request)");
|
||||||
|
ok &= ExpectNotContains("session_helper_main.cpp", session_helper,
|
||||||
|
"EnsureThreadDesktop(L\"Winlogon\", &secure_desktop)");
|
||||||
|
ok &= ExpectContains("service_host.cpp", service_host,
|
||||||
|
"winsta0\\\\default");
|
||||||
|
ok &= ExpectNotContains("service_host.cpp", service_host,
|
||||||
|
"startup_info.lpDesktop = const_cast<LPWSTR>(L\"winsta0\\\\Winlogon\")");
|
||||||
|
ok &= ExpectContains("interactive_state.h", interactive_state,
|
||||||
|
"interactive_stage == \"lock-screen\"");
|
||||||
|
ok &= ExpectContains("render_callback.cpp", render_callback,
|
||||||
|
"RemoteAction remote_action{};");
|
||||||
|
ok &= ExpectContains("render.cpp", render,
|
||||||
|
"previous_secure_desktop_interaction");
|
||||||
|
ok &= ExpectNotContains(
|
||||||
|
"render_callback.cpp", render_callback,
|
||||||
|
"render->local_service_available_ &&\n"
|
||||||
|
" IsSecureDesktopInteractionRequired(render->local_interactive_stage_)");
|
||||||
|
ok &= ExpectContains("screen_capturer_win.h", screen_capturer_h,
|
||||||
|
"std::string secure_shared_stage_;");
|
||||||
|
ok &= ExpectContains("screen_capturer_win.cpp", screen_capturer_cpp,
|
||||||
|
"const std::string& stage");
|
||||||
|
ok &= ExpectContains("screen_capturer_win.cpp", screen_capturer_cpp,
|
||||||
|
"secure_shared_stage_ == stage");
|
||||||
|
ok &= ExpectContains("screen_capturer_win.cpp", screen_capturer_cpp,
|
||||||
|
"secure_shared_stage_ = stage");
|
||||||
|
ok &= ExpectContains("screen_capturer_win.cpp", screen_capturer_cpp,
|
||||||
|
"secure_shared_stage_.clear()");
|
||||||
return ok ? 0 : 1;
|
return ok ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -217,6 +217,7 @@ function setup_targets()
|
|||||||
add_deps("rd_log", "path_manager")
|
add_deps("rd_log", "path_manager")
|
||||||
add_links("Advapi32", "User32", "Wtsapi32", "Gdi32")
|
add_links("Advapi32", "User32", "Wtsapi32", "Gdi32")
|
||||||
add_files("src/service/windows/session_helper_main.cpp")
|
add_files("src/service/windows/session_helper_main.cpp")
|
||||||
|
add_files("scripts/windows/crossdesk.rc")
|
||||||
add_includedirs("src/service/windows", {public = true})
|
add_includedirs("src/service/windows", {public = true})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user