2009-07-13 25 views
47

Trong .NET, có phương pháp nào, chẳng hạn như một sự kiện, để phát hiện khi một ứng dụng Console đang thoát? Tôi cần phải làm sạch một số chủ đề và các đối tượng COM..NET Console Application Exit Event

Tôi đang chạy một vòng lặp tin nhắn, không có biểu mẫu, từ ứng dụng bảng điều khiển. Một thành phần DCOM mà tôi đang sử dụng dường như yêu cầu thông báo bơm ứng dụng.

Tôi đã thử thêm trình xử lý vào Process.GetCurrentProcess.Exited và Process.GetCurrentProcess.Disposed.

Tôi cũng đã thử thêm một trình xử lý cho các sự kiện Application.ApplicationExit và Application.ThreadExit, nhưng chúng không kích hoạt. Có lẽ đó là bởi vì tôi không sử dụng một hình thức.

+2

Process.GetCurrentProcess(). Sự kiện đã xảy ra sẽ kích hoạt nếu bạn thiết lập Process.EnableRaisingEvents = true; nếu không, nó sẽ không cháy. – Triynko

+2

có thể trùng lặp của [Capture console exit C#] (http://stackoverflow.com/questions/474679/capture-console-exit-c-sharp) – nawfal

+0

@Triynko thế nào? Process.Exited được kích hoạt sau khi quá trình này được thoát. Và nếu ứng dụng của riêng được thoát thì không có mã nào được thực thi cả. Khá vô dụng .. – nawfal

Trả lời

48

Bạn có thể sử dụng các sự kiện ProcessExit của 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"); 
    } 
} 

Cập nhật

Dưới đây là một ví dụ chương trình đầy đủ với một sản phẩm nào "Thông điệp bơm" chạy trên một chủ đề riêng biệt, cho phép người người dùng nhập lệnh thoát trong bảng điều khiển để đóng ứng dụng một cách duyên dáng. Sau khi vòng lặp trong MessagePump, bạn có thể sẽ muốn dọn sạch tài nguyên được sử dụng bởi luồng theo cách tốt đẹp. Tốt hơn bạn nên làm điều đó hơn trong ProcessExit vì một vài lý do:

  • Tránh các vấn đề về luồng chéo; nếu các đối tượng COM bên ngoài đã được tạo trên luồng MessagePump, thì việc xử lý chúng ở đó dễ dàng hơn.
  • Có giới hạn thời gian trên ProcessExit (3 giây theo mặc định), vì vậy nếu việc dọn dẹp tốn nhiều thời gian, nó có thể thất bại nếu được đặt trong trình xử lý sự kiện đó.

Đây là mã:

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

Cảm ơn đề xuất Fredrik. Sự kiện này cũng không xuất hiện. – user79755

+0

Quy trình của bạn thoát ra sao? Bạn có gọi bất kỳ mã nào khiến nó thoát không? –

+0

Hiện tại, cách duy nhất để thoát, là nhấp vào nút Đóng trên cửa sổ bảng điều khiển hoặc có thể nhấn ctrl + c. – user79755

1

Nếu bạn đang sử dụng một ứng dụng giao diện điều khiển và bạn đang bơm thông điệp, bạn không thể sử dụng thông điệp WM_QUIT?

+1

Tôi cũng thắc mắc về điều đó. Tôi cho rằng nó * có thể * là trường hợp cửa sổ không gửi tin nhắn đến một ứng dụng giao diện điều khiển, nhưng nó có vẻ lạ với tôi. Người ta nên nghĩ rằng yêu cầu sẽ có một máy bơm tin nhắn, không hay không nó có một GUI ... –

15

Ứng dụng là một máy chủ chỉ chạy cho đến khi hệ thống tắt hoặc nhận được một Ctrl + C hoặc cửa sổ bảng điều khiển được đóng lại.

Do tính chất phi thường của ứng dụng, không khả thi để thoát "duyên dáng". (Có thể là tôi có thể viết một ứng dụng khác sẽ gửi thông báo "shutdown server" nhưng đó sẽ là quá mức cần thiết cho một ứng dụng và vẫn không đủ cho các trường hợp nhất định như khi máy chủ (Actual OS) thực sự tắt.)

Vì những hoàn cảnh tôi đã thêm một "ConsoleCtrlHandler" nơi tôi dừng lại chủ đề của tôi và dọn dẹp các đối tượng COM của tôi vv ...


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 

thiết lập này dường như làm việc ra một cách hoàn hảo. Dưới đây là một số link đối với một số mã C# cho cùng một thứ.

+1

Cảm ơn, nó đã giúp tôi :) 1 Bạn nên đánh dấu câu trả lời của riêng bạn là câu trả lời. – leppie

+2

Dường như có giới hạn 3 giây đối với trình xử lý để hoàn tất. Nếu bạn không hoàn thành kịp thời, bạn sẽ bị chấm dứt không đúng lúc. Điều đó bao gồm nếu bạn đang ngồi trên một breakpoint trong trình gỡ lỗi! –

+0

Điều này làm việc nhưng một mẫu làm việc hoàn chỉnh được tìm thấy bên dưới trong C# –

7

Đối với trường hợp tổ hợp phím CTRL + C, bạn có thể sử dụng này:

// 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); 
} 
+0

Thao tác này có xử lý việc đăng xuất tắt máy ctrl + c v.v ... không? – user79755

+1

Đã thử nó, sự kiện không bao giờ cháy. Giống như các sự kiện khác. – user79755

+0

Điều này làm việc tốt cho tôi, khi chạy một ứng dụng Console. – Contango

16

Đây là một hoàn chỉnh, giải pháp rất đơn giản Net mà làm việc trong tất cả các phiên bản của cửa sổ.Đơn giản chỉ cần dán nó vào một dự án mới, chạy nó và thử CTRL-C để xem nó như thế nào xử lý nó:

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.."); 
     } 
    } 
} 
+2

Điều này có vẻ là giải pháp C# tốt nhất hoạt động cho tất cả các tình huống thoát! Hoạt động hoàn hảo! Cảm ơn :) – HansLindgren

+0

mã không hoạt động trong windows xp sp3. nếu chúng ta đóng ứng dụng từ trình quản lý tác vụ. – Avinash

0

Là một ví dụ điển hình có thể được giá trị nó để navigate to this project và xem làm thế nào để xử lý thoát quá trình ngữ pháp hoặc tại đây đoạn mã từ 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; 
    } 
Các vấn đề liên quan