ตัวจัดการข้อยกเว้น. NET Global ในแอปพลิเคชันคอนโซล


199

คำถาม: ฉันต้องการกำหนดตัวจัดการข้อยกเว้นส่วนกลางสำหรับข้อยกเว้นที่ไม่สามารถจัดการได้ในแอปพลิเคชันคอนโซลของฉัน ใน asp.net หนึ่งสามารถกำหนดหนึ่งใน global.asax และใน windows applications / บริการหนึ่งสามารถกำหนดดังนี้

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

แต่ฉันจะกำหนดตัวจัดการข้อยกเว้นส่วนกลางสำหรับแอปพลิเคชันคอนโซลได้อย่างไร
currentDomain ดูเหมือนว่าจะไม่ทำงาน (. NET 2.0)?

แก้ไข:

ผิดพลาดโง่ ๆ
ใน VB.NET เราต้องเพิ่มคีย์เวิร์ด "AddHandler" ต่อหน้าโดเมนปัจจุบันหรือมิฉะนั้นจะไม่เห็นเหตุการณ์ UnhandledException ใน IntelliSense ...
นั่นเป็นเพราะคอมไพเลอร์ VB.NET และ C # ปฏิบัติต่อการจัดการเหตุการณ์ต่างกัน

คำตอบ:


283

ไม่นั่นเป็นวิธีที่ถูกต้องที่จะทำ สิ่งนี้ทำงานได้อย่างที่ควรจะเป็นสิ่งที่คุณสามารถทำได้จาก:

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);
    }
}

โปรดทราบว่าคุณไม่สามารถตรวจจับชนิดและข้อยกเว้นการโหลดไฟล์ที่สร้างโดย jitter ด้วยวิธีนี้ มันเกิดขึ้นก่อนที่วิธีการหลัก () ของคุณจะเริ่มทำงาน การจับสิ่งเหล่านั้นต้องใช้เวลาในการกระวนกระวายใจเลื่อนรหัสความเสี่ยงไปยังวิธีอื่นและใช้แอตทริบิวต์ [MethodImpl (MethodImplOptions.NoInlining)]


3
ฉันใช้สิ่งที่คุณเสนอมาที่นี่ แต่ไม่ต้องการออกจากแอปพลิเคชัน ฉันต้องการเข้าสู่ระบบและดำเนินการตามกระบวนการต่อไป (โดยไม่มีConsole.ReadLine()การรบกวนของโปรแกรมอื่น ๆ ) แต่สิ่งที่ฉันได้รับคือข้อยกเว้นการเพิ่มซ้ำอีกครั้งและอีกครั้งและอีกครั้ง

3
@Shahrooz Jefri: คุณไม่สามารถดำเนินการต่อเมื่อคุณได้รับข้อยกเว้นที่ไม่สามารถจัดการได้ สแต็กจะเลอะและนี่คือเทอร์มินัล หากคุณมีเซิร์ฟเวอร์สิ่งที่คุณสามารถทำได้ใน UnhandledExceptionTrapper จะรีสตาร์ทโปรแกรมด้วยอาร์กิวเมนต์บรรทัดคำสั่งเดียวกัน
Stefan Steiger

6
มันแน่นอนที่สุด! เราไม่ได้พูดถึงเหตุการณ์ Application.ThreadException ที่นี่
Hans Passant

4
ฉันคิดว่ากุญแจสำคัญในการทำความเข้าใจคำตอบนี้และความคิดเห็นในการตอบกลับคือการตระหนักว่ารหัสนี้แสดงให้เห็นในขณะนี้เพื่อตรวจสอบข้อยกเว้น แต่ไม่ "จัดการ" อย่างเต็มที่ตามที่คุณอาจอยู่ในบล็อกลอง / จับปกติ . ดูคำตอบอื่น ๆ ของฉันสำหรับรายละเอียด หากคุณ "จัดการ" ข้อยกเว้นด้วยวิธีนี้คุณไม่มีตัวเลือกให้ทำงานต่อเมื่อเกิดข้อยกเว้นในเธรดอื่น ในแง่นั้นอาจกล่าวได้ว่าคำตอบนี้ไม่ได้ "จัดการ" อย่างสมบูรณ์ยกเว้นหาก "จัดการ" คุณหมายถึง "กระบวนการและดำเนินการต่อ"
BlueMonkMN

4
ไม่ต้องพูดถึงมีเทคโนโลยีมากมายที่จะทำงานในเธรดที่แตกต่างกัน ตัวอย่างเช่น Task Parallel Library (TPL) มีวิธีของตนเองในการตรวจจับข้อยกเว้นที่ไม่สามารถจัดการได้ ดังนั้นการพูดแบบนี้ไม่ได้ผลสำหรับทุกสถานการณ์เป็นเรื่องน่าหัวเราะไม่มีสถานที่ใดที่จับได้ทุกอย่างใน C # แต่ขึ้นอยู่กับเทคโนโลยีที่คุณใช้มีสถานที่ต่าง ๆ ที่คุณสามารถขอได้
Doug

23

หากคุณมีแอพพลิเคชั่นที่มีเธรดเดียวคุณสามารถใช้ลอง / จับได้ง่ายในฟังก์ชั่นหลักอย่างไรก็ตามสิ่งนี้จะไม่ครอบคลุมถึงข้อยกเว้นที่อาจถูกโยนออกไปนอกฟังก์ชั่นหลักในเธรดอื่น ๆ ความเห็น) รหัสนี้แสดงให้เห็นว่าข้อยกเว้นสามารถทำให้แอปพลิเคชั่นยุติได้แม้ว่าคุณจะพยายามจัดการกับมันใน Main (สังเกตว่าโปรแกรมออกจากระบบได้อย่างสง่างามอย่างไรถ้าคุณกด Enter และอนุญาตให้แอปพลิเคชันออกจากระบบก่อนที่จะเกิดข้อยกเว้น มันยุติอย่างไม่มีความสุข):

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;
   }
}

คุณสามารถรับการแจ้งเตือนเมื่อเธรดอื่นโยนข้อยกเว้นเพื่อทำการล้างบางอย่างก่อนที่แอปพลิเคชันจะออก แต่เท่าที่ฉันสามารถบอกได้คุณไม่สามารถจากแอปพลิเคชันคอนโซลบังคับให้แอปพลิเคชันทำงานต่อไปหากคุณไม่จัดการข้อยกเว้น บนเธรดที่ถูกส่งออกมาโดยไม่ใช้ตัวเลือกความเข้ากันได้ที่คลุมเครือเพื่อทำให้แอปพลิเคชันทำงานเหมือนที่ใช้กับ. NET 1.x รหัสนี้แสดงให้เห็นว่าสามารถแจ้งเตือนเธรดหลักของข้อยกเว้นที่มาจากเธรดอื่น ๆ ได้อย่างไร แต่จะยังคงสิ้นสุดอย่างไม่มีความสุข:

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;
   }
}

ดังนั้นในความคิดของฉันวิธีที่สะอาดที่สุดในการจัดการในคอนโซลแอ็พพลิเคชันคือเพื่อให้แน่ใจว่าทุกเธรดมีตัวจัดการข้อยกเว้นที่ระดับรูต:

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 ไม่ทำงานในโหมดการเปิดตัวสำหรับข้อผิดพลาดที่ไม่คาดคิด: /
Muflix

12

คุณต้องจัดการข้อยกเว้นจากกระทู้:

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

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

อ๊ะขออภัยสำหรับ winforms สำหรับเธรดใด ๆ ที่คุณใช้ในแอปพลิเคชันคอนโซลคุณจะต้องใส่ในบล็อก try / catch เธรดพื้นหลังที่พบข้อยกเว้น unhandled ไม่ทำให้แอปพลิเคชันสิ้นสุด


1

ฉันเพิ่งได้รับแอปพลิเคชั่นคอนโซล VB.NET รุ่นเก่าและจำเป็นต้องติดตั้ง Global Exception Handler เนื่องจากคำถามนี้กล่าวถึง VB.NET สองสามครั้งและถูกติดแท็กด้วย VB.NET แต่คำตอบอื่น ๆ ทั้งหมดที่อยู่ใน C # ฉันคิดว่าฉันจะเพิ่มไวยากรณ์ที่แน่นอนสำหรับแอปพลิเคชัน 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

ผมใช้ความคิดเห็นเครื่องหมายแทนคำพูดเดียวที่นี่เพราะกองมากเกินดูเหมือนจะจัดการกับการเน้นไวยากรณ์บิตที่ดีขึ้นด้วยREMREM


-13

สิ่งที่คุณพยายามจะทำงานตามเอกสาร MSDN สำหรับ. Net 2.0 นอกจากนี้คุณยังสามารถลอง / จับได้ทันทีในจุดที่อยู่รอบ ๆ จุดเริ่มต้นของคุณสำหรับแอปคอนโซล

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

และตอนนี้ที่จับของคุณจะจัดการกับสิ่งที่ไม่ได้จับ ( ในหัวข้อหลัก ) มันอาจเป็นระบบที่สง่างามและแม้กระทั่งการรีสตาร์ทในกรณีที่คุณต้องการหรือคุณสามารถปล่อยให้แอปพลิเคชันนั้นตายและบันทึกข้อยกเว้น คุณจะเพิ่มในที่สุดหากคุณต้องการทำความสะอาดใด ๆ แต่ละเธรดจะต้องมีข้อยกเว้นระดับสูงของตัวเองจัดการคล้ายกับหลัก

แก้ไขเพื่อชี้แจงประเด็นเกี่ยวกับกระทู้ตามที่ BlueMonkMN ระบุและแสดงรายละเอียดในคำตอบของเขา


1
จริงๆแล้วข้อยกเว้นยังคงถูกโยนออกไปนอกบล็อกหลัก () นี่ไม่ใช่ "จับทั้งหมด" อย่างที่คุณคิด ดูคำตอบของ @Hans
Mike Atlas

@ ไมค์แรกฉันบอกว่าวิธีที่เขาทำมันถูกต้องและว่าเขาสามารถลอง / จับในหลัก ฉันไม่แน่ใจว่าทำไมคุณ (หรือคนอื่น) ให้ฉันลงคะแนนเมื่อฉันเห็นด้วยกับฮันส์เพียงแค่ให้คำตอบอื่นที่ฉันไม่ได้คาดหวังว่าจะได้รับการตรวจสอบ ที่ไม่ยุติธรรมจริง ๆ แล้วจะบอกว่าทางเลือกไม่ถูกต้องโดยไม่มีการพิสูจน์ใด ๆ เป็นวิธียกเว้นที่สามารถจับได้โดยกระบวนการ AppDomain UnhandledException ที่ลอง / จับในหลักไม่สามารถจับ ฉันคิดว่ามันหยาบคายที่จะพูดอะไรบางอย่างผิดปกติโดยไม่ต้องพิสูจน์ว่าทำไมมันผิดเพียงแค่พูดว่าเป็นเช่นนั้น
Rodney S. Foley

5
ฉันได้โพสต์ตัวอย่างที่คุณต้องการ โปรดรับผิดชอบและลบคะแนนโหวตที่ไม่เกี่ยวข้องออกจากคำตอบเก่า ๆ ของ Mike หากคุณยังไม่มี (ไม่มีความสนใจส่วนตัวเพียงแค่ไม่ชอบเห็นการใช้ระบบในทางที่ผิด)
BlueMonkMN

3
แต่คุณยังคงเล่น "เกม" เดียวกันกับที่เขาทำอยู่ในทางที่แย่ลงเพราะมันเป็นการตอบโต้ที่บริสุทธิ์ไม่ได้ขึ้นอยู่กับคุณภาพของคำตอบ นั่นไม่ใช่วิธีการแก้ปัญหาเพียงทำให้แย่ลงเท่านั้น มันแย่มากโดยเฉพาะเมื่อคุณตอบโต้คนที่มีข้อกังวลที่ถูกกฎหมายเกี่ยวกับคำตอบของคุณ
BlueMonkMN

3
โอ้ฉันจะเพิ่มว่าการลงคะแนนเสียงไม่ได้มีไว้สำหรับคนที่ "เป็นคนงี่เง่าที่สมบูรณ์หรือละเมิดกฎ" แต่เพื่อตัดสินคุณภาพของคำตอบ สำหรับฉันแล้วดูเหมือนว่าคำตอบที่ลงคะแนนเพื่อ "แสดงความคิดเห็น" กับผู้ที่ให้คำตอบนั้นเป็นการละเมิดที่ใหญ่กว่าคำตอบที่ลงคะแนนโดยพิจารณาจากเนื้อหาของคำตอบนั้นโดยไม่คำนึงว่าการลงคะแนนนั้นถูกต้องหรือไม่ อย่าใช้ / ทำให้เป็นส่วนตัว
BlueMonkMN
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.