97

非常に多くのスレッドを含むコンソール アプリケーションがあります。特定の条件を監視し、真の場合にプログラムを終了するスレッドがあります。この終了はいつでも発生する可能性があります。

他のすべてのスレッドをクリーンアップし、すべてのファイル ハンドルと接続を適切に閉じることができるように、プログラムの終了時にトリガーできるイベントが必要です。.NET フレームワークに既に組み込まれているものがあるかどうかわからないので、独自のものを作成する前に尋ねています。

次のようなイベントがあったかどうか疑問に思っていました。

MyConsoleProgram.OnExit += CleanupBeforeExit;
4

10 に答える 10

103

Web 上のどこでコードを見つけたのかわかりませんが、古いプロジェクトの 1 つで見つけました。これにより、コンソールが突然閉じられた場合やシャットダウンが原因で発生した場合など、コンソールでクリーンアップ コードを実行できます...

[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 ではうまく機能しない(またはまったく機能しない) ようです。次のスレッドはこれについて話します

于 2009-01-23T21:52:03.977 に答える
7

以下も確認してください。

AppDomain.CurrentDomain.ProcessExit
于 2009-05-25T15:18:42.487 に答える
5

私は同様の問題を抱えていました.ちょうど私のコンソールアプリが無限ループで実行され、真ん中に1つのプリエンプティブステートメントがありました. これが私の解決策です:

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);
    }
}
于 2014-09-03T07:07:35.120 に答える
4

アプリケーションを直接終了するスレッドがあるようですね。おそらく、アプリケーションを終了する必要があることを示すために、スレッドからメイン スレッドにシグナルを送る方がよいでしょう。

このシグナルを受信すると、メイン スレッドは他のスレッドを正常にシャットダウンし、最終的に自身を閉じることができます。

于 2009-01-23T22:36:29.317 に答える
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 によって呼び出されず、代わりにすぐに終了されることに注意してください。

于 2016-07-08T13:32:13.467 に答える
3

WinForms アプリ用があります。

Application.ApplicationExit += CleanupBeforeExit;

コンソール アプリの場合は、

AppDomain.CurrentDomain.DomainUnload += CleanupBeforeExit;

しかし、それがどの時点で呼び出されるのか、または現在のドメイン内から機能するかどうかはわかりません。ないと思います。

于 2009-01-23T21:40:58.803 に答える
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();         
        }

    }
}
于 2015-12-31T16:39:24.250 に答える
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
于 2011-02-11T21:10:22.597 に答える