2

I have created a console application which has a method that stores kinect stream to hard disk. I want to capture console exit event in order to close the kinect stream since I have issues when the console exit from close event. I want to find a way to detect the exit event of the console. I came across solutions like the following one. I add the following code to my app:

[DllImport("Kernel32")]
private static extern bool SetConsoleCtrlHandler(EventHandler handler, bool add);

private delegate bool EventHandler(CtrlType sig);
static EventHandler _handler;

enum CtrlType
    {
        CTRL_C_EVENT = 0,
        CTRL_BREAK_EVENT = 1,
        CTRL_CLOSE_EVENT = 2,
        CTRL_LOGOFF_EVENT = 5,
        CTRL_SHUTDOWN_EVENT = 6
} 

private static bool Handler(CtrlType sig)
{
   switch (sig)
   {
            case CtrlType.CTRL_C_EVENT:
            case CtrlType.CTRL_LOGOFF_EVENT:
            case CtrlType.CTRL_SHUTDOWN_EVENT:
            case CtrlType.CTRL_CLOSE_EVENT:
            default:
                return false;
    }
}

And in the main function:

  Program obj = new Program(dirPath + args[3] + "_" + args[4]);
  _handler += new EventHandler(Handler);
  SetConsoleCtrlHandler(_handler, true);

  Console.ReadLine();

I am wandering firstly if I can import handler event to a boolean variable and secondly how can I give it as an input to program constructor in order to close my stream.

EDIT: My program constructor:

public Program(string filePath)
    {

        Thread thread = new Thread(() => writeRGSStream(file));
        thread.Start();
        writeSkelFiles(file);

    }

I want to give the flag input inside the writeRGSStream method. Can I call

SetConsoleCtrlHandler(new HandlerRoutine(ConsoleCtrlCheck), true);

inside that method in order to get the flag value?

Community
  • 1
  • 1
Jose Ramon
  • 5,572
  • 25
  • 76
  • 152

3 Answers3

5

Look at this example, it works fine:

Main

private static bool isclosing = false;
        static void Main(string[] args)
        {
            SetConsoleCtrlHandler(new HandlerRoutine(ConsoleCtrlCheck), true);

            Console.WriteLine("CTRL+C,CTRL+BREAK or suppress the application to exit");
            while (!isclosing) ;

        }

Console check

private static bool ConsoleCtrlCheck(CtrlTypes ctrlType)
        {
            // Put your own handler here
            switch (ctrlType)
            {
                case CtrlTypes.CTRL_C_EVENT:
                    isclosing = true;
                    Console.WriteLine("CTRL+C received!");
                    break;

                case CtrlTypes.CTRL_BREAK_EVENT:
                    isclosing = true;
                    Console.WriteLine("CTRL+BREAK received!");
                    break;

                case CtrlTypes.CTRL_CLOSE_EVENT:
                    isclosing = true;
                    Console.WriteLine("Program being closed!");
                    break;

                case CtrlTypes.CTRL_LOGOFF_EVENT:
                case CtrlTypes.CTRL_SHUTDOWN_EVENT:
                    isclosing = true;
                    Console.WriteLine("User is logging off!");
                    break;

            }
            return true;
        }

And WinAPI part:

#region unmanaged
// Declare the SetConsoleCtrlHandler function
// as external and receiving a delegate.

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

// A delegate type to be used as the handler routine
// for SetConsoleCtrlHandler.
public delegate bool HandlerRoutine(CtrlTypes CtrlType);

// An enumerated type for the control messages
// sent to the handler routine.
public enum CtrlTypes
{
    CTRL_C_EVENT = 0,
    CTRL_BREAK_EVENT,
    CTRL_CLOSE_EVENT,
    CTRL_LOGOFF_EVENT = 5,
    CTRL_SHUTDOWN_EVENT
}

#endregion
Maxim Goncharuk
  • 1,285
  • 12
  • 20
  • At the second block there is a handler of all events, so you can put your code to one of those cases. – Maxim Goncharuk Dec 03 '15 at 14:06
  • Thus I can use isclosing as my desired flag? – Jose Ramon Dec 03 '15 at 14:28
  • No, `isClosing` is only for example. You can use switch `switch (ctrlType)` in method `ConsoleCtrlCheck` for choosing what situation you will going to handle. – Maxim Goncharuk Dec 03 '15 at 14:30
  • Sorry mate but I am totally confused. I just want to handle the close event from console. Basically the situation is a killing process from terminal – Jose Ramon Dec 03 '15 at 14:36
  • `case CtrlTypes.CTRL_CLOSE_EVENT:` this one catches killing process. – Maxim Goncharuk Dec 03 '15 at 14:39
  • don't use `SetConsoleCtrlHandler(new HandlerRoutine(ConsoleCtrlCheck), true);` because it will be garbage collected. use `static HandlerRoutine Handler;` in class and in `Main` use `Handler = new HandlerRoutine(ConsoleCtrlCheck);` and `SetConsoleCtrlHandler(Handler, true);` – Jinjinov Jun 20 '19 at 12:18
1
  return false;

This does not do what you hope it does. You cannot cancel a close request, the return value merely tells Windows whether it should call any other registered handlers. So whether you return true or false has no affect on the outcome, the OS will always terminate the program.

So anything you need to do must be done inside the callback method. Beware that you have very limited options. After all, your main thread is busy writing that file, you cannot delete it. Nor is it a real fix at all, you still have trouble if your app terminates for any other reason. Like an unhandled exception, the user killing it with Task Manager or somebody tripping over the power cord and unplugging the machine.

Just write smarter code, create the file with a temporary name and rename the file after you are done writing it. Now you never care that your program gets interrupted for any reason.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • Can you please elaborate more on your last suggestion? – Jose Ramon Dec 03 '15 at 15:02
  • Hard to elaborate. Simply first write to "tempfile.tmp", then use File.Replace() to rename it to "file.ext". You'll at worst leave a half-written tempfile.tmp, nobody is going to be interested in it. Another goody is that you'll never destroy any existing file. – Hans Passant Dec 03 '15 at 15:09
1

Use the CancelKeyPress event (fired on CTRL+C).

Console.CancelKeyPress += Console_CancelKeyPress;

do things before close here

private static void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e)
{
    Process.GetCurrentProcess().CloseMainWindow();
}

Hopefully this is what you tried to do.

bb441db
  • 276
  • 1
  • 3
  • 11