diff --git a/host/platform/Windows/CMakeLists.txt b/host/platform/Windows/CMakeLists.txt index 4ccf06c3..d689001c 100644 --- a/host/platform/Windows/CMakeLists.txt +++ b/host/platform/Windows/CMakeLists.txt @@ -28,6 +28,7 @@ target_link_libraries(platform_Windows psapi shlwapi powrprof + rpcrt4 ) target_include_directories(platform_Windows diff --git a/host/platform/Windows/src/platform.c b/host/platform/Windows/src/platform.c index 39dce89c..e7e93842 100644 --- a/host/platform/Windows/src/platform.c +++ b/host/platform/Windows/src/platform.c @@ -57,6 +57,9 @@ struct AppState NOTIFYICONDATA iconData; UINT trayRestartMsg; HMENU trayMenu; + + HANDLE exitThreadEvent; + LGThread * exitThread; }; static struct AppState app = {0}; @@ -212,6 +215,7 @@ LRESULT CALLBACK DummyWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) switch(msg) { case WM_DESTROY: + Shell_NotifyIcon(NIM_DELETE, &app.iconData); PostQuitMessage(0); break; @@ -264,12 +268,31 @@ static int appThread(void * opaque) { RegisterTrayIcon(); int result = app_main(app.argc, app.argv); - - Shell_NotifyIcon(NIM_DELETE, &app.iconData); - SendMessage(app.messageWnd, WM_DESTROY, 0, 0); + DestroyWindow(app.messageWnd); return result; } +static int exitThread(void * opaque) +{ + HANDLE handles[2] = { (HANDLE) opaque, app.exitThreadEvent }; + + switch (WaitForMultipleObjects(2, handles, FALSE, INFINITE)) + { + case WAIT_OBJECT_0: + DEBUG_INFO("Received exit event"); + SendMessage(app.messageWnd, WM_CLOSE, 0, 0); + break; + + case WAIT_OBJECT_0 + 1: + break; + + case WAIT_FAILED: + DEBUG_ERROR("WaitForMultipleObjects failed: 0x%lx", GetLastError()); + } + + return 0; +} + LRESULT sendAppMessage(UINT Msg, WPARAM wParam, LPARAM lParam) { return SendMessage(app.messageWnd, Msg, wParam, lParam); @@ -362,6 +385,13 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine .type = OPTION_TYPE_STRING, .value.x_string = logFilePath }, + { + .module = "os", + .name = "exitEvent", + .description = "Exit when the specified event is signaled", + .type = OPTION_TYPE_STRING, + .value.x_string = "" + }, {0} }; @@ -404,6 +434,8 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine app.messageWnd = CreateWindowEx(0, MAKEINTATOM(class), NULL, 0, 0, 0, 0, 0, NULL, NULL, hInstance, NULL); + app.exitThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + // this is needed so that unprivileged processes can send us this message _ChangeWindowMessageFilterEx = (PChangeWindowMessageFilterEx)GetProcAddress(user32, "ChangeWindowMessageFilterEx"); if (_ChangeWindowMessageFilterEx) @@ -449,12 +481,20 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine shutdown: DestroyMenu(app.trayMenu); app_shutdown(); + SetEvent(app.exitThreadEvent); + if (!lgJoinThread(thread, &result)) { DEBUG_ERROR("Failed to join the main application thread"); result = LG_HOST_EXIT_FAILED; } + if (app.exitThread && !lgJoinThread(app.exitThread, &result)) + { + DEBUG_ERROR("Failed to join the exit thread"); + result = LG_HOST_EXIT_FAILED; + } + finish: for(int i = 0; i < app.argc; ++i) @@ -518,6 +558,19 @@ bool app_init(void) // try to boost the scheduler priority boostPriority(); + // open exit signaling event + HANDLE exitEvent = NULL; + const char * exitEventName = option_get_string("os", "exitEvent"); + if (exitEventName && exitEventName[0]) + { + exitEvent = OpenEvent(SYNCHRONIZE, FALSE, exitEventName); + if (!exitEvent) + DEBUG_WARN("Failed to open exitEvent with error 0x%lx: %s", GetLastError(), exitEventName); + } + + if (exitEvent && !lgCreateThread("exitThread", exitThread, exitEvent, &app.exitThread)) + DEBUG_ERROR("Failed to create the exit thread"); + return true; } diff --git a/host/platform/Windows/src/service.c b/host/platform/Windows/src/service.c index 92b330ac..be6ef9db 100644 --- a/host/platform/Windows/src/service.c +++ b/host/platform/Windows/src/service.c @@ -50,6 +50,8 @@ struct Service FILE * logFile; bool running; HANDLE process; + HANDLE exitEvent; + char exitEventName[64]; }; struct Service service = { 0 }; @@ -311,10 +313,19 @@ void Launch(void) .lpDesktop = "WinSta0\\Default" }; + char * cmdline = NULL; + char cmdbuf[128]; + if (service.exitEvent) + { + snprintf(cmdbuf, sizeof(cmdbuf), "looking-glass-host.exe os:exitEvent=%s", + service.exitEventName); + cmdline = cmdbuf; + } + if (!f_CreateProcessAsUserA( hToken, os_getExecutable(), - NULL, + cmdline, NULL, NULL, FALSE, @@ -643,6 +654,21 @@ VOID WINAPI SvcMain(DWORD dwArgc, LPTSTR *lpszArgv) ReportSvcStatus(SERVICE_RUNNING, NO_ERROR, 0); + UUID uuid; + RPC_CSTR uuidStr; + UuidCreate(&uuid); + + if (UuidToString(&uuid, &uuidStr) == RPC_S_OK) + { + strcpy(service.exitEventName, "Global\\"); + strcat(service.exitEventName, (const char*) uuidStr); + RpcStringFree(&uuidStr); + + service.exitEvent = CreateEvent(NULL, FALSE, FALSE, service.exitEventName); + if (!service.exitEvent) + doLog("Failed to create exit event: 0x%lx\n", GetLastError()); + } + int failCount = 0; while(1) { @@ -736,14 +762,34 @@ VOID WINAPI SvcMain(DWORD dwArgc, LPTSTR *lpszArgv) stopped: if (service.running) { - doLog("Terminating the host application\n"); - if (TerminateProcess(service.process, LG_HOST_EXIT_KILLED)) + SetEvent(service.exitEvent); + switch (WaitForSingleObject(service.process, 1000)) { - while(WaitForSingleObject(service.process, INFINITE) != WAIT_OBJECT_0) {} - doLog("Host application terminated\n"); + case WAIT_OBJECT_0: + service.running = false; + break; + case WAIT_TIMEOUT: + doLog("Host application failed to exit in 1 second\n"); + break; + case WAIT_FAILED: + doLog("WaitForSingleObject failed: 0x%lx\n", GetLastError()); + break; } - else - doLog("Failed to terminate the host application\n"); + + if (service.running) + { + doLog("Terminating the host application\n"); + if (TerminateProcess(service.process, LG_HOST_EXIT_KILLED)) + { + if (WaitForSingleObject(service.process, INFINITE) == WAIT_OBJECT_0) + doLog("Host application terminated\n"); + else + doLog("WaitForSingleObject failed: 0x%lx\n", GetLastError()); + } + else + doLog("Failed to terminate the host application\n"); + } + CloseHandle(service.process); service.process = NULL; }