문제

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

다른 스레드를 정리하고 모든 파일 핸들과 연결을 올바르게 닫을 수 있도록 프로그램이 닫힐 때 트리거 될 수있는 이벤트가 필요합니다. .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);
  ...
}

업데이트

의견을 확인하지 않은 사람들을 위해이 특정 솔루션은 ~ 아니다 잘 작동합니다 (또는 전혀) 윈도우 7. 다음과 같은 이것에 대해 이야기합니다

다른 팁

전체 작업 예는 Ctrl-C와 작동하여 X로 Windows를 닫고 킬 :

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

응용 프로그램을 직접 종료하는 스레드가있는 것 같습니다. 아마도 스레드가 메인 스레드에 신호를 보내는 것이 더 나을 것입니다. 응용 프로그램을 종료해야한다고 말합니다.

이 신호를 받으면 기본 스레드가 다른 스레드를 깨끗하게 셧다운하고 마지막으로 닫을 수 있습니다.

WinForms 앱이 있습니다.

Application.ApplicationExit += CleanupBeforeExit;

콘솔 앱의 경우 시도하십시오

AppDomain.CurrentDomain.DomainUnload += CleanupBeforeExit;

그러나 나는 어떤 시점에서 호출되는지 또는 그것이 현재 도메인 내에서 작동할지 확실하지 않습니다. 나는 의심하지 않는다.

비슷한 문제가 있었는데, 콘솔 앱 만 중간에 하나의 선제 적 진술로 무한 루프로 실행됩니다. 내 해결책은 다음과 같습니다.

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

Zerokelvin의 답변은 Windows 10 X64, .NET 4.6 콘솔 앱에서 작동합니다. Ctrltype enum을 다룰 필요가없는 사람들을 위해 프레임 워크의 종료에 연결하는 간단한 방법이 있습니다.

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

    }
}

그만큼 링크 Charle B가 FLQ에 대한 의견으로 위에서 언급했습니다

Deep Down의 말 :

user32에 링크하면 setConsolectrlHandler가 Windows7에서 작동하지 않습니다.

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

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
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top