2009-07-13 137 views
47

在.NET中,是否有方法(如事件)来检测控制台应用程序何时退出?我需要清理一些线程和COM对象。.NET控制台应用程序退出事件

我运行一个消息循环,没有一种形式,从控制台应用程序。我正在使用的DCOM组件似乎要求应用程序泵送消息。

我曾尝试添加一个处理程序Process.GetCurrentProcess.Exited和Process.GetCurrentProcess.Disposed。

我也试图加入一个处理程序Application.ApplicationExit和Application.ThreadExit事件,但他们不点火。也许这是因为我没有使用表单。

+2

的Process.GetCurrentProcess()已退出事件段应火灾。否则,它不会着火。 – Triynko 2013-04-22 23:54:49

+2

[Capture控制台出口C#]的可能重复(http://stackoverflow.com/questions/474679/capture-console-exit-c-sharp) – nawfal 2014-01-10 09:01:33

+0

@Triynko那是怎么回事? Process.Exited在退出进程后触发。如果自己的应用程序退出,那么根本没有执行任何代码。漂亮的无用的IMo .. – nawfal 2014-01-10 16:25:14

回答

48

您可以使用ProcessExit事件AppDomain的:

class Program 
{ 
    static void Main(string[] args) 
    { 
     AppDomain.CurrentDomain.ProcessExit += new EventHandler(CurrentDomain_ProcessExit);   
     // do some work 

    } 

    static void CurrentDomain_ProcessExit(object sender, EventArgs e) 
    { 
     Console.WriteLine("exit"); 
    } 
} 

更新

下面是空的“消息泵”在一个单独的线程上运行一个完整的示例程序,允许用户可以在控制台中输入退出命令以优雅地关闭应用程序。在MessagePump中循环之后,您可能需要以一种很好的方式清理线程使用的资源。最好这样做比在ProcessExit中有以下几个原因:

  • 避免交叉线程问题;如果外部COM对象是在MessagePump线程上创建的,那么在那里处理它们会更容易。
  • ProcessExit有一个时间限制(默认为3秒),所以如果清理非常耗时,如果在该事件处理程序中进行了修改,则可能会失败。

下面是代码:

class Program 
{ 
    private static bool _quitRequested = false; 
    private static object _syncLock = new object(); 
    private static AutoResetEvent _waitHandle = new AutoResetEvent(false); 

    static void Main(string[] args) 
    { 
     AppDomain.CurrentDomain.ProcessExit += new EventHandler(CurrentDomain_ProcessExit); 
     // start the message pumping thread 
     Thread msgThread = new Thread(MessagePump); 
     msgThread.Start(); 
     // read input to detect "quit" command 
     string command = string.Empty; 
     do 
     { 
      command = Console.ReadLine(); 
     } while (!command.Equals("quit", StringComparison.InvariantCultureIgnoreCase)); 
     // signal that we want to quit 
     SetQuitRequested(); 
     // wait until the message pump says it's done 
     _waitHandle.WaitOne(); 
     // perform any additional cleanup, logging or whatever 
    } 

    private static void SetQuitRequested() 
    { 
     lock (_syncLock) 
     { 
      _quitRequested = true; 
     } 
    } 

    private static void MessagePump() 
    { 
     do 
     { 
      // act on messages 
     } while (!_quitRequested); 
     _waitHandle.Set(); 
    } 

    static void CurrentDomain_ProcessExit(object sender, EventArgs e) 
    { 
     Console.WriteLine("exit"); 
    } 
} 
+3

感谢您的建议弗雷德里克。这个事件似乎也没有触发。 – user79755 2009-07-13 14:48:38

1

如果您使用的是控制台应用程序,你都抽的消息,你不能使用WM_QUIT消息?

15

的应用是简单地运行,直到系统关闭或者它接收到Ctrl + C或控制台窗口被关闭的服务器。

由于应用的特殊性,它不是“正常”退出可行的。 (这可能是因为我可以代码中的另一个应用程序,它会发送“服务器关闭”消息,但是这将是矫枉过正了一个应用程序,仍不足以某些情况下,当服务器(实际OS)实际上是关闭等。)

因为这些情况下,我增加了一个“ConsoleCtrlHandler”在那里我停下我的线程和清理我的COM对象等等......


Public Declare Auto Function SetConsoleCtrlHandler Lib "kernel32.dll" (ByVal Handler As HandlerRoutine, ByVal Add As Boolean) As Boolean 

Public Delegate Function HandlerRoutine(ByVal CtrlType As CtrlTypes) As Boolean 

Public Enum CtrlTypes 
    CTRL_C_EVENT = 0 
    CTRL_BREAK_EVENT 
    CTRL_CLOSE_EVENT 
    CTRL_LOGOFF_EVENT = 5 
    CTRL_SHUTDOWN_EVENT 
End Enum 

Public Function ControlHandler(ByVal ctrlType As CtrlTypes) As Boolean 
. 
.clean up code here 
. 
End Function 

Public Sub Main() 
. 
. 
. 
SetConsoleCtrlHandler(New HandlerRoutine(AddressOf ControlHandler), True) 
. 
. 
End Sub 

这种设置似乎完美地工作了。这是一个link到一些C#代码为同样的事情。

7

对于CTRL + C的情况下,你可以使用这个:

// Tell the system console to handle CTRL+C by calling our method that 
// gracefully shuts down. 
Console.CancelKeyPress += new ConsoleCancelEventHandler(Console_CancelKeyPress); 


static void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e) 
{ 
      Console.WriteLine("Shutting down..."); 
      // Cleanup here 
      System.Threading.Thread.Sleep(750); 
} 
16

这是一个完整的,非常简单的.NET解决方案,在所有版本的Windows的作品。只需将其粘贴到一个新的项目,运行它,并尝试CTRL-C来查看它如何处理它:

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 biolerplate 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.."); 
     } 
    } 
} 
0

作为一个很好的例子可能是值得的,navigate to this project,看看如何处理退出过程语法或本如果第一套Process.EnableRaisingEvents =真正从VM found in here

   ConsoleOutputStream = new ObservableCollection<string>(); 

       var startInfo = new ProcessStartInfo(FilePath) 
       { 
        WorkingDirectory = RootFolderPath, 
        Arguments = StartingArguments, 
        RedirectStandardOutput = true, 
        UseShellExecute = false, 
        CreateNoWindow = true 
       }; 
       ConsoleProcess = new Process {StartInfo = startInfo}; 

       ConsoleProcess.EnableRaisingEvents = true; 

       ConsoleProcess.OutputDataReceived += (sender, args) => 
       { 
        App.Current.Dispatcher.Invoke((System.Action) delegate 
        { 
         ConsoleOutputStream.Insert(0, args.Data); 
         //ConsoleOutputStream.Add(args.Data); 
        }); 
       }; 
       ConsoleProcess.Exited += (sender, args) => 
       { 
        InProgress = false; 
       }; 

       ConsoleProcess.Start(); 
       ConsoleProcess.BeginOutputReadLine(); 
     } 
    } 

    private void RegisterProcessWatcher() 
    { 
     startWatch = new ManagementEventWatcher(
      new WqlEventQuery($"SELECT * FROM Win32_ProcessStartTrace where ProcessName = '{FileName}'")); 
     startWatch.EventArrived += new EventArrivedEventHandler(startProcessWatch_EventArrived); 

     stopWatch = new ManagementEventWatcher(
      new WqlEventQuery($"SELECT * FROM Win32_ProcessStopTrace where ProcessName = '{FileName}'")); 
     stopWatch.EventArrived += new EventArrivedEventHandler(stopProcessWatch_EventArrived); 
    } 

    private void stopProcessWatch_EventArrived(object sender, EventArrivedEventArgs e) 
    { 
     InProgress = false; 
    } 

    private void startProcessWatch_EventArrived(object sender, EventArrivedEventArgs e) 
    { 
     InProgress = true; 
    } 
相关问题