Fix console session changes via fast user switching
We need to respawn Sunshine.exe in the new console session.
This commit is contained in:
parent
f41e57ea8c
commit
9955890023
1 changed files with 62 additions and 30 deletions
|
|
@ -16,6 +16,7 @@
|
||||||
SERVICE_STATUS_HANDLE service_status_handle;
|
SERVICE_STATUS_HANDLE service_status_handle;
|
||||||
SERVICE_STATUS service_status;
|
SERVICE_STATUS service_status;
|
||||||
HANDLE stop_event;
|
HANDLE stop_event;
|
||||||
|
HANDLE session_change_event;
|
||||||
|
|
||||||
#define SERVICE_NAME "SunshineSvc"
|
#define SERVICE_NAME "SunshineSvc"
|
||||||
|
|
||||||
|
|
@ -25,6 +26,14 @@ HandlerEx(DWORD dwControl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpConte
|
||||||
case SERVICE_CONTROL_INTERROGATE:
|
case SERVICE_CONTROL_INTERROGATE:
|
||||||
return NO_ERROR;
|
return NO_ERROR;
|
||||||
|
|
||||||
|
case SERVICE_CONTROL_SESSIONCHANGE:
|
||||||
|
// If a new session connects to the console, restart Sunshine
|
||||||
|
// to allow it to spawn inside the new console session.
|
||||||
|
if (dwEventType == WTS_CONSOLE_CONNECT) {
|
||||||
|
SetEvent(session_change_event);
|
||||||
|
}
|
||||||
|
return NO_ERROR;
|
||||||
|
|
||||||
case SERVICE_CONTROL_PRESHUTDOWN:
|
case SERVICE_CONTROL_PRESHUTDOWN:
|
||||||
// The system is shutting down
|
// The system is shutting down
|
||||||
case SERVICE_CONTROL_STOP:
|
case SERVICE_CONTROL_STOP:
|
||||||
|
|
@ -39,7 +48,7 @@ HandlerEx(DWORD dwControl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpConte
|
||||||
return NO_ERROR;
|
return NO_ERROR;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return NO_ERROR;
|
return ERROR_CALL_NOT_IMPLEMENTED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -87,13 +96,7 @@ AllocateProcThreadAttributeList(DWORD attribute_count) {
|
||||||
}
|
}
|
||||||
|
|
||||||
HANDLE
|
HANDLE
|
||||||
DuplicateTokenForConsoleSession() {
|
DuplicateTokenForSession(DWORD console_session_id) {
|
||||||
auto console_session_id = WTSGetActiveConsoleSessionId();
|
|
||||||
if (console_session_id == 0xFFFFFFFF) {
|
|
||||||
// No console session yet
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
HANDLE current_token;
|
HANDLE current_token;
|
||||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE, ¤t_token)) {
|
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE, ¤t_token)) {
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
@ -201,6 +204,7 @@ ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv) {
|
||||||
service_status.dwCurrentState = SERVICE_START_PENDING;
|
service_status.dwCurrentState = SERVICE_START_PENDING;
|
||||||
SetServiceStatus(service_status_handle, &service_status);
|
SetServiceStatus(service_status_handle, &service_status);
|
||||||
|
|
||||||
|
// Create a manual-reset stop event
|
||||||
stop_event = CreateEventA(NULL, TRUE, FALSE, NULL);
|
stop_event = CreateEventA(NULL, TRUE, FALSE, NULL);
|
||||||
if (stop_event == NULL) {
|
if (stop_event == NULL) {
|
||||||
// Tell SCM we failed to start
|
// Tell SCM we failed to start
|
||||||
|
|
@ -210,6 +214,16 @@ ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create an auto-reset session change event
|
||||||
|
session_change_event = CreateEventA(NULL, FALSE, FALSE, NULL);
|
||||||
|
if (session_change_event == NULL) {
|
||||||
|
// Tell SCM we failed to start
|
||||||
|
service_status.dwWin32ExitCode = GetLastError();
|
||||||
|
service_status.dwCurrentState = SERVICE_STOPPED;
|
||||||
|
SetServiceStatus(service_status_handle, &service_status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
auto log_file_handle = OpenLogFileHandle();
|
auto log_file_handle = OpenLogFileHandle();
|
||||||
if (log_file_handle == INVALID_HANDLE_VALUE) {
|
if (log_file_handle == INVALID_HANDLE_VALUE) {
|
||||||
// Tell SCM we failed to start
|
// Tell SCM we failed to start
|
||||||
|
|
@ -248,13 +262,19 @@ ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv) {
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
// Tell SCM we're running (and stoppable now)
|
// Tell SCM we're running (and stoppable now)
|
||||||
service_status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PRESHUTDOWN;
|
service_status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PRESHUTDOWN | SERVICE_ACCEPT_SESSIONCHANGE;
|
||||||
service_status.dwCurrentState = SERVICE_RUNNING;
|
service_status.dwCurrentState = SERVICE_RUNNING;
|
||||||
SetServiceStatus(service_status_handle, &service_status);
|
SetServiceStatus(service_status_handle, &service_status);
|
||||||
|
|
||||||
// Loop every 3 seconds until the stop event is set or Sunshine.exe is running
|
// Loop every 3 seconds until the stop event is set or Sunshine.exe is running
|
||||||
while (WaitForSingleObject(stop_event, 3000) != WAIT_OBJECT_0) {
|
while (WaitForSingleObject(stop_event, 3000) != WAIT_OBJECT_0) {
|
||||||
auto console_token = DuplicateTokenForConsoleSession();
|
auto console_session_id = WTSGetActiveConsoleSessionId();
|
||||||
|
if (console_session_id == 0xFFFFFFFF) {
|
||||||
|
// No console session yet
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto console_token = DuplicateTokenForSession(console_session_id);
|
||||||
if (console_token == NULL) {
|
if (console_token == NULL) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -292,30 +312,42 @@ ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for either the stop event to be set or Sunshine.exe to terminate
|
bool still_running;
|
||||||
const HANDLE wait_objects[] = { stop_event, process_info.hProcess };
|
do {
|
||||||
switch (WaitForMultipleObjects(_countof(wait_objects), wait_objects, FALSE, INFINITE)) {
|
// Wait for the stop event to be set, Sunshine.exe to terminate, or the console session to change
|
||||||
case WAIT_OBJECT_0:
|
const HANDLE wait_objects[] = { stop_event, process_info.hProcess, session_change_event };
|
||||||
// The service is shutting down, so try to gracefully terminate Sunshine.exe.
|
switch (WaitForMultipleObjects(_countof(wait_objects), wait_objects, FALSE, INFINITE)) {
|
||||||
// If it doesn't terminate in 20 seconds, we will forcefully terminate it.
|
case WAIT_OBJECT_0 + 2:
|
||||||
if (!RunTerminationHelper(console_token, process_info.dwProcessId) ||
|
if (WTSGetActiveConsoleSessionId() == console_session_id) {
|
||||||
WaitForSingleObject(process_info.hProcess, 20000) != WAIT_OBJECT_0) {
|
// The active console session didn't actually change. Let Sunshine keep running.
|
||||||
// If it won't terminate gracefully, kill it now
|
still_running = true;
|
||||||
TerminateProcess(process_info.hProcess, ERROR_PROCESS_ABORTED);
|
continue;
|
||||||
}
|
}
|
||||||
break;
|
// Fall-through to terminate Sunshine.exe and start it again.
|
||||||
|
case WAIT_OBJECT_0:
|
||||||
|
// The service is shutting down, so try to gracefully terminate Sunshine.exe.
|
||||||
|
// If it doesn't terminate in 20 seconds, we will forcefully terminate it.
|
||||||
|
if (!RunTerminationHelper(console_token, process_info.dwProcessId) ||
|
||||||
|
WaitForSingleObject(process_info.hProcess, 20000) != WAIT_OBJECT_0) {
|
||||||
|
// If it won't terminate gracefully, kill it now
|
||||||
|
TerminateProcess(process_info.hProcess, ERROR_PROCESS_ABORTED);
|
||||||
|
}
|
||||||
|
still_running = false;
|
||||||
|
break;
|
||||||
|
|
||||||
case WAIT_OBJECT_0 + 1: {
|
case WAIT_OBJECT_0 + 1: {
|
||||||
// Sunshine terminated itself.
|
// Sunshine terminated itself.
|
||||||
|
|
||||||
DWORD exit_code;
|
DWORD exit_code;
|
||||||
if (GetExitCodeProcess(process_info.hProcess, &exit_code) && exit_code == ERROR_SHUTDOWN_IN_PROGRESS) {
|
if (GetExitCodeProcess(process_info.hProcess, &exit_code) && exit_code == ERROR_SHUTDOWN_IN_PROGRESS) {
|
||||||
// Sunshine is asking for us to shut down, so gracefully stop ourselves.
|
// Sunshine is asking for us to shut down, so gracefully stop ourselves.
|
||||||
SetEvent(stop_event);
|
SetEvent(stop_event);
|
||||||
|
}
|
||||||
|
still_running = false;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
} while (still_running);
|
||||||
|
|
||||||
CloseHandle(process_info.hThread);
|
CloseHandle(process_info.hThread);
|
||||||
CloseHandle(process_info.hProcess);
|
CloseHandle(process_info.hProcess);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue