Question

I know that I have to use setconsolehandler() if I want to manage console closing events.

I do not know how to block the CTRL_CLOSE_EVENT. I've tried returning false/true if it catches that event, but no success

Here is what I have so far (thank you Anton Gogolev!)

[DllImport("Kernel32")]
public static extern bool SetConsoleCtrlHandler(HandlerRoutine Handler, bool Add);

public delegate bool HandlerRoutine(CtrlTypes CtrlType);

public enum CtrlTypes{
    CTRL_C_EVENT = 0,
    CTRL_BREAK_EVENT,
    CTRL_CLOSE_EVENT,
    CTRL_LOGOFF_EVENT = 5,
    CTRL_SHUTDOWN_EVENT
}

private static bool ConsoleCtrlCheck(CtrlTypes ctrlType)
{ 
    if(ctrlType == CtrlTypes.CTRL_CLOSE_EVENT)
        return false;// I have tried true and false and viceversa with the return   
                     // true/false but I cant seem to get it right.
    return true;
}


//and then I use this to call it
SetConsoleCtrlHandler(new HandlerRoutine(ConsoleCtrlCheck), true);

Also is it possible to run a new thread to monitor if the console is closing and block that close if the main thread is in the middle of doing something?

Was it helpful?

Solution

The documentation for SetConsoleCtrlHandler() says:

The system generates CTRL_CLOSE_EVENT, CTRL_LOGOFF_EVENT, and CTRL_SHUTDOWN_EVENT signals when the user closes the console, logs off, or shuts down the system so that the process has an opportunity to clean up before termination.

This implies that unlike when handling CTRL+C or CTRL+BREAK events, your process does not get the opportunity to cancel the close, logoff, or shutdown.

OTHER TIPS

Actually you can block it (I have reproduced this on Windows XP at least). For example if in your handler you have an endless while loop with a sleep, this will stop this process from terminating forever (or at least for a long time, or until the user kills the process through task manager).

If you really needed to start a thread, you could use a wait condition (AutoResetEvent in C#) and start your thread (though a new thread probably isn't needed in most cases) then notify the wait condition when your thread is finished. However, just doing any cleanup in the handler would suffice in most cases.

If in the worst case you did wait forever, the process will remain running and you'll be able to see it when you log back in (at least on Windows XP). However, this causes the desktop to pause for around 20 seconds before going to the log out screen (while it waits for your applicaiton to exit), and then pauses again at the log out screen (I suppose while it tries for a 2nd time). Of course, I strongly advise against waiting forever; for any long running stuff you should really put this in a service.

I found a possible 'hack' that could prevent the application from closing, whilst still neatly obeying the console close request. Particularly, this is suitable, I think, for GUI applications that have created a console as an 'extra'. From the MSDN documentation, at the point where the Ctrl Handler is called a new thread is created in the process to call the handler. My solution, then, is to kill the assaulting invading thread before the default handler can call ExitProcess. In the handler routine (code in C++):

// Detach Console:
FreeConsole();
// Prevent closing:
ExitThread(0);
return TRUE; // Not reached

EDIT: It appears this does cause some issues, as to be expected I suppose. A subsequent call to AllocConsole() hangs indefinitely, so I suspect that exiting the thread prematurely fails to clean up properly.

EDIT 2:

To clarify above, I have found no direct issues with continuing to run the program. But keep in mind that we have forcefully terminated a thread that kernel32 has created, so any resources inside kernel32 could be in an indeterminate state. This could cause unforeseen problems when the program continues to run.

Mostly, I think, these problems will be related to the Console API. Like mentioned, AllocConsole fails to work from this point onwards (it will hang the application), so the program can not open a new console. It's highly possible that other console functions will fail as well. Basically, anything you do from that point onwards that in any way (directly or indirectly) calls into kernel32 is subject to undefined behaviour, but I suspect that in practice there won't be any issues outside of the Console functions.

My conclusion is that you should avoid this method if at all possible, but if premature termination is worse, then this can be thought of as an emergency work-around, to be used sparingly and with care.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top