2010-11-25 42 views
6

J'ai une application console qui utilise SetConsoleCtrlHandler pour définir un gestionnaire qui gère et CTRL_CLOSE_EVENT. Le gestionnaire renvoie simplement TRUE, ce qui provoque l'apparition d'une boîte de dialogue et invite l'utilisateur à poursuivre l'arrêt ou l'annulation.CtrlHandler n'est pas appelé sur l'événement CTRL_CLOSE_EVENT dans Windows Web Server 2008

Le logiciel fonctionne sous Windows XP SP3 et Windows Web Server 2008 SP2. Sur XP, lorsque l'on clique sur 'X' dans la fenêtre de la console, mon gestionnaire de contrôle est appelé et une invite apparaît comme prévu. Sur Server 2008, la fermeture de la fenêtre de la console n'appelle pas mon gestionnaire de contrôle et l'application se ferme sans invite.

Pour vérifier que le gestionnaire de contrôle est réglé correctement, j'ai ajouté un cas pour CTRL_C_EVENT. Je peux voir que le code est appelé pour Ctrl-C.

Y a-t-il des différences dans la façon dont les événements de fermeture sont traités dans Server 2008? Il semble qu'ils ne passent pas par les gestionnaires de ctrl du tout.

EDIT: regardant la page MSDN pour SetConsoleCtrlHandler Je ne trouve aucune information sur CTRL_CLOSE_EVENT ne sont plus traitées dans Vista et versions ultérieures.

Si vous traitez avec Windows (HWND) au lieu d'événements console ctrl, est-il possible d'obtenir les messages de fermeture envoyés à la fenêtre de la console et gérer cela?

+0

Ouais, ce comportement a été changé dans Vista. Les programmes ne peuvent plus bloquer l'arrêt. –

+2

Alors, n'y a-t-il plus de notification? Est-il possible que les processus effectuent un nettoyage lorsqu'ils sont fermés comme ça? –

+0

Oui, cela a changé dans Vista (Server 2008); vous ne recevez plus d'événements de déconnexion. Pour ma propre application de console, j'ai créé une fenêtre cachée et elle reçoit à la fois des événements de fermeture et de fermeture de session. – bronekk

Répondre

3

Voici ce que je fais dans mon application console (fonctionnant sous Windows 7):

i. Créer une fenêtre cachée pour attendre la notification de fermeture/fermeture de session. Important: donner son propre fil pour sa boucle de message

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. Implémenter le traitement des "événements spéciaux" dans votre gestionnaire de messages de fenêtre

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. Implémenter une fonction interne pour gérer les requêtes "stop". Il doit gérer 2 conditions spéciales:

a. lorsque pas appelé à partir du fil de message fenêtre, envoyez WM_CLOSE à la fenêtre et attendez que son thread pour quitter

b. lorsque n'est pas appelé depuis le thread principal, attendez la fin des variables statiques (au moins une). En effet, après la fermeture de CtrlHandler, Windows mettra fin sans condition à votre processus sans donner à votre code de chance de nettoyage. Les variables statiques sont détruites sur le thread principal, donc cette attente vous donne au moins la garantie que int main() a quitté. Vous pouvez capturer l'identificateur de thread du thread principal dans le constructeur d'une variable statique (éventuellement la même que celle qui a démarré la fenêtre "shadow").

Voici comment je l'ai fait dans mon 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; 
+0

Est-ce que cela va être capable d'intercepter la fenêtre de la console de fermeture avec le bouton 'x', ce que demande OP? – Anonymous