Thread: Win32 console app shutdown issues

  1. #1
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607

    Win32 console app shutdown issues

    I'm trying to capture the close event for a console window. Currently the code uses SetConsoleCtrlHandler() which sets a handler that Windows calls when certain events happen.

    Big problem is that this spawns another thread according to the debugger. So if you call your shutdown function from the handler function, it will then return to main where you would also call shutdown (for a normal app termination).

    This means you are calling shutdown twice. I thought it was perfectly valid to call delete on NULL pointers and it's not bailing on that but it bails after the return 0 in main.

    I'm getting a DebugBreak() upon exiting the application in debug mode. No memory leaks or issues but it throws an exception. There is a small blurb in the docs about throwing an exception in debug mode but this applies to capturing CTRL-C which I'm not doing.

    Any ideas? I'm just trying to be a well-behaved app and clean up even if the user presses the close button on the console window.

    I'm seriously about to register my window handle with Windows to tap into the message loop scheme of things.
    Console apps are a pain.
    Last edited by VirtualAce; 12-18-2007 at 06:09 PM.

  2. #2
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Quote Originally Posted by Bubba View Post
    Big problem is that this spawns another thread according to the debugger. So if you call your shutdown function from the handler function, it will then return to main where you would also call shutdown (for a normal app termination).
    Well, that would make sense since it's not like a console app is sitting doing a message loop like a Windows app. The events must be passed through a different thread.

    I'm seriously about to register my window handle with Windows to tap into the message loop scheme of things.
    Console apps are a pain.
    Funny to hear that

    The solution that I find (right now, at least) is to use a global flag indicating (if true) that the app should terminate.
    Unless, of course, your app is stuck waiting for input somewhere? I suppose that might be a problem.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  3. #3
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    Go fullscreen! Then the user can't press the Close button.

    The solution that I find (right now, at least) is to use a global flag indicating (if true) that the app should terminate.
    Unless, of course, your app is stuck waiting for input somewhere? I suppose that might be a problem.
    That's basically the scope of it. From http://www.thescripts.com/forum/thread283269.html:
    When you click Close button, the default handler for the event calls ExitProcess,
    thus triggering MFC cleanup. The problem is that the system starts a new thread
    to call the handler, and therefore the cleanup is made on that thread, not on
    the main thread (which is killed by ExitProcess by that time).

    The solution is to register your own handler for Close, Ctrl-C and Ctrl-Break events
    (using SetConsoleCtrlHandler function), handle these events and ask your main thread
    to exit cleanly.
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  4. #4
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    A global flag will not work since if the flag is set in the main thread, it's not set in the control handler thread. I tried this and it did not work.

    When you click Close button, the default handler for the event calls ExitProcess,
    thus triggering MFC cleanup. The problem is that the system starts a new thread
    to call the handler, and therefore the cleanup is made on that thread, not on
    the main thread (which is killed by ExitProcess by that time).

    The solution is to register your own handler for Close, Ctrl-C and Ctrl-Break events
    (using SetConsoleCtrlHandler function), handle these events and ask your main thread
    to exit cleanly.
    I've done this and it's getting called twice.

    Code:
    MyObject *g_pObject;
    
    int main(...............)
    {
    
        //Init
        g_pObject =  new Object();
    
       //Do stuff
       g_pObject->DoStuff();
     
       //Shutdown
       Shutdown();
       return 0;
    
    }
    
    BOOL WINAPI CtrlHandler(DWORD ctrltype)
    {
       switch (ctrltype)
       {
           case CTRL_CLOSE_EVENT:
               Shutdown();
               return true; 
               break;
       }
    }
    
    void Shutdown()
    {
       //Cleanup
       delete g_pObject;
    }
    Ok on normal shut down, that is, when the app exits normally and is not forced by the user clicking the close button in the system menu Shutdown() gets called and all is well.

    But if the user closes this app, the handler gets called but is in a different thread of execution. So Shutdown() gets called and executes. But when Shutdown() returns, it will return to main immediately after DoStuff() was called. This is because the user shut down the app while we were inside DoStuff(). So now when Shutdown() returns, it returns just to call Shutdown() again. Setting a boolean inside of Shutdown() won't work because the boolean is stored locally to the main thread and the control thread.

    I'm not even sure if this is an error or is some type of behavior associated with a debug build and SetConsoleCtrlHandler(). Technically there is no reason for this to throw an exception.

    CodeGuru and CodeProject failed me on this one. And whats with Expert's Exchange? Only showing answers if you join their little clubby. No thx.
    Last edited by VirtualAce; 12-18-2007 at 06:51 PM.

  5. #5
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    You ought to be able to get away with something like this.
    Code:
    void shutdown() {
        static int did = 0;
        if(did) return;
        did = 1;
    
        //Cleanup
        delete g_pObject;
    }
    But that's just a hack, it doesn't explain why this happens or anything.
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  6. #6
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Nah I tried that with a static boolean. But it was in global scope so perhaps it will work like that.

    I set the boolean at the end of shutdown to true as in...already shutdown. But while debugging I found that when it got back to the main thread, the boolean was false. Very odd.

    Why couldn't MS just have made this like atexit() and use function pointers?? I may have to utilize the message loop or perhaps use atexit().

    But even if I'm calling shutdown twice this amounts to calling delete on NULL pointers which is perfectly valid. I'm still lost as to why I get the exception long after return 0.
    Last edited by VirtualAce; 12-18-2007 at 06:59 PM.

  7. #7
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Well, how about

    Code:
    	HWND hMyWindow = FindWindow(NULL, argv[0]);
    	HMENU hSysMenu = GetSystemMenu(hMyWindow, FALSE);
    	EnableMenuItem(hSysMenu, SC_CLOSE, MF_BYCOMMAND | MF_GRAYED);
    ?
    Close button is evil.
    Btw, why don't you set a data breakpoint, if you can, on the boolean to see where it changes or, optionally, if both booleans that you think are the same, have the same address?
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  8. #8
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    I cannot tell where it changes. I've set all the breakpoints I can to no avail. It's not that it gets reset...it's that it is a different variable in a different address space. Thread local storage is the only thing I can come up with as to why the two variables would not be the same.

  9. #9
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Then how about memory mapped files?
    Create a shared space that both "threads" can process.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  10. #10
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Yes and I thought of that but it's more of a hack.

    I found the problem. It was not my code at all but the net code I'd been given. Had a nice little memory leak and tried to delete a pointer twice, both times without setting it to NULL. So first time was ok but second time it pointed to...well...whatever and released it.

    I googled this problem at work today and this thread came up as the second hit. Cprog must be fairly popular with google.

  11. #11
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    Very. Many times, when googling for a specific problem, I find CBoard in the top few hits, and often the very first one.

    As someone pointed out, googling for "c programming" gets you cprogramming.com -- though that might be on account of the site's name.
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Adding a console window to a Win32 app
    By VirtualAce in forum Game Programming
    Replies: 7
    Last Post: 02-01-2009, 01:09 PM
  2. Simple Win32 console app using 99% of CPU?
    By c_olin3404 in forum C++ Programming
    Replies: 5
    Last Post: 02-17-2005, 03:04 PM
  3. win32 and consele app.
    By master2000 in forum C++ Programming
    Replies: 5
    Last Post: 12-22-2002, 11:50 AM
  4. Is it possible to get a console up with a win32 app?
    By SilentStrike in forum Windows Programming
    Replies: 2
    Last Post: 12-18-2001, 05:15 PM
  5. Turning a Console APP into Windows.
    By Darkflame in forum C++ Programming
    Replies: 3
    Last Post: 09-14-2001, 03:10 AM