none
Windows 7 console application has no way of trapping logoff\shutdown event? RRS feed

  • Question

  • I have a console application and on logoff/shutdown , it exits immediately. I wanted to do some cleanup before the exit, but it doesn't allow me.

    These are the methods i tried:
    atexit
    SetConsoleCtrlHandler
    SetUnhandledExceptionFilter
    signal
    creating a thread which creates an invisible window and has a wndproc

    All this didn't work. This problem is also reported at

    http://www.gamedev.net/community/forums/topic.asp?topic_id=560998
    http://social.msdn.microsoft.com/Forums/en-US/windowscompatibility/thread/2f1341c5-c807-462f-8fab-2711e0ff3d31/

    without any solution.

    I cannot make my program a service because its only one of the parts of a multi-process enterprise product.

    Here's the sample program code:-

    // TrapLogoffShutdown.cpp :
    //
    #include <stdio.h>
    #include <Windows.h>
    #include <Wincon.h>
    #include <winbase.h>
    #include<Tlhelp32.h>
    #include<signal.h>


    BOOL WINAPI ConsoleHandler ( DWORD cEvent )
    {
        FILE* fp=fopen("c:\\ajith\\ctrlhandler.txt","a+");
        switch(cEvent)
        {
       case CTRL_C_EVENT:
            MessageBox(NULL,
                "CTRL+C received!","CEvent",MB_OK);
            fprintf(fp,"CTRL+C received!\n");
            break;
        case CTRL_BREAK_EVENT:
            MessageBox(NULL,
                "CTRL+BREAK received!","CEvent",MB_OK);
            fprintf(fp,"CTRL+BREAK received!\n");
            break;
        case CTRL_CLOSE_EVENT:
            MessageBox(NULL,
                "Program being closed!","CEvent",MB_OK);
            fprintf(fp,"CTRL_CLOSE_EVENT received!\n");
            break;
        case CTRL_LOGOFF_EVENT:
            MessageBox(NULL,
                "User is logging off!","CEvent",MB_OK);
            fprintf(fp,"CTRL_LOGOFF_EVENT received!\n");
            break;
        case CTRL_SHUTDOWN_EVENT:
            MessageBox(NULL,
                "User is logging off!","CEvent",MB_OK);
            fprintf(fp,"CTRL_SHUTDOWN_EVENT received!\n");
            break;
        }
        fclose(fp);
        ExitProcess(-1);
        return TRUE;
    }

    void HandleSignals (int signal){
        FILE* fp=fopen("c:\\ajith\\handlesignals.txt","a+");
        switch(signal)
        {
    /*
    SIGABRT     Abnormal termination
    SIGFPE     Floating-point error
    SIGILL     Illegal instruction
    SIGINT     CTRL+C signal
    SIGSEGV     Illegal storage access
    SIGTERM     Termination request
    */
        case SIGABRT:
             MessageBox(NULL,"SIGABRT received!\n","Signal",MB_OK);
             fprintf(fp,"SIGABRT");
             break;
        case SIGFPE:
             MessageBox(NULL,"SIGFPE received!\n","Signal",MB_OK);
             fprintf(fp,"SIGFPE");
             break;
        case SIGILL:
            MessageBox(NULL,"SIGILL received!\n","Signal",MB_OK);
            fprintf(fp,"SIGILL");
             break;
        case SIGINT:
            MessageBox(NULL,"SIGINT received!\n","Signal",MB_OK);
            fprintf(fp,"SIGINT");
             break;
        case SIGSEGV:
            MessageBox(NULL,"SIGSEGV received!\n","Signal",MB_OK);
            fprintf(fp,"SIGSEGV");
             break;
        case SIGTERM:
            MessageBox(NULL,"SIGTERM received!\n","Signal",MB_OK);
            fprintf(fp,"SIGTERM");
             break;
        default:
            MessageBox(NULL,"default received!\n","Signal",MB_OK);
            fprintf(fp,"signal=%d",signal);
        }
        fclose(fp);
    }

    LONG myFunc(LPEXCEPTION_POINTERS p)
    {   
         FILE* fp=fopen("c:\\ajith\\unhandledexceptionfilter.txt","a+");
         fprintf(fp," UnhandledExceptionFilterHandler running \n");
         fclose(fp);
         return EXCEPTION_EXECUTE_HANDLER;
    }

    void DoesthisExecute()
    {
        FILE* fp=fopen("c:\\ajith\\atexit.txt","a+");
        fprintf(fp," Atexit running \n");
        fclose(fp);
    }

    LRESULT CALLBACK MainWndProc ( HWND hWnd , UINT msg , WPARAM wParam, LPARAM lParam )
    {
        FILE* fp=fopen("c:\\ajith\\mainwndproc.txt","a+");
        fprintf(fp,"Received window message %d \n",msg);
        switch(msg)
        {
        case WM_QUERYENDSESSION:
            MessageBox(NULL,"Received WM_QUERYENDSESSION","MainWndProc",MB_OK);
            fprintf(fp,"Received WM_QUERYENDSESSION\n");
            fclose(fp);
            return TRUE;
        case WM_ENDSESSION:
            MessageBox(NULL,"Received WM_ENDSESSION","MainWndProc",MB_OK);
            fprintf(fp,"Received WM_ENDSESSION\n");
            fclose(fp);
            return TRUE;
        default:
            break;
        }
        fclose(fp);
        return TRUE;
    }

    void CreateInvisibleWindow()
    {
        HINSTANCE hinstance=NULL;
        HWND hwnd;
        WNDCLASSEX wcx;
        wcx.cbSize=sizeof(wcx);
        wcx.style=CS_HREDRAW|CS_VREDRAW;
        wcx.lpfnWndProc=MainWndProc;
        wcx.cbClsExtra=0;
        wcx.cbWndExtra=0;
        wcx.hInstance=hinstance;
        wcx.hIcon=LoadIcon(NULL, IDI_APPLICATION);
        wcx.hCursor=LoadCursor(NULL,IDC_ARROW);
        wcx.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
        wcx.lpszMenuName="MainMenu";
        wcx.lpszClassName="MainWClass";
        wcx.hIconSm=LoadIcon(NULL, IDI_APPLICATION);   
        RegisterClassEx(&wcx);

        hwnd=CreateWindow("MainWClass","Sample",WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,(HWND) NULL, (HMENU) NULL, hinstance, (LPVOID) NULL);
        if(!hwnd)
            printf("FAILED to create window!!!\n");
    }

    DWORD WINAPI RunInvisibleWindowThread(LPVOID lpParam)
    {
        MSG msg;
        CreateInvisibleWindow();
        BOOL fGotMessage;
        while ( (fGotMessage = GetMessage(&msg,(HWND) NULL , 0 , 0))!=0 && fGotMessage!=-1 )
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        return 0;
    }

    int main(int argc,char* argv)
    {
        printf("Enabling SetConsoleCtrlHandler \n");

        if( SetConsoleCtrlHandler((PHANDLER_ROUTINE)ConsoleHandler,TRUE) == FALSE )
        {
            printf("Enabling SetConsoleCtrlHandler FAILED\n");
            return -1;
        }
        printf("Enabling signal handler \n");
        signal(SIGABRT, HandleSignals);
        signal(SIGFPE, HandleSignals);
        signal(SIGILL, HandleSignals);
        signal(SIGINT, HandleSignals);
        signal(SIGSEGV, HandleSignals);
        signal(SIGTERM, HandleSignals);
        signal(SIGSEGV, HandleSignals);
        printf("Setting unhandledexceptionfilter !!\n");
        SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)&myFunc);
        printf("setting atexit \n");
        atexit(DoesthisExecute);


        DWORD tid;
        HANDLE hInvisiblethread=CreateThread(NULL, 4*1024*1024, RunInvisibleWindowThread, NULL, 0, &tid);
        CloseHandle(hInvisiblethread);

        while(TRUE)
        {
            printf(" Iam running ..whoa!!\n");
            Sleep(3000);
        }
    }




    Please reply to me at **************
    Friday, February 19, 2010 4:13 PM

Answers

  • Like Martyn Davis said, you need to create an invisible window. Here's sample code

    / TrapLogoffShutdown.cpp : Defines the entry point for the console application.

    //

    #include <stdio.h>

    #include <Windows.h>

     

    LRESULT CALLBACK MainWndProc ( HWND hwnd , UINT msg , WPARAM wParam, LPARAM lParam )

    {

    FILE* fp=fopen("c:\\ajith\\mainwndproc.txt","a+");

    fprintf(fp,"Received window message %d \n",msg);

    switch(msg)

    {

    case WM_QUERYENDSESSION:

    MessageBox(NULL,"Received WM_QUERYENDSESSION","MainWndProc",MB_OK);

    fprintf(fp,"Received WM_QUERYENDSESSION\n");

    fclose(fp);

    return TRUE;

     

    case WM_ENDSESSION:

    MessageBox(NULL,"Received WM_ENDSESSION","MainWndProc",MB_OK);

    fprintf(fp,"Received WM_ENDSESSION\n");

    fclose(fp);

    {

    if (wParam)

    MessageBox(hwnd, "ConsoleWaWindow", "WM_ENDSESSION at any TIME!!", MB_OK);

    else

    MessageBox(hwnd, "ConsoleWaWindow", "WM_ENDSESSION aNO!!", MB_OK);

     

    }

    return TRUE;

     

    case WM_DESTROY:

    {

    PostQuitMessage(0);

    }

    break;

     

    case WM_CLOSE:

    fprintf(fp,"WM_CLOSE received\n");

    break;

    default:

    return DefWindowProc(hwnd, msg, wParam, lParam);

    break;

    }

    fclose(fp);

    return TRUE;

    }

     

    void CreateInvisibleWindow()

    {

    HWND hwnd;

    WNDCLASS wc={0};

    wc.lpfnWndProc=(WNDPROC)MainWndProc;

    wc.hInstance=GetModuleHandle(NULL);

    wc.hIcon=LoadIcon(GetModuleHandle(NULL), "TestWClass");

    wc.lpszClassName="TestWClass";

    RegisterClass(&wc);

     

    hwnd=CreateWindowEx(0,"TestWClass","TestWClass",WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,(HWND) NULL, (HMENU) NULL, GetModuleHandle(NULL), (LPVOID) NULL);

    if(!hwnd)

    printf("FAILED to create window!!!  %d\n",GetLastError());

    }

     

    DWORD WINAPI RunInvisibleWindowThread(LPVOID lpParam)

    {

    MSG msg;

    CreateInvisibleWindow();

    printf(" Thread and window created ..whoa!!\n");

    while (GetMessage(&msg,(HWND) NULL , 0 , 0))

    {

    TranslateMessage(&msg);

    DispatchMessage(&msg);

    }

    return 0;

    }

     

    int main(int argc,char* argv)

    {

     

    DWORD tid;

    HANDLE hInvisiblethread=CreateThread(NULL, 0, RunInvisibleWindowThread, NULL, 0, &tid);

     

    while(TRUE)

    {

    printf(" Iam running ..whoa!!\n");

    Sleep(3000);

    }

     

    printf("Finished\n");

    CloseHandle(hInvisiblethread);

    }

    • Marked as answer by Ajith T Pillai Tuesday, November 22, 2011 12:49 PM
    Tuesday, November 22, 2011 12:47 PM

All replies

  • Hello 

    This a quick note to let you know that I am performing research on this issue and will get back to you as soon as possible. I appreciate your patience.

    Thanks,
    Rong-Chun Zhang

    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
    Monday, February 22, 2010 11:52 AM
  • Hello,

    "The CTRL_LOGOFF_EVENTsignal is received only by services. Interactive applications are terminated at logoff, so they are not present when the system sends this signal."


    Thanks,
    Rong-Chun Zhang


    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
    Tuesday, February 23, 2010 11:45 AM
  • CTRL_LOGOFF_EVENT was just one of the aspect. I tried each of the other methods

    atexit
    SetUnhandledExceptionFilter
    signal
    creating a thread which creates an invisible window and has a wndproc

    one by one, and none of them works. This could break all console applications which had some cleanup/wind up code during shutdown or logoff.

    Can you please provide me some workaround? And also mail me in case I miss checking the forum?
    Tuesday, March 2, 2010 8:37 AM
  • Hello

    Please try to using Session notifications instead.

    DWORD WINAPI ServiceCtrlEx(DWORD dwCtrlCode, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext)
    {
          DWORD SessionId = 0;
          PWTSSESSION_NOTIFICATION PSN = NULL;

          switch(dwCtrlCode)
          {
                // try to stop
                case SERVICE_CONTROL_STOP:
                      ssStatus.dwCurrentState = SERVICE_STOP_PENDING;
                      USBWatchSvcStop();
                      break;

                // status
                case SERVICE_CONTROL_INTERROGATE:
                      break;

                // SessionChange
                case SERVICE_CONTROL_SESSIONCHANGE:
                
                      PSN = (PWTSSESSION_NOTIFICATION) lpEventData;
                      SessionId = PSN->dwSessionId;

                      switch(dwEventType)
                      {
                            case WTS_SESSION_LOGON:
                                  OutputDebugString(TEXT("\nMySvc: Message WTS_SESSION_LOGON arrived."));
                                  break;
                            
                            case WTS_SESSION_LOGOFF:
                                  OutputDebugString(TEXT("\nMySvc: Message WTS_SESSION_LOGOFF arrived."));
                                  break;

                            case WTS_CONSOLE_CONNECT:
                                  OutputDebugString(TEXT("\n nMySvc -> A session was connected to the cons terminal"));
                                  break;

                            case WTS_CONSOLE_DISCONNECT:
                                  OutputDebugString(TEXT("\nA session was disconnected from the console terminal"));
                                  break;

                            case WTS_REMOTE_CONNECT:
                                  OutputDebugString(TEXT("\nA session was connected to the remote terminal"));
                                  LogLine(TEXT("USBWatchSvcCtrlEx"), _T("Message WTS_CONSOLE_DISCONNECT arrived"));
                                  break;

                            case WTS_REMOTE_DISCONNECT:
                                  OutputDebugString(TEXT("\nA session was disconnected to the remote terminal"));
                                  LogLine(TEXT("USBWatchSvcCtrlEx"), _T("Message WTS_REMOTE_CONNECT arrived"));
                                  break;

                            case WTS_SESSION_LOCK:
                                  OutputDebugString(TEXT("\nA session has been locked"));
                                  LogLine(TEXT("USBWatchSvcCtrlEx"), _T("Message WTS_SESSION_LOCK arrived"));
                                  break;

                            case WTS_SESSION_UNLOCK:
                                  OutputDebugString(TEXT("\nA session has been unlocked"));
                                  LogLine(TEXT("USBWatchSvcCtrlEx"), _T("Message WTS_SESSION_UNLOCK arrived"));
                                  break;

                            case WTS_SESSION_REMOTE_CONTROL:
                                  OutputDebugString(TEXT("\nA session has changed its remote controlled status"));
                                  LogLine(TEXT("USBWatchSvcCtrlEx"), _T("Message WTS_SESSION_REMOTE_CONTROL arrived"));
                                  break;

                            default:
                                  break;
                      }
                      break;

                default:
                      break;
          }

    Thanks,
    Rong-Chun Zhang

    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
    Wednesday, March 3, 2010 1:32 PM
  • Does this work if its not a service? But just a console application? I had noted above that the program couldn't be converted into a service. 
    Couldn't find any documentation of ServiceCtrlEx.
    Friday, March 5, 2010 12:03 PM
  • That case create a service and do conversation by IPC. But either in this case I m not sure that your console application wont be forcibly closed at time of logoff before your polling service can send information about it.
    Thursday, June 10, 2010 10:17 AM
  • This is the bahaviour of console applications using SetConsoleCtrlHandler() callback to receive logoff notifications:

    Windows XP - CTRL_LOGOFF_EVENT received and Windows will wait for the application to exit (with the user's blessing) no matter how long this takes.

    Windows Vista - CTRL_LOGOFF_EVENT received with Windows terminating the application if it does not exit within 5 seconds (approx).

    Windows 7 - Application is terminated - SetConsoleCtrlHandler() callback is not invoked.

    Surely there must be an alternate solution to allowing task based console applications which require some sort of cleanup during orderly shutdowns (logoff and system shutdown).  Not everything can be done in a service.

    Legacy code is breaking due to this change.

    Wednesday, October 20, 2010 7:51 PM
  • I posted a similar question at http://social.msdn.microsoft.com/Forums/en-US/windowsgeneraldevelopmentissues/thread/c47e7163-2c43-4669-addc-4b7232eee712.

     

    So far it looks hopeless. Either Microsoft deliberately made console/windowless applications so they shouldn't get an opportunity to clean up after themselves on logoff/shutdown, or it's some kind of a system design flaw. Both theories sound incredible to me.


    Al
    Sunday, October 24, 2010 11:33 AM
  • The only solution I can fathom is to convert the console application into a windows based application which has a hidden window which can respond to WM_ENDSESSION, which from my testing, allows up to 5 seconds of processing before Windows terminates the process.

    You also have the option of displaying some UI so that Windows will not terminate the application without the express approval of the user.

    NOTE: This is for a process which must be run within the user context and not as a service.

    Monday, October 25, 2010 3:35 PM
  • Hi Rong-Chun Zhang,

     

    Can you please post the complete code?

     

    Thanks


    Regards, http://tnvbalaji.wordpress.com tnvbalaji http://in.linkedin.com/in/tnvbalaji
    Friday, August 19, 2011 6:58 PM
  • Hi,

     

    I got bitten by it recently in Windows 7 and posted a query - http://social.msdn.microsoft.com/Forums/en-US/vcgeneral/thread/189cae8b-4c93-4cf2-9585-a57878f3a032. Then I came across your thread. Did you get any solution to it to deal with your console app? Can you please respond?

     

    We use at lot of places to capture CTRL_CLOSE_EVENT, CTRL_LOGOFF_EVENT, CTRL_SHUTDOWN_EVENT and I think none of them would work in window 7 and need a change. I don't see any reference mentioning about the non support for these events in MSDN for windows 7. Do you also agree?

     

    -Karthik

     

     


    Kartlee
    Thursday, November 17, 2011 3:49 PM
  • Like Martyn Davis said, you need to create an invisible window. Here's sample code

    / TrapLogoffShutdown.cpp : Defines the entry point for the console application.

    //

    #include <stdio.h>

    #include <Windows.h>

     

    LRESULT CALLBACK MainWndProc ( HWND hwnd , UINT msg , WPARAM wParam, LPARAM lParam )

    {

    FILE* fp=fopen("c:\\ajith\\mainwndproc.txt","a+");

    fprintf(fp,"Received window message %d \n",msg);

    switch(msg)

    {

    case WM_QUERYENDSESSION:

    MessageBox(NULL,"Received WM_QUERYENDSESSION","MainWndProc",MB_OK);

    fprintf(fp,"Received WM_QUERYENDSESSION\n");

    fclose(fp);

    return TRUE;

     

    case WM_ENDSESSION:

    MessageBox(NULL,"Received WM_ENDSESSION","MainWndProc",MB_OK);

    fprintf(fp,"Received WM_ENDSESSION\n");

    fclose(fp);

    {

    if (wParam)

    MessageBox(hwnd, "ConsoleWaWindow", "WM_ENDSESSION at any TIME!!", MB_OK);

    else

    MessageBox(hwnd, "ConsoleWaWindow", "WM_ENDSESSION aNO!!", MB_OK);

     

    }

    return TRUE;

     

    case WM_DESTROY:

    {

    PostQuitMessage(0);

    }

    break;

     

    case WM_CLOSE:

    fprintf(fp,"WM_CLOSE received\n");

    break;

    default:

    return DefWindowProc(hwnd, msg, wParam, lParam);

    break;

    }

    fclose(fp);

    return TRUE;

    }

     

    void CreateInvisibleWindow()

    {

    HWND hwnd;

    WNDCLASS wc={0};

    wc.lpfnWndProc=(WNDPROC)MainWndProc;

    wc.hInstance=GetModuleHandle(NULL);

    wc.hIcon=LoadIcon(GetModuleHandle(NULL), "TestWClass");

    wc.lpszClassName="TestWClass";

    RegisterClass(&wc);

     

    hwnd=CreateWindowEx(0,"TestWClass","TestWClass",WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,(HWND) NULL, (HMENU) NULL, GetModuleHandle(NULL), (LPVOID) NULL);

    if(!hwnd)

    printf("FAILED to create window!!!  %d\n",GetLastError());

    }

     

    DWORD WINAPI RunInvisibleWindowThread(LPVOID lpParam)

    {

    MSG msg;

    CreateInvisibleWindow();

    printf(" Thread and window created ..whoa!!\n");

    while (GetMessage(&msg,(HWND) NULL , 0 , 0))

    {

    TranslateMessage(&msg);

    DispatchMessage(&msg);

    }

    return 0;

    }

     

    int main(int argc,char* argv)

    {

     

    DWORD tid;

    HANDLE hInvisiblethread=CreateThread(NULL, 0, RunInvisibleWindowThread, NULL, 0, &tid);

     

    while(TRUE)

    {

    printf(" Iam running ..whoa!!\n");

    Sleep(3000);

    }

     

    printf("Finished\n");

    CloseHandle(hInvisiblethread);

    }

    • Marked as answer by Ajith T Pillai Tuesday, November 22, 2011 12:49 PM
    Tuesday, November 22, 2011 12:47 PM
  • Personally I think MS is intentionally trying to kill off any non-GUI applications.

    With a single change to their core code they destroyed the viability of all previously written console applications as well making it extremely difficult or at very least make it overly complex for any to be created in the future.

    Wednesday, September 11, 2013 8:26 PM
  • http://stackoverflow.com/a/11285839/149482
    Wednesday, April 9, 2014 2:46 AM
  • You are importing user32 aren't you? If so, don't do that, and the CTRL_LOGOFF_EVENT/CTRL_SHUTDOWN_EVENT will work.
    Wednesday, April 9, 2014 8:08 PM
  • You are importing user32 aren't you? If so, don't do that, and the CTRL_LOGOFF_EVENT/CTRL_SHUTDOWN_EVENT will work.

    That looks to me too as the likely secret that people above have missed in their hopes of seeing CTRL_LOGOFF_EVENT in their console applications. Yet even if the documentation, also quoted above, spelled out that importing from USER32 is what's meant by "interactive applications", so that people at least could understand the design straightaway, the design still would strike many as unhelpful.

    It's all well and good to avoid using USER32 functions in code you write, but the more that your console application does real work the more you're exposed to picking up USER32 from other people's code. For instance, if your console application uses sockets, then prepare to go without CTRL_LOGOFF_EVENT: both WS2_32.DLL and MSWSOCK.DLL can import from USER32.

    Thursday, April 10, 2014 5:51 PM