83


32

私はかなり多くのスレッドを含むコンソールアプリケーションを持っています。 特定の条件を監視し、それらが真であればプログラムを終了させるスレッドがあります。 この終了はいつでも起こり得ます。

他のすべてのスレッドをクリーンアップし、すべてのファイルハンドルと接続を適切に閉じることができるように、プログラムを閉じるときに発生させることができるイベントが必要です。 .NETフレームワークにすでに組み込まれているものがあるかどうかわからないので、自分で書く前に聞いています。

私は次の行に沿ってイベントがあるのか​​どうか疑問に思いました。

MyConsoleProgram.OnExit = CleanupBeforeExit;

10 Answer


89


Web上のどこでコードが見つかったのかわかりませんが、古いプロジェクトの中で見つけました。 これにより、コンソールでクリーンアップコードを実行できます。 突然閉じたときやシャットダウンのために…​

[DllImport("Kernel32")]
プライベート静的外部bool SetConsoleCtrlHandler(EventHandlerハンドラー、bool追加)。

プライベートデリゲートブールEventHandler(CtrlType sig)。 static EventHandler _handler;

列挙型CtrlType {CTRL_C_EVENT = 0、CTRL_BREAK_EVENT = 1、CTRL_CLOSE_EVENT = 2、CTRL_LOGOFF_EVENT = 5、CTRL_SHUTDOWN_EVENT = 6}

private static boolハンドラ(CtrlType sig){switch(sig){case CtrlType.CTRL_C_EVENT:case CtrlType.CTRL_LOGOFF_EVENT:case CtrlType.CTRL_SHUTDOWN_EVENT:case CtrlType.CTRL_CLOSE_EVENT:デフォルト:falseを返します。 }}

static void Main(string [] args){//ウィンドウを閉じるイベントに反応するためのバイオプレート。_handler = new EventHandler(Handler); SetConsoleCtrlHandler(_handler、true); ... }

更新

コメントをチェックしていない人たちにとって、この特定の解決策は* Windows 7 *ではうまく機能しない(またはまったく機能しない)ようです。 次の threadはこれについて話しています


24


完全な動作例では、Ctrlキーを押しながらCキーを押し、Xキーを押してウィンドウを閉じます。

システムを使用する。 System.Collections.Genericを使用します。 System.Linqを使用します。 System.Runtime.InteropServicesを使用します。 System.Textを使用します。 System.Threadingを使用します。

名前空間TestTrapCtrlC {public class Program {static bool exitSystem = false;}

#regionトラップアプリケーションの終了[DllImport( "Kernel32")] private static extern bool SetConsoleCtrlHandler(EventHandlerハンドラ、bool add);

プライベートデリゲートブールEventHandler(CtrlType sig)。 static EventHandler _handler;

列挙型CtrlType {CTRL_C_EVENT = 0、CTRL_BREAK_EVENT = 1、CTRL_CLOSE_EVENT = 2、CTRL_LOGOFF_EVENT = 5、CTRL_SHUTDOWN_EVENT = 6}

private static boolハンドラ(CtrlType sig){Console.WriteLine( "外部CTRL-Cによりシステムを終了、またはプロセスの終了、シャットダウン");

//ここでクリーンアップを行います。Thread.Sleep(5000); //クリーンアップ遅延をシミュレートする

Console.WriteLine( "クリーンアップ完了");

// mainがexitSystem = trueを実行することを許可します。

//残りのスレッドがないようにすぐにシャットダウンします。Environment.Exit(-1);

trueを返します。 #endregion

static void Main(string [] args){//ウィンドウを閉じるイベント、Ctrl + Cキー、killキーなどに反応するためのバイオプレート。 SetConsoleCtrlHandler(_handler、true);

//ここでマルチスレッドプログラムを起動します。Program p = new Program(); p.Start();

//コンソールを最後まで実行しないように保持します。(!exitSystem){Thread.Sleep(500); }}

public void Start(){//スレッドを開始して何らかの処理を開始します。Console.WriteLine( "Thread started、processing .."); }}}


7


またチェックしなさい:

AppDomain.CurrentDomain.ProcessExit


3


WinFormsアプリ用です。

Application.ApplicationExit = CleanupBeforeExit;

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

AppDomain.CurrentDomain.DomainUnload = CleanupBeforeExit;

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


3


アプリケーションを直接終了させるスレッドがあるようですね。 おそらく、アプリケーションを終了する必要があると言うために、スレッドにメインスレッドに通知させる方が良いでしょう。

このシグナルを受け取ると、メインスレッドは他のスレッドをクリーンにシャットダウンし、最後に自分自身を閉じることができます。


3


私は同様の問題を抱えていました、ちょうど私のコンソールAppが途中で1つの先制命令で無限ループで実行されるでしょう。 これが私の解決策です:

クラスProgram {static int Main(string [] args){//コードの初期化... Console.CancelKeyPress = Console_CancelKeyPress; //イベントキャンセル機能を登録

//私は自分のものをやる

while(true){//コード... SomePreemptiveCall(); //ループはここでwating関数を返して戻ります//コード... 0を返します。 //二度とここに来ないが… }

static void Console_CancelKeyPress(オブジェクト送信側、ConsoleCancelEventArgs e){Console.WriteLine( "Exiting"); // Environment.Exit(-1);を終了するために必要なものを削除します。 }}


1


flqへのコメントでCharle Bによって上記で言及されたhttp://social.msdn.microsoft.com/Forums/en/windowscompatibility/thread/abf09824-4e4c-4f2c-ae1e-5981f06c9c6e[link]

深く言う:

_ user32にリンクすると、Windows 7では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();
}


1


ZeroKelvinの答えは、Windows 10 x 64、.NET 4.6コンソールアプリで機能します。 CtrlType列挙体を扱う必要がない人のために、フレームワークのシャットダウンにフックするための本当に簡単な方法は次のとおりです。

クラスProgram {private delegate bool ConsoleCtrlHandlerDelegate(int sig);}

[DllImport( "Kernel32")]プライベート静的外部ブールSetConsoleCtrlHandler(ConsoleCtrlHandlerDelegateハンドラ、ブール追加);

static ConsoleCtrlHandlerDelegate _consoleCtrlHandler;

static void Main(string [] args){_consoleCtrlHandler = s => {// DoCustomShutdownStuff(); falseを返します。 ; SetConsoleCtrlHandler(_consoleCtrlHandler、true); }}

ハンドラからFALSEを返すと、制御信号を「処理していない」ことがフレームワークに伝えられ、このプロセスのハンドラのリストにある次のハンドラ関数が使用されます。 どのハンドラもTRUEを返さない場合は、デフォルトのハンドラが呼び出されます。

ユーザーがログオフまたはシャットダウンを実行すると、コールバックはWindowsによって呼び出されず、代わりに直ちに終了します。


0


VB.netに興味がある人のために。 (私はインターネットを検索しましたが、それに相当するものを見つけることができませんでした)ここでそれはvb.netに翻訳されています。

_プライベート関数SetConsoleCtrlHandler(ByVal HandlerRoutineとしてのHandlerDelegate、ByValとしてのブールとしての追加)ブールとしての終了関数プライベート_handlerとしてのHandlerDelegateプライベートデリゲート関数HandlerDelegate(ByValとしてのdwControlTypeとしてのControlEventType)ブールとしてのプライベート関数ControlEventType.CtrlCEvent、ControlEventType.CtrlCloseEvent Console.WriteLine( "Closing ...")戻り値Trueの場合)_handler = New HandlerDelegate(AddressOf ControlHandler)を試してください。SetConsoleCtrlHandler(_handler、True)..... 終了サブ


0


Visual Studio 2015 Windows 10

  • クリーンアップを許可する

  • シングルインスタンスアプリ

  • 金メッキ

コード:

システムを使用する。 System.Linqを使用します。 System.Runtime.InteropServicesを使用します。 System.Threadingを使用します。

namespace YourNamespace {class Program {// 1つのインスタンスのみを許可する場合は次の行を削除します。static Mutex mutex = new Mutex(false、 "YOURGUID-YOURGUID-YOURGUID-YO");

static ManualResetEvent run = new ManualResetEvent(true);

[DllImport( "Kernel32")]プライベート静的外部ブールSetConsoleCtrlHandler(EventHandlerハンドラー、ブール追加);プライベートデリゲートブールEventHandler(CtrlType sig)。 static EventHandler exitHandler;列挙型CtrlType {CTRL_C_EVENT = 0、CTRL_BREAK_EVENT = 1、CTRL_CLOSE_EVENT = 2、CTRL_LOGOFF_EVENT = 5、CTRL_SHUTDOWN_EVENT = 6}プライベートスタティックbool ExitHandler(CtrlType sig){Console.WriteLine( "ShuttingString:sig)") run.Reset(); Thread.Sleep(2000); falseを返します。 //関数が制御信号を処理する場合は、TRUEを返します。 FALSEが返された場合、このプロセスのハンドラのリストにある次のハンドラ関数が使用されます(MSDNから)。 }

static void Main(string [] args){// 1つのインスタンスのみを許可したい場合は、次の場合に次の4行を削除します。(!mutex.WaitOne(TimeSpan.FromSeconds(2)、false)){return;} //シングルトンアプリケーションはすでに起動しています}

exitHandler = new EventHandler(ExitHandler); SetConsoleCtrlHandler(exitHandler、true);

{Console.BackgroundColor = ConsoleColor.Gray;を試してください。 Console.ForegroundColor = ConsoleColor.Black; Console.Clear(); Console.SetBufferSize(Console.BufferWidth、1024);

Console.Title = "コンソールのタイトル -  XYZ";

//ここでスレッドを開始しますThread thread1 = new Thread(new ThreadStart(ThreadFunc1)); thread1.Start();

スレッドthread2 = new Thread(new ThreadStart(ThreadFunc2)); thread2.IsBackground = true; //バックグラウンドスレッドthread2.Start();

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

//ここでスレッド同期を実行して終了を通知し、手動でのリセットイベントをクリーンアップまたは使用するか、中止することができます。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);最後に{//ここでアプリのクリーンアップを行います

// 1つのインスタンスのみを許可する場合は、次の行を削除します。mutex.ReleaseMutex();

// Console.Beep(5000、100)のテスト後にこれを削除します。 }}

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

そうでなければ(line == "command 1"){

そうでなければ(line == "?"){

}

Console.Write( ">"); }}

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

//ここでスレッドのクリーンアップを行います。Console.Beep(); }

}}