Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CtrlHandler not called on event CTRL_CLOSE_EVENT in Windows Web Server 2008

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?

like image 595
Christopher Howlin Avatar asked Nov 25 '10 16:11

Christopher Howlin


1 Answers

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;
like image 92
bronekk Avatar answered Sep 17 '22 11:09

bronekk