I have a console application which uses SetConsoleCtrlHandler
to set a handler which handles and CTRL_CLOSE_EVENT
. The handler simply returns TRUE
which will cause a dialog box to appear and prompt the user to continue shutdown or cancel.
The software runs on Windows XP SP3 and Windows Web Server 2008 SP2.
On XP, when the 'X' on the console window is clicked, my control handler gets called and a prompt appears as expected. On Server 2008 closing the console window does not call my control handler and the application closes down without prompting.
To check that the control handler is being set correctly I have added a case for CTRL_C_EVENT
. I can see the code get called for Ctrl-C.
Are there any differences in the way close events are handled in Server 2008? It seems like they do not go through the ctrl handlers at all.
EDIT: Looking at the MSDN page for SetConsoleCtrlHandler
I can't find any information about CTRL_CLOSE_EVENT
no longer being handled in Vista and later.
If you are dealing with windows (HWND
) instead of console ctrl events, is it possible to get the close messages sent to the console window and handle that?
Here is what I do in my console application (running on Windows 7):
i. Create hidden window to wait for close/logoff notification. Important: give it own thread for its message loop
void interrupt::start()
{
WNDCLASSEX wc = {};
HINSTANCE hi = GetModuleHandle(NULL);
wc.cbSize = sizeof(WNDCLASSEX);
wc.lpfnWndProc = WndProc;
// . . . etc
if(!RegisterClassEx(&wc))
return;
hwnd_ = CreateWindowEx(WS_EX_CLIENTEDGE, class_name, "Waiting for user logoff event",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 240, 120, NULL, NULL, hi , NULL);
ShowWindow(hwnd_, SW_HIDE);
UpdateWindow(hwnd_);
MSG msg = {};
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// call internal function for sending "stop" notification to rest of program
ctrl.stop(CTRL_CLOSE_EVENT);
}
ii. Implement handling of "special events" in your window message handler
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_ENDSESSION:
if (lParam)
{
// call internal function for sending "stop" notification to rest of program
ctrl.stop(lParam == ENDSESSION_LOGOFF ? (int) interrupt::logoff : CTRL_CLOSE_EVENT);
}
break;
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
iii. Implement internal function for handling "stop" requests. It must handle 2 special conditions:
a. when not called from window message thread, send WM_CLOSE to window and wait for its thread to exit
b. when not called from main thread, wait for termination of static variables (at least one).
This is because after CtrlHandler exit, Windows will unconditionally terminate your process without giving your code any chance for cleanup. Static variables are destroyed on main thread, so this wait gives you at least guarantee that int main()
has exited. You can capture thread id of main thread in the constructor of a static variable (possibly the same which started "shadow" window).
Here is how I did it in my code:
void interrupt::stop(int signal)
{
// . . .
// Set exit signal
InterlockedExchange(&stage_, 2L);
// Close shadow window if notification is from elsewhere
if (hwnd_ && GetCurrentThreadId() != thread_.id())
{
PostMessage(hwnd_, WM_CLOSE, 0, 0);
thread_.wait();
}
// Wait for completion of own destructor on main thread
if (GetCurrentThreadId() != main_thread_id_)
while(stage_ != 3L)
Sleep(10);
}
// My static variable to wait for
interrupt ctrl;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With