Trình xử lý ngoại lệ .NET Global trong ứng dụng bảng điều khiển


198

Câu hỏi: Tôi muốn xác định một trình xử lý ngoại lệ toàn cầu cho các ngoại lệ chưa được xử lý trong ứng dụng bảng điều khiển của tôi. Trong asp.net, người ta có thể định nghĩa một trong global.asax và trong các ứng dụng / dịch vụ của windows, người ta có thể định nghĩa như dưới đây

AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.UnhandledException += new UnhandledExceptionEventHandler(MyExceptionHandler);

Nhưng làm thế nào tôi có thể định nghĩa một trình xử lý ngoại lệ toàn cầu cho một ứng dụng giao diện điều khiển?
currentDomain dường như không hoạt động (.NET 2.0)?

Biên tập:

Argh, sai lầm ngu ngốc.
Trong VB.NET, người ta cần thêm từ khóa "AddHandler" trước currentDomain hoặc người khác không thấy sự kiện UnhandledException trong IntelliSense ...
Đó là vì trình biên dịch VB.NET và C # xử lý sự kiện khác nhau.

Câu trả lời:


283

Không, đó là cách chính xác để làm điều đó. Điều này hoạt động chính xác như nó cần, một cái gì đó bạn có thể làm việc từ có lẽ:

using System;

class Program {
    static void Main(string[] args) {
        System.AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionTrapper;
        throw new Exception("Kaboom");
    }

    static void UnhandledExceptionTrapper(object sender, UnhandledExceptionEventArgs e) {
        Console.WriteLine(e.ExceptionObject.ToString());
        Console.WriteLine("Press Enter to continue");
        Console.ReadLine();
        Environment.Exit(1);
    }
}

Hãy nhớ rằng bạn không thể bắt ngoại lệ tải loại và tệp được tạo bởi jitter theo cách này. Chúng xảy ra trước khi phương thức Main () của bạn bắt đầu chạy. Việc bắt những thứ đó đòi hỏi phải trì hoãn jitter, di chuyển mã rủi ro sang một phương thức khác và áp dụng thuộc tính [MethodImpl (MethodImplOptions.NoInlining)] cho nó.


3
Tôi đã triển khai những gì bạn đề xuất ở đây, nhưng tôi không muốn thoát khỏi ứng dụng. Tôi chỉ muốn đăng nhập nó và tiếp tục quá trình (không có Console.ReadLine()hoặc có bất kỳ sự xáo trộn nào khác về luồng chương trình. Nhưng điều tôi nhận được là ngoại lệ tái lập lại nhiều lần, và một lần nữa.

3
@Shahrooz Jefri: Bạn không thể tiếp tục một khi bạn có một ngoại lệ chưa được xử lý. Ngăn xếp bị rối tung, và đây là thiết bị đầu cuối. Nếu bạn có một máy chủ, những gì bạn có thể làm trong UnhandledExceptionTrapper là khởi động lại chương trình với cùng các đối số dòng lệnh.
Stefan Steiger

6
Nó chắc chắn là có! Chúng tôi không nói về sự kiện Application.ThreadException ở đây.
Hans Passant

4
Tôi nghĩ rằng chìa khóa để hiểu câu trả lời này và các ý kiến ​​trả lời là nhận ra rằng mã này hiện đang phát hiện ngoại lệ, nhưng không hoàn toàn "xử lý" nó như bạn có thể trong một khối thử / bắt bình thường nơi bạn có tùy chọn tiếp tục . Xem câu trả lời khác của tôi để biết chi tiết. Nếu bạn "xử lý" ngoại lệ theo cách này, bạn không có tùy chọn để tiếp tục chạy khi có ngoại lệ xảy ra trên một luồng khác. Theo nghĩa đó, có thể nói rằng câu trả lời này không hoàn toàn "xử lý" ngoại lệ nếu bằng cách "xử lý" bạn có nghĩa là "xử lý và tiếp tục chạy".
BlueMonkMN

4
Chưa kể có rất nhiều công nghệ để chạy trong các luồng khác nhau. Ví dụ, Thư viện song song nhiệm vụ (TPL) có cách riêng để bắt các ngoại lệ chưa được xử lý. Vì vậy, nói rằng điều này không hoạt động trong mọi tình huống là vô lý, không có nơi nào bắt được tất cả mọi thứ trong C #, nhưng tùy thuộc vào công nghệ bạn sử dụng, có nhiều nơi bạn có thể tham gia.
Doug

23

Tuy nhiên, nếu bạn có một ứng dụng một luồng, bạn có thể sử dụng một lần thử / bắt đơn giản trong chức năng Chính, tuy nhiên, điều này không bao gồm các ngoại lệ có thể được ném ra ngoài chức năng Chính, ví dụ như trên các luồng khác (như đã lưu ý khác bình luận). Mã này cho thấy cách một ngoại lệ có thể khiến ứng dụng chấm dứt ngay cả khi bạn đã cố xử lý nó trong Main (chú ý cách chương trình thoát ra một cách duyên dáng nếu bạn nhấn enter và cho phép ứng dụng thoát một cách duyên dáng trước khi ngoại lệ xảy ra, nhưng nếu bạn để nó chạy , nó chấm dứt khá không vui):

static bool exiting = false;

static void Main(string[] args)
{
   try
   {
      System.Threading.Thread demo = new System.Threading.Thread(DemoThread);
      demo.Start();
      Console.ReadLine();
      exiting = true;
   }
   catch (Exception ex)
   {
      Console.WriteLine("Caught an exception");
   }
}

static void DemoThread()
{
   for(int i = 5; i >= 0; i--)
   {
      Console.Write("24/{0} =", i);
      Console.Out.Flush();
      Console.WriteLine("{0}", 24 / i);
      System.Threading.Thread.Sleep(1000);
      if (exiting) return;
   }
}

Bạn có thể nhận được thông báo khi một luồng khác ném ngoại lệ để thực hiện dọn dẹp trước khi thoát ứng dụng, nhưng theo tôi có thể nói, bạn không thể, từ một ứng dụng bảng điều khiển, buộc ứng dụng tiếp tục chạy nếu bạn không xử lý ngoại lệ trên luồng mà nó được ném mà không sử dụng một số tùy chọn tương thích tối nghĩa để làm cho ứng dụng hoạt động giống như với .NET 1.x. Mã này cho thấy cách luồng chính có thể được thông báo về các ngoại lệ đến từ các luồng khác, nhưng vẫn sẽ chấm dứt một cách không vui:

static bool exiting = false;

static void Main(string[] args)
{
   try
   {
      System.Threading.Thread demo = new System.Threading.Thread(DemoThread);
      AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
      demo.Start();
      Console.ReadLine();
      exiting = true;
   }
   catch (Exception ex)
   {
      Console.WriteLine("Caught an exception");
   }
}

static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
   Console.WriteLine("Notified of a thread exception... application is terminating.");
}

static void DemoThread()
{
   for(int i = 5; i >= 0; i--)
   {
      Console.Write("24/{0} =", i);
      Console.Out.Flush();
      Console.WriteLine("{0}", 24 / i);
      System.Threading.Thread.Sleep(1000);
      if (exiting) return;
   }
}

Vì vậy, theo tôi, cách sạch nhất để xử lý nó trong ứng dụng giao diện điều khiển là đảm bảo rằng mọi luồng đều có một trình xử lý ngoại lệ ở cấp gốc:

static bool exiting = false;

static void Main(string[] args)
{
   try
   {
      System.Threading.Thread demo = new System.Threading.Thread(DemoThread);
      demo.Start();
      Console.ReadLine();
      exiting = true;
   }
   catch (Exception ex)
   {
      Console.WriteLine("Caught an exception");
   }
}

static void DemoThread()
{
   try
   {
      for (int i = 5; i >= 0; i--)
      {
         Console.Write("24/{0} =", i);
         Console.Out.Flush();
         Console.WriteLine("{0}", 24 / i);
         System.Threading.Thread.Sleep(1000);
         if (exiting) return;
      }
   }
   catch (Exception ex)
   {
      Console.WriteLine("Caught an exception on the other thread");
   }
}

TRY CATCH không hoạt động trong chế độ phát hành cho các lỗi không mong muốn: /
Muflix

12

Bạn cũng cần xử lý các trường hợp ngoại lệ từ các chủ đề:

static void Main(string[] args) {
Application.ThreadException += MYThreadHandler;
}

private void MYThreadHandler(object sender, Threading.ThreadExceptionEventArgs e)
{
    Console.WriteLine(e.Exception.StackTrace);
}

Rất tiếc, đó là vì winforms, đối với bất kỳ luồng nào bạn đang sử dụng trong ứng dụng bảng điều khiển, bạn sẽ phải gửi kèm theo một khối thử / bắt. Các chủ đề nền gặp phải các ngoại lệ chưa được xử lý không làm cho ứng dụng kết thúc.


1

Tôi vừa thừa hưởng một ứng dụng bảng điều khiển VB.NET cũ và cần thiết lập Trình xử lý ngoại lệ toàn cầu. Vì câu hỏi này đề cập đến VB.NET một vài lần và được gắn thẻ VB.NET, nhưng tất cả các câu trả lời khác ở đây đều có trong C #, tôi nghĩ rằng tôi cũng sẽ thêm cú pháp chính xác cho ứng dụng VB.NET.

Public Sub Main()
    REM Set up Global Unhandled Exception Handler.
    AddHandler System.AppDomain.CurrentDomain.UnhandledException, AddressOf MyUnhandledExceptionEvent

    REM Do other stuff
End Sub

Public Sub MyUnhandledExceptionEvent(ByVal sender As Object, ByVal e As UnhandledExceptionEventArgs)
    REM Log Exception here and do whatever else is needed
End Sub

Tôi đã sử dụng REMđánh dấu nhận xét thay vì trích dẫn đơn ở đây vì Stack Overflow dường như xử lý cú pháp tô sáng tốt hơn một chút với REM.


-13

Những gì bạn đang cố gắng sẽ hoạt động theo tài liệu MSDN cho .Net 2.0. Bạn cũng có thể thử dùng thử / bắt ngay chính xung quanh điểm vào của bạn cho ứng dụng bảng điều khiển.

static void Main(string[] args)
{
    try
    {
        // Start Working
    }
    catch (Exception ex)
    {
        // Output/Log Exception
    }
    finally
    {
        // Clean Up If Needed
    }
}

Và bây giờ, sản phẩm khai thác của bạn sẽ xử lý mọi thứ không bắt được ( trong luồng chính ). Nó có thể duyên dáng và thậm chí khởi động lại nếu bạn muốn, hoặc bạn có thể để ứng dụng chết và đăng nhập ngoại lệ. Bạn woul thêm một cuối cùng nếu bạn muốn làm sạch bất kỳ. Mỗi luồng sẽ yêu cầu xử lý ngoại lệ cấp cao của riêng nó tương tự như chính.

Được chỉnh sửa để làm rõ quan điểm về các chủ đề được BlueMonkMN chỉ ra và thể hiện chi tiết trong câu trả lời của ông.


1
Thật không may, các ngoại lệ vẫn có thể được ném ra ngoài khối Main (). Đây không thực sự là một "bắt tất cả" như bạn nghĩ. Xem câu trả lời của @Hans.
Mike Atlas

@Mike Trước tiên tôi nói cách anh ấy làm là đúng, và anh ấy có thể thử một lần thử / bắt trong chính. Tôi không chắc tại sao bạn (hoặc người khác) lại bỏ phiếu cho tôi khi tôi đồng ý với Hans chỉ đưa ra một câu trả lời khác mà tôi không mong đợi để được kiểm tra. Điều đó không thực sự công bằng, và sau đó nói rằng sự thay thế là sai mà không cung cấp bất kỳ bằng chứng nào về việc làm thế nào một ngoại lệ có thể bị bắt bởi quá trình AppDomain UnhandledException mà một thử / bắt trong Main không thể bắt được. Tôi thấy thật thô lỗ khi nói điều gì đó sai mà không chứng minh tại sao nó sai, chỉ nói nó là như vậy, không làm cho nó trở nên như vậy.
Rodney S. Foley

5
Tôi đã đăng ví dụ bạn đang yêu cầu. Hãy chịu trách nhiệm và loại bỏ phiếu bầu không liên quan của bạn khỏi câu trả lời cũ của Mike nếu bạn không. (Không có lợi ích cá nhân, chỉ không thích nhìn thấy sự lạm dụng hệ thống như vậy.)
BlueMonkMN

3
Tuy nhiên, bạn vẫn chơi cùng một "trò chơi" mà anh ta làm, chỉ theo một cách tồi tệ hơn vì đó là sự trả đũa thuần túy, không dựa trên chất lượng của một câu trả lời. Đó không phải là một cách để giải quyết vấn đề, chỉ làm cho nó tồi tệ hơn. Điều này đặc biệt tệ khi bạn trả thù một người thậm chí có mối quan tâm chính đáng về câu trả lời của bạn (như tôi đã chứng minh).
BlueMonkMN

3
Ồ, tôi cũng sẽ nói thêm rằng bỏ phiếu không dành cho những người "là một thằng ngốc hoàn toàn hoặc vi phạm các quy tắc", mà là để đánh giá chất lượng của một câu trả lời. Đối với tôi, dường như các câu trả lời bỏ phiếu để "bình luận" về người cung cấp cho họ là một sự lạm dụng lớn hơn nhiều so với các câu trả lời bỏ phiếu dựa trên nội dung của câu trả lời bất kể việc bỏ phiếu đó có chính xác hay không. Đừng lấy / làm cho nó cá nhân.
BlueMonkMN
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.