7 Stimmen

CtrlHandler wird beim Ereignis CTRL_CLOSE_EVENT in Windows Web Server 2008 nicht aufgerufen

Ich habe eine Konsolenanwendung, die Folgendes verwendet SetConsoleCtrlHandler um einen Handler zu setzen, der die CTRL_CLOSE_EVENT . Der Handler gibt einfach zurück TRUE woraufhin ein Dialogfeld erscheint, in dem der Benutzer aufgefordert wird, das Herunterfahren fortzusetzen oder abzubrechen.

Die Software läuft unter Windows XP SP3 und Windows Web Server 2008 SP2.

Unter XP wird, wenn das "X" im Konsolenfenster angeklickt wird, mein Control-Handler aufgerufen und eine Eingabeaufforderung erscheint wie erwartet. Auf Server 2008 wird beim Schließen des Konsolenfensters mein Control-Handler nicht aufgerufen und die Anwendung wird ohne Eingabeaufforderung geschlossen.

Um zu überprüfen, ob der Control-Handler korrekt gesetzt wird, habe ich einen Fall für CTRL_C_EVENT . Ich kann sehen, wie der Code für Strg-C aufgerufen wird.

Gibt es Unterschiede in der Art und Weise, wie Abschlussereignisse in Server 2008 behandelt werden? Es scheint, wie sie nicht durch die ctrl-Handler überhaupt gehen.

EDIT: Ein Blick auf die MSDN-Seite für SetConsoleCtrlHandler Ich kann keine Informationen finden über CTRL_CLOSE_EVENT wird in Vista und späteren Versionen nicht mehr gehandhabt.

Wenn Sie mit Windows arbeiten ( HWND ) anstelle von Konsole ctrl-Ereignisse, ist es möglich, die Schließen-Nachrichten an das Konsolenfenster gesendet zu erhalten und behandeln, dass?

4voto

bronekk Punkte 2021

In meiner Konsolenanwendung (unter Windows 7) gehe ich folgendermaßen vor:

i. Erstellen Sie ein verstecktes Fenster, um auf die Benachrichtigung zum Schließen/Abmelden zu warten. Das ist wichtig: einen eigenen Thread für seine Nachrichtenschleife einrichten

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. Implementieren Sie die Behandlung von "speziellen Ereignissen" in Ihrem 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. Implementierung einer internen Funktion zur Behandlung von "Stop"-Anfragen. Sie muss 2 besondere Bedingungen erfüllen:

a. wenn no Aufruf vom Fenster-Thread, Senden von WM_CLOSE an das Fenster und Warten auf das Beenden des Threads

b. wenn no vom Haupt-Thread aufgerufen wird, auf die Beendigung der statischen Variablen (mindestens eine) warten.

Das liegt daran, dass Windows nach dem Beenden von CtrlHandler Ihren Prozess bedingungslos beendet, ohne Ihrem Code eine Chance zur Bereinigung zu geben. Statische Variablen werden im Haupt-Thread zerstört, so dass dieses Warten Ihnen zumindest die Garantie gibt, dass int main() verlassen hat. Sie können die Thread-ID des Hauptthreads im Konstruktor einer statischen Variablen erfassen (möglicherweise diejenige, die das "Schattenfenster" gestartet hat).

Hier ist, wie ich es in meinem Code gemacht habe:

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;

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X