CtrlHandler не вызывается для события CTRL_CLOSE_EVENT в Windows Web Server 2008 - PullRequest
6 голосов
/ 25 ноября 2010

У меня есть консольное приложение, которое использует SetConsoleCtrlHandler для установки обработчика, который обрабатывает и CTRL_CLOSE_EVENT.Обработчик просто возвращает TRUE, что приведет к появлению диалогового окна и предложению пользователю продолжить выключение или отмену.

Программное обеспечение работает на Windows XP SP3 и Windows Web Server 2008 SP2.

В XP, когда в окне консоли щелкается «X», вызывается мой обработчик управления и появляется сообщение, как и ожидалось.На сервере 2008 закрытие окна консоли не вызывает мой обработчик элемента управления, и приложение закрывается без запроса.

Чтобы проверить правильность установки обработчика элемента управления, я добавил регистр для CTRL_C_EVENT.Я вижу, как код вызывается для Ctrl-C.

Существуют ли различия в способе обработки событий закрытия в Server 2008?Кажется, что они вообще не проходят через обработчики ctrl.

РЕДАКТИРОВАТЬ: Глядя на страницу MSDN для SetConsoleCtrlHandler Я больше не могу найти информацию о CTRL_CLOSE_EVENTобрабатываются в Vista и более поздних версиях.

Если вы имеете дело с окнами (HWND) вместо событий консоли Ctrl, возможно ли получить сообщения закрытия, отправленные в окно консоли, и обработать это?

1 Ответ

3 голосов
/ 22 января 2012

Вот что я делаю в своем консольном приложении (работающем в Windows 7):

я. Создайте скрытое окно для ожидания уведомления о закрытии / выходе. Важное замечание: назначить собственный поток для цикла сообщений

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. Реализуйте обработку «специальных событий» в вашем обработчике сообщений окна

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. Реализовать внутреннюю функцию для обработки «стоп» запросов. Он должен обрабатывать 2 специальных условия:

а. когда не вызывается из потока сообщений окна, отправьте WM_CLOSE в окно и дождитесь выхода его потока

б. когда не вызывается из основного потока, дождитесь завершения статических переменных (хотя бы одной).

Это потому, что после выхода из CtrlHandler Windows безоговорочно прервет ваш процесс, не предоставляя вашему коду никакой возможности для очистки. Статические переменные уничтожаются в главном потоке, поэтому это ожидание дает вам хотя бы гарантию, что int main() завершился. Вы можете захватить идентификатор потока основного потока в конструкторе статической переменной (возможно, той же самой, которая запустила «теневое» окно).

Вот как я это сделал в своем коде:

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;
...