콘솔 종료 C # 캡처


93

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

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

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

MyConsoleProgram.OnExit += CleanupBeforeExit;

2
나는 이것이 매우 늦은 주석이라는 것을 알고 있지만 "파일 및 연결 닫기"가 정리로 수행하려는 유일한 작업이라면 실제로 그렇게 할 필요가 없습니다. Windows는 종료 중에 프로세스와 관련된 모든 핸들을 이미 닫았 기 때문입니다.
Sedat Kapanoglu

6
^ 해당 리소스가 종료되는 프로세스가 소유 한 경우에만. 예를 들어 백그라운드에서 숨겨진 COM 응용 프로그램 (예 : Word 또는 Excel)을 자동화하고 앱이 종료되기 전에 반드시 종료
해야하는

1
이것은 짧은 대답을 가지고 있습니다. stackoverflow.com/questions/2555292/…
barlop

답변:


97

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

[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 에서 잘 작동 하지 않는 것 같습니다 (또는 전혀) . 다음 스레드 는 이것에 대해 이야기합니다.


4
이 기능을 사용하여 종료를 취소 할 수 있습니까? 종료 될 때 외에는!
ingh.am 2010 년

7
이것은 훌륭하게 작동하며 bool Handler()반드시 return false;(코드에서 아무것도 반환하지 않음) 작동하므로 작동합니다. true를 반환하면 "Terminate Process Now"대화 상자가 표시됩니다. = D
Cipi 2010

3
이 솔루션은 종료 이벤트로 인해 Windows 7에서 작동하지 않는 것 같습니다. social.msdn.microsoft.com/Forums/en/windowscompatibility/thread/…
CharlesB

3
'Handler'메서드에 중단 점을 넣으면 NullReferenceException이 발생합니다. VS2010, 윈도우 7에서 검사
맥심

10
이것은 Windows 7 (64 비트)에서 잘 작동했습니다. 왜 모든 사람들이 그렇게 말하지 않는지 잘 모르겠습니다. 내가 만든 유일한 주요 수정 사항은 enum 및 switch 문을 제거하고 메서드에서 "거짓을 반환"하는 것입니다. 메서드 본문에서 모든 정리를 수행합니다.
BrainSlugs83 2013

25

전체 작업 예제는 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..");
        }
    }
}

2
나는 초를 계산하는 while 루프를 Handler제외하고는 모든 것을 주석 처리하여 Windows 7에서 이것을 테스트했습니다 return true. 응용 프로그램은 ctrl-c에서 계속 실행되지만 X로 닫을 때 5 초 후에 닫힙니다.
Antonios Hadjigeorgalis

죄송합니다.이 코드를 사용하면 'X'버튼으로 닫을 때가 아니라 Ctrl + C를 눌러야 "정리 완료" 를 얻을 수 있습니다 . 후자의 경우에 난 단지 얻을 "때문에 외부 CTRL-C 또는 프로세스 킬, 또는 종료에 시스템 종료" 하지만 그것의 나머지 부분을 실행하기 전에 콘솔 닫히고 보인다 Handler{Win10, .NET 프레임 워크 4.6.1을 사용} 방법
Giacomo Pirinoli

Windows 10에서는 CTRL-C, X가 창에서 작동하고 작업 관리자에서 프로세스가 종료됩니다.
JJ_Coder4Hire

8

또한 확인 :

AppDomain.CurrentDomain.ProcessExit

7
이것은 return 또는 Environment.Exit에서 출구를 포착하는 것처럼 보이며 콘솔의 CTRL + C, CTRL + Break 또는 실제 닫기 버튼을 포착하지 않습니다.
Kit10

사용 별도로 CTRL + C를 처리 할 경우 Console.CancelKeyPress다음 ProcessExit이벤트가 실제로 결국 상승 CancelKeyPress이벤트 핸들러 실행.
Konard

5

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

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

4

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

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


3
이 답변에 동의해야합니다. 응용 프로그램을 강제 종료 한 다음 나중에 정리를 시도하는 것은 좋은 방법이 아닙니다. 응용 프로그램을 제어하십시오, Noit. 그것이 당신을 통제하게하지 마십시오.
Randolpho

1
내가 직접 생성 한 스레드가 반드시 내 애플리케이션을 닫을 수있는 유일한 것은 아닙니다. Ctrl-C 및 "닫기 버튼"은 종료 할 수있는 다른 방법입니다. Frank가 게시 한 코드는 약간의 수정 후 완벽하게 맞습니다.
ZeroKelvin

4

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에서 호출되지 않고 대신 즉시 종료됩니다.


3

WinForms 앱이 있습니다.

Application.ApplicationExit += CleanupBeforeExit;

콘솔 앱의 경우

AppDomain.CurrentDomain.DomainUnload += CleanupBeforeExit;

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


DomainUnload에 대한 도움말 문서에는 "이 이벤트에 대한 EventHandler 대리자는 응용 프로그램 도메인이 언로드되기 전에 모든 종료 작업을 수행 할 수 있습니다."라고 말합니다. 따라서 현재 도메인 내에서 작동하는 것처럼 들립니다. 그러나 그의 스레드가 도메인을 유지하기 때문에 그의 필요에 맞지 않을 수 있습니다.
Rob Parker

2
이것은 CTRL + C와 CTRL + Close 만 처리하고, Environment.Exit를 반환하거나 닫기 버튼을 클릭해도 존재하지 않습니다.
Kit10

Linux의 Mono에서 CTRL + C를 잡지 못합니다.
starbeamrainbowlabs

2

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();         
        }

    }
}

이것이 가장 확실한 대답 인 것 같습니다. 그러나 콘솔 버퍼 크기를 변경할 때는주의해야합니다. 버퍼 높이가 창 높이보다 작 으면 프로그램이 시작시 예외를 throw합니다.
John Zabroski

1

링크 FLQ 의견에 찰 B에 의해 위에서 언급 한

깊은 곳에서 말한다 :

user32에 연결하면 Windows7에서 SetConsoleCtrlHandler가 작동하지 않습니다.

스레드의 다른 곳에서는 숨겨진 창을 만드는 것이 좋습니다. 그래서 나는 winform을 만들고 onload에서 콘솔에 연결하고 원래 Main을 실행합니다. 그런 다음 SetConsoleCtrlHandle이 제대로 작동합니다 (SetConsoleCtrlHandle은 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();
}

실제로 이것은 작동하지 않습니다. 다중 창 WFP 앱이 있고 콘솔 ( AllocConsole예 : 예)을 사용하여 몇 가지 추가 정보를 표시합니다. 문제는 사용자가 콘솔 창에서 (X)를 클릭하면 전체 앱 (모든 Windows)이 닫히는 것입니다. SetConsoleCtrlHandler작동하지만 핸들러의 코드가 실행 어쨌든 전에 응용 프로그램은 정지는 (I 중단 점은 바로 다음 응용 프로그램은 정지 해고 참조).
Mike Keskinov 2016

하지만 나에게 맞는 해결책을 찾았 습니다. 닫기 버튼을 사용 중지했습니다 . 참조 : stackoverflow.com/questions/6052992/…
Mike Keskinov 2011

0

VB.net에 관심이있는 분들을 위해. (인터넷을 검색했지만 이에 상응하는 것을 찾을 수 없습니다.) 여기에서 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

위의 솔루션이 나를 위해 작동하지 않습니다. vb.net 4.5 프레임 워크 ControlEventType이 해결되지 않습니다. 이 아이디어를 솔루션으로 사용할 수있었습니다. stackoverflow.com/questions/15317082/…
glant
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.