mirror of
https://github.com/gnif/LookingGlass.git
synced 2025-01-09 14:27:06 +00:00
[host] windows: use event to gracefully signal exit
This allows the process to be terminated without resorting to TerminateProcess. With some fixes, this allows the notification icon to be removed when the service is restarted. Furthermore, instead of sending WM_DESTROY to fool the window into believing it's being destroyed, we actually call DestroyWindow now.
This commit is contained in:
parent
a4f5ce08b9
commit
16ee1a825c
@ -28,6 +28,7 @@ target_link_libraries(platform_Windows
|
||||
psapi
|
||||
shlwapi
|
||||
powrprof
|
||||
rpcrt4
|
||||
)
|
||||
|
||||
target_include_directories(platform_Windows
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
@ -734,16 +760,36 @@ VOID WINAPI SvcMain(DWORD dwArgc, LPTSTR *lpszArgv)
|
||||
}
|
||||
|
||||
stopped:
|
||||
if (service.running)
|
||||
{
|
||||
SetEvent(service.exitEvent);
|
||||
switch (WaitForSingleObject(service.process, 1000))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
if (service.running)
|
||||
{
|
||||
doLog("Terminating the host application\n");
|
||||
if (TerminateProcess(service.process, LG_HOST_EXIT_KILLED))
|
||||
{
|
||||
while(WaitForSingleObject(service.process, INFINITE) != WAIT_OBJECT_0) {}
|
||||
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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user