Programing

콘솔 종료 C # 캡처

crosscheck 2020. 9. 10. 07:15
반응형

콘솔 종료 C # 캡처


꽤 많은 스레드가 포함 된 콘솔 응용 프로그램이 있습니다. 특정 조건을 모니터링하고 참일 경우 프로그램을 종료하는 스레드가 있습니다. 이 종료는 언제든지 발생할 수 있습니다.

다른 모든 스레드를 정리하고 모든 파일 핸들과 연결을 제대로 닫을 수 있도록 프로그램이 닫힐 때 트리거 될 수있는 이벤트가 필요합니다. .NET 프레임 워크에 이미 빌드 된 것이 있는지 확실하지 않으므로 직접 작성하기 전에 묻습니다.

다음과 같은 이벤트가 있는지 궁금합니다.

MyConsoleProgram.OnExit += CleanupBeforeExit;

웹에서 코드를 어디에서 찾았는지 잘 모르겠지만 지금은 이전 프로젝트 중 하나에서 찾았습니다. 이렇게하면 콘솔에서 정리 코드를 수행 할 수 있습니다. 예를 들어 갑자기 닫히거나 종료로 인해 ...

[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;
  }
}


static void Main(string[] args)
{
  // Some biolerplate to react to close window event
  _handler += new EventHandler(Handler);
  SetConsoleCtrlHandler(_handler, true);
  ...
}

최신 정보

의견을 확인하지 않는 사람들에게는이 특정 솔루션이 Windows 7 에서 잘 작동 하지 않는 것 같습니다 (또는 전혀) . 다음 스레드 는 이것에 대해 이야기합니다.


전체 작업 예제는 ctrl-c로 작업하고 X로 창을 닫고 종료합니다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;

namespace TestTrapCtrlC {
    public class Program {
        static bool exitSystem = false;

        #region Trap application termination
        [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) {
            Console.WriteLine("Exiting system due to external CTRL-C, or process kill, or shutdown");

            //do your cleanup here
            Thread.Sleep(5000); //simulate some cleanup delay

            Console.WriteLine("Cleanup complete");

            //allow main to run off
            exitSystem = true;

            //shutdown right away so there are no lingering threads
            Environment.Exit(-1);

            return true;
        }
        #endregion

        static void Main(string[] args) {
            // Some boilerplate to react to close window event, CTRL-C, kill, etc
            _handler += new EventHandler(Handler);
            SetConsoleCtrlHandler(_handler, true);

            //start your multi threaded program here
            Program p = new Program();
            p.Start();

            //hold the console so it doesn’t run off the end
            while (!exitSystem) {
                Thread.Sleep(500);
            }
        }

        public void Start() {
            // start a thread and start doing some processing
            Console.WriteLine("Thread started, processing..");
        }
    }
}

또한 확인 :

AppDomain.CurrentDomain.ProcessExit

스레드가 응용 프로그램을 직접 종료하는 것 같습니까? 애플리케이션이 종료되어야한다는 것을 알리기 위해 스레드가 메인 스레드에 신호를 보내는 것이 더 낫습니다.

이 신호를 받으면 메인 스레드는 다른 스레드를 완전히 종료하고 마지막으로 자신을 종료 할 수 있습니다.


비슷한 문제가 있었는데, 내 콘솔 앱이 중간에 하나의 선점 문이있는 무한 루프에서 실행될 것입니다. 내 해결책은 다음과 같습니다.

class Program
{
    static int Main(string[] args)
    {
        // Init Code...
        Console.CancelKeyPress += Console_CancelKeyPress;  // Register the function to cancel event

        // I do my stuffs

        while ( true )
        {
            // Code ....
            SomePreemptiveCall();  // The loop stucks here wating function to return
            // Code ...
        }
        return 0;  // Never comes here, but...
    }

    static void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e)
    {
        Console.WriteLine("Exiting");
        // Termitate what I have to terminate
        Environment.Exit(-1);
    }
}

WinForms 앱이 있습니다.

Application.ApplicationExit += CleanupBeforeExit;

콘솔 앱의 경우

AppDomain.CurrentDomain.DomainUnload += CleanupBeforeExit;

그러나 어떤 시점에서 호출되는지 또는 현재 도메인 내에서 작동하는지 확실하지 않습니다. 나는 그렇지 않다고 생각한다.


ZeroKelvin의 답변은 Windows 10 x64, .NET 4.6 콘솔 앱에서 작동합니다. CtrlType 열거 형을 다룰 필요가없는 사람들을 위해 다음은 프레임 워크의 종료에 연결하는 매우 간단한 방법입니다.

class Program
{
    private delegate bool ConsoleCtrlHandlerDelegate(int sig);

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

    static ConsoleCtrlHandlerDelegate _consoleCtrlHandler;

    static void Main(string[] args)
    {
        _consoleCtrlHandler += s =>
        {
            //DoCustomShutdownStuff();
            return false;   
        };
        SetConsoleCtrlHandler(_consoleCtrlHandler, true);
    }
}

핸들러에서 FALSE를 반환하면 프레임 워크에 제어 신호를 "처리"하지 않고 있으며이 프로세스의 핸들러 목록에있는 다음 핸들러 함수가 사용됩니다. TRUE를 반환하는 처리기가 없으면 기본 처리기가 호출됩니다.

사용자가 로그 오프 또는 종료를 수행하면 콜백이 Windows에서 호출되지 않고 대신 즉시 종료됩니다.


Visual Studio 2015 + Windows 10

  • 정리 허용
  • 단일 인스턴스 앱
  • 일부 금도금

암호:

using System;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;

namespace YourNamespace
{
    class Program
    {
        // if you want to allow only one instance otherwise remove the next line
        static Mutex mutex = new Mutex(false, "YOURGUID-YOURGUID-YOURGUID-YO");

        static ManualResetEvent run = new ManualResetEvent(true);

        [DllImport("Kernel32")]
        private static extern bool SetConsoleCtrlHandler(EventHandler handler, bool add);                
        private delegate bool EventHandler(CtrlType sig);
        static EventHandler exitHandler;
        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 ExitHandler(CtrlType sig)
        {
            Console.WriteLine("Shutting down: " + sig.ToString());            
            run.Reset();
            Thread.Sleep(2000);
            return false; // If the function handles the control signal, it should return TRUE. If it returns FALSE, the next handler function in the list of handlers for this process is used (from MSDN).
        }


        static void Main(string[] args)
        {
            // if you want to allow only one instance otherwise remove the next 4 lines
            if (!mutex.WaitOne(TimeSpan.FromSeconds(2), false))
            {
                return; // singleton application already started
            }

            exitHandler += new EventHandler(ExitHandler);
            SetConsoleCtrlHandler(exitHandler, true);

            try
            {
                Console.BackgroundColor = ConsoleColor.Gray;
                Console.ForegroundColor = ConsoleColor.Black;
                Console.Clear();
                Console.SetBufferSize(Console.BufferWidth, 1024);

                Console.Title = "Your Console Title - XYZ";

                // start your threads here
                Thread thread1 = new Thread(new ThreadStart(ThreadFunc1));
                thread1.Start();

                Thread thread2 = new Thread(new ThreadStart(ThreadFunc2));
                thread2.IsBackground = true; // a background thread
                thread2.Start();

                while (run.WaitOne(0))
                {
                    Thread.Sleep(100);
                }

                // do thread syncs here signal them the end so they can clean up or use the manual reset event in them or abort them
                thread1.Abort();
            }
            catch (Exception ex)
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.Write("fail: ");
                Console.ForegroundColor = ConsoleColor.Black;
                Console.WriteLine(ex.Message);
                if (ex.InnerException != null)
                {
                    Console.WriteLine("Inner: " + ex.InnerException.Message);
                }
            }
            finally
            {                
                // do app cleanup here

                // if you want to allow only one instance otherwise remove the next line
                mutex.ReleaseMutex();

                // remove this after testing
                Console.Beep(5000, 100);
            }
        }

        public static void ThreadFunc1()
        {
            Console.Write("> ");
            while ((line = Console.ReadLine()) != null)
            {
                if (line == "command 1")
                {

                }
                else if (line == "command 1")
                {

                }
                else if (line == "?")
                {

                }

                Console.Write("> ");
            }
        }


        public static void ThreadFunc2()
        {
            while (run.WaitOne(0))
            {
                Thread.Sleep(100);
            }

           // do thread cleanup here
            Console.Beep();         
        }

    }
}

The link mentioned above by Charle B in comment to flq

Deep down says:

SetConsoleCtrlHandler won't work on windows7 if you link to user32

Some where else in the thread it is suggested to crate a hidden window. So I create a winform and in onload I attached to console and execute original Main. And then SetConsoleCtrlHandle works fine (SetConsoleCtrlHandle is called as suggested by flq)

public partial class App3DummyForm : Form
{
    private readonly string[] _args;

    public App3DummyForm(string[] args)
    {
        _args = args;
        InitializeComponent();
    }

    private void App3DummyForm_Load(object sender, EventArgs e)
    {
        AllocConsole();
        App3.Program.OriginalMain(_args);
    }

    [DllImport("kernel32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool AllocConsole();
}

For those interested in VB.net. (I searched the internet and couldn't find an equivalent for it) Here it is translated into vb.net.

    <DllImport("kernel32")> _
    Private Function SetConsoleCtrlHandler(ByVal HandlerRoutine As HandlerDelegate, ByVal Add As Boolean) As Boolean
    End Function
    Private _handler As HandlerDelegate
    Private Delegate Function HandlerDelegate(ByVal dwControlType As ControlEventType) As Boolean
    Private Function ControlHandler(ByVal controlEvent As ControlEventType) As Boolean
        Select Case controlEvent
            Case ControlEventType.CtrlCEvent, ControlEventType.CtrlCloseEvent
                Console.WriteLine("Closing...")
                Return True
            Case ControlEventType.CtrlLogoffEvent, ControlEventType.CtrlBreakEvent, ControlEventType.CtrlShutdownEvent
                Console.WriteLine("Shutdown Detected")
                Return False
        End Select
    End Function
    Sub Main()
        Try
            _handler = New HandlerDelegate(AddressOf ControlHandler)
            SetConsoleCtrlHandler(_handler, True)
     .....
End Sub

참고URL : https://stackoverflow.com/questions/474679/capture-console-exit-c-sharp

반응형