From ac1e206278b98fbe762f4b554803c64e8b562156 Mon Sep 17 00:00:00 2001 From: Marc Hoersken Date: Sun, 12 Apr 2020 18:38:12 +0200 Subject: tests/server: add hidden window to gracefully handle WM_CLOSE Forward Window events as signals to existing signal event handler. --- tests/server/util.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) (limited to 'tests') diff --git a/tests/server/util.c b/tests/server/util.c index c8bc32945..65d491c0b 100644 --- a/tests/server/util.c +++ b/tests/server/util.c @@ -544,6 +544,12 @@ static SIGHANDLER_T old_sigterm_handler = SIG_ERR; static SIGHANDLER_T old_sigbreak_handler = SIG_ERR; #endif +#ifdef WIN32 +static DWORD thread_main_id = 0; +static HANDLE thread_main_window = NULL; +static HWND hidden_main_window = NULL; +#endif + /* var which if set indicates that the program should finish execution */ volatile int got_exit_signal = 0; @@ -606,6 +612,78 @@ static BOOL WINAPI ctrl_event_handler(DWORD dwCtrlType) } return TRUE; } +/* Window message handler for Windows applications to add support + * for graceful process termination via taskkill (without /f) which + * sends WM_CLOSE to all Windows of a process (even hidden ones). + * + * Therefore we create and run a hidden Window in a separate thread + * to receive and handle the WM_CLOSE message as SIGTERM signal. + */ +static LRESULT CALLBACK main_window_proc(HWND hwnd, UINT uMsg, + WPARAM wParam, LPARAM lParam) +{ + int signum = 0; + if(hwnd == hidden_main_window) { + switch(uMsg) { +#ifdef SIGTERM + case WM_CLOSE: signum = SIGTERM; break; +#endif + case WM_DESTROY: PostQuitMessage(0); break; + } + if(signum) { + logmsg("main_window_proc: %d -> %d", uMsg, signum); + exit_signal_handler(signum); + } + } + return DefWindowProc(hwnd, uMsg, wParam, lParam); +} +/* Window message queue loop for hidden main window, details see above. + */ +static DWORD WINAPI main_window_loop(LPVOID lpParameter) +{ + WNDCLASS wc; + BOOL ret; + MSG msg; + + ZeroMemory(&wc, sizeof(wc)); + wc.lpfnWndProc = (WNDPROC)main_window_proc; + wc.hInstance = (HINSTANCE)lpParameter; + wc.lpszClassName = "MainWClass"; + if(!RegisterClass(&wc)) { + perror("RegisterClass failed"); + return (DWORD)-1; + } + + hidden_main_window = CreateWindowEx(0, "MainWClass", "Recv WM_CLOSE msg", + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, + (HWND)NULL, (HMENU)NULL, + wc.hInstance, (LPVOID)NULL); + if(!hidden_main_window) { + perror("CreateWindowEx failed"); + return (DWORD)-1; + } + + do { + ret = GetMessage(&msg, NULL, 0, 0); + if(ret == -1) { + perror("GetMessage failed"); + return (DWORD)-1; + } + else if(ret) { + if(msg.message == WM_APP) { + DestroyWindow(hidden_main_window); + } + else if(msg.hwnd && !TranslateMessage(&msg)) { + DispatchMessage(&msg); + } + } + } while(ret); + + hidden_main_window = NULL; + return (DWORD)msg.wParam; +} #endif void install_signal_handlers(bool keep_sigalrm) @@ -659,6 +737,12 @@ void install_signal_handlers(bool keep_sigalrm) #ifdef WIN32 if(!SetConsoleCtrlHandler(ctrl_event_handler, TRUE)) logmsg("cannot install CTRL event handler"); + thread_main_window = CreateThread(NULL, 0, + &main_window_loop, + (LPVOID)GetModuleHandle(NULL), + 0, &thread_main_id); + if(!thread_main_window || !thread_main_id) + logmsg("cannot start main window loop"); #endif } @@ -694,5 +778,9 @@ void restore_signal_handlers(bool keep_sigalrm) #endif #ifdef WIN32 (void)SetConsoleCtrlHandler(ctrl_event_handler, FALSE); + if(thread_main_window && thread_main_id) { + if(PostThreadMessage(thread_main_id, WM_APP, 0, 0)) + (void)WaitForSingleObjectEx(thread_main_window, INFINITE, TRUE); + } #endif } -- cgit v1.2.3