ตรวจพบข้อยกเว้นทั่วโลกในแอปพลิเคชัน WPF หรือไม่


242

เรามีแอปพลิเคชั่น WPF ซึ่งบางส่วนของมันอาจเกิดข้อยกเว้นในขณะทำงาน ฉันต้องการจับข้อยกเว้นที่ไม่สามารถจัดการได้ทั่วโลกและบันทึกพวกเขา แต่อย่างอื่นดำเนินการโปรแกรมต่อไปราวกับว่าไม่มีอะไรเกิดขึ้น (kinda เช่น VB's On Error Resume Next)

เป็นไปได้ใน C #? และถ้าเป็นเช่นนั้นฉันจะต้องวางรหัสการจัดการข้อยกเว้นไว้ที่ไหน

ขณะนี้ฉันไม่เห็นจุดใดจุดหนึ่งที่ฉันสามารถล้อมรอบtry/ catchและซึ่งจะจับข้อยกเว้นทั้งหมดที่อาจเกิดขึ้น และถึงอย่างนั้นฉันก็จะเหลืออะไรก็ตามที่ถูกประหารเพราะจับได้ หรือฉันกำลังคิดผิดทางอย่างน่ากลัวที่นี่?

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

สำหรับข้อยกเว้นที่ตรวจพบ: ฉันบันทึกไว้ในไฟล์บันทึกรวมถึงการติดตามสแต็กทั้งหมด นั่นคือจุดรวมของการออกกำลังกายนั้น เพียงเพื่อตอบโต้คนเหล่านั้นที่กำลังเปรียบเทียบกับ OERN ของ VB อย่างแท้จริง

ฉันรู้ว่าการละเว้นข้อผิดพลาดบางประเภทเป็นอันตรายและอาจทำให้แอปพลิเคชันของฉันเสียหาย อย่างที่เคยกล่าวไว้ก่อนหน้านี้โปรแกรมนี้ไม่ได้มีความสำคัญสำหรับทุกคน ไม่มีใครในใจที่ถูกต้องของพวกเขาจะพนันความอยู่รอดของอารยธรรมมนุษย์ในนั้น มันเป็นเพียงเครื่องมือเล็ก ๆ สำหรับการทดสอบแนวทางการออกแบบบางอย่าง วิศวกรรมซอฟต์แวร์.

สำหรับการใช้งานแอปพลิเคชั่นในทันทีมีหลายสิ่งที่อาจเกิดขึ้นได้ยกเว้น:

  • ไม่มีการจัดการข้อยกเว้น - กล่องโต้ตอบข้อผิดพลาดและการออกจากแอปพลิเคชัน ต้องทำการทดสอบซ้ำแม้ว่าจะมีอีกเรื่องหนึ่ง ไม่มีข้อผิดพลาดถูกบันทึกซึ่งเป็นโชคร้าย
  • การจัดการข้อยกเว้นทั่วไป - ข้อผิดพลาดที่เป็นพิษเป็นภัยติดไม่ทำอันตราย นี่ควรเป็นกรณีทั่วไปที่ตัดสินจากข้อผิดพลาดทั้งหมดที่เราเห็นระหว่างการพัฒนา การละเว้นข้อผิดพลาดประเภทนี้ไม่ควรมีผลกระทบในทันที โครงสร้างข้อมูลหลักได้รับการทดสอบอย่างดีพอที่พวกเขาจะอยู่รอดได้อย่างง่ายดาย
  • การจัดการข้อยกเว้นทั่วไป - ข้อผิดพลาดร้ายแรงที่ติดอยู่อาจผิดพลาดได้ในภายหลัง สิ่งนี้อาจเกิดขึ้นได้ยาก เราไม่เคยเห็นมันมาก่อน ข้อผิดพลาดถูกบันทึกต่อไปและความผิดพลาดอาจหลีกเลี่ยงไม่ได้ ดังนั้นนี่คือแนวคิดคล้ายกับกรณีแรก ยกเว้นว่าเรามีการติดตามสแต็ก และในกรณีส่วนใหญ่ผู้ใช้จะไม่ได้แจ้งให้ทราบล่วงหน้า

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

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

หมายเหตุด้านข้าง: มีผู้ใช้หนึ่งรายสำหรับแอปพลิเคชันนั้น ไม่ใช่สิ่งที่เหมือนกับ Windows หรือ Office ที่มีผู้ใช้หลายล้านคนซึ่งค่าใช้จ่ายในการยกเว้นฟองสบู่ให้กับผู้ใช้จะแตกต่างกันมากในตอนแรก


ไม่คิดอย่างนี้ - ใช่คุณอาจต้องการให้แอปพลิเคชันออก แต่จะไม่ดีถ้าหากบันทึกข้อยกเว้นด้วย StackTrace เป็นอันดับแรก หากสิ่งที่คุณได้รับจากผู้ใช้คือ "ใบสมัครของคุณชนเมื่อฉันกดปุ่มนี้" คุณอาจไม่สามารถแก้ไขปัญหาได้เนื่องจากคุณไม่มีข้อมูลเพียงพอ แต่ถ้าคุณบันทึกข้อยกเว้นก่อนที่จะยกเลิกแอปพลิเคชันอย่างมีความสุขคุณจะมีข้อมูลมากขึ้น
Russ

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

หมายเหตุด้านอื่น: หากคุณป้องกันการขัดข้องด้วยวิธีนี้ผู้ใช้มักจะหลงรักมัน
lahjaton_j

ดูของ Windows การจัดการจัดการข้อยกเว้นใน WPF (คอลเลกชันที่สมบูรณ์ที่สุดของรถขน) ตัวอย่างใน C # สำหรับ Visual Studio 2010 มี 5 ตัวอย่างรวมถึง AppDomain.CurrentDomain.FirstChanceException, Application.DispatcherUnhandledException และ AppDomain.CurrentDomain.UnhandledException
user34660

ฉันต้องการเพิ่มว่าการเขียนโค้ดแบบ VB- เหมือนOn Error Resume Nextเป็นไปไม่ได้ใน C # หลังจากException(C # ไม่มี "ข้อผิดพลาด") คุณไม่สามารถดำเนินการต่อด้วยคำสั่งถัดไป: การดำเนินการจะดำเนินการต่อในcatchบล็อก - หรือหนึ่งในตัวจัดการเหตุการณ์ที่อธิบายไว้ในคำตอบด้านล่าง
ไมค์

คำตอบ:


191

Application.DispatcherUnhandledException Eventใช้ ดูคำถามนี้สำหรับการสรุป (ดูคำตอบของ Drew Noakes )

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


ขอบคุณ และใช่ฉันรู้ว่ามีข้อยกเว้นที่ฉันไม่สามารถกู้คืนได้ แต่ส่วนใหญ่ของสิ่งที่อาจเกิดขึ้นนั้นอ่อนโยนในกรณีนี้
Joey

14
หากข้อยกเว้นของคุณเกิดขึ้นในเธรดพื้นหลัง (พูดโดยใช้ ThreadPool.QueueUserWorkItem) สิ่งนี้จะไม่ทำงาน
Szymon Rozga

@siz: จุดดี แต่สิ่งเหล่านั้นสามารถจัดการกับบล็อกลองแบบปกติใน workitem ใช่ไหม?
David Schmitt

1
@PitiOngmongkolkul: ตัวจัดการเรียกว่าเป็นเหตุการณ์จากคุณวนรอบหลัก เมื่อตัวจัดการเหตุการณ์กลับมาแอปของคุณจะดำเนินการตามปกติ
David Schmitt

4
ดูเหมือนว่าเราจำเป็นต้องตั้งค่า e.Handled = true โดยที่ e คือ DispatcherUnhandledExceptionEventArgs เพื่อข้ามตัวจัดการเริ่มต้นซึ่งออกจากโปรแกรม msdn.microsoft.com/en-us/library/…
Piti Ongmongkolkul

55

โค้ดตัวอย่างที่ใช้NLogที่จะตรวจจับข้อยกเว้นที่เกิดขึ้นจากเธรดทั้งหมดใน AppDomainจากเธรด UI dispatcherและจากฟังก์ชัน async :

App.xaml.cs:

public partial class App : Application
{
    private static Logger _logger = LogManager.GetCurrentClassLogger();

    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        SetupExceptionHandling();
    }

    private void SetupExceptionHandling()
    {
        AppDomain.CurrentDomain.UnhandledException += (s, e) =>
            LogUnhandledException((Exception)e.ExceptionObject, "AppDomain.CurrentDomain.UnhandledException");

        DispatcherUnhandledException += (s, e) =>
        {
            LogUnhandledException(e.Exception, "Application.Current.DispatcherUnhandledException");
            e.Handled = true;
        };

        TaskScheduler.UnobservedTaskException += (s, e) =>
        {
            LogUnhandledException(e.Exception, "TaskScheduler.UnobservedTaskException");
            e.SetObserved();
        };
    }

    private void LogUnhandledException(Exception exception, string source)
    {
        string message = $"Unhandled exception ({source})";
        try
        {
            System.Reflection.AssemblyName assemblyName = System.Reflection.Assembly.GetExecutingAssembly().GetName();
            message = string.Format("Unhandled exception in {0} v{1}", assemblyName.Name, assemblyName.Version);
        }
        catch (Exception ex)
        {
            _logger.Error(ex, "Exception in LogUnhandledException");
        }
        finally
        {
            _logger.Error(exception, message);
        }
    }

10
นี่คือคำตอบที่สมบูรณ์แบบที่สุดที่นี่! รวมข้อยกเว้นกำหนดการ Taks ดีที่สุดสำหรับฉันรหัสที่สะอาดและเรียบง่าย
Nikola Jovic

3
เมื่อเร็ว ๆ นี้ฉันมีความเสียหายของ App.config ที่ลูกค้าและแอพของเธอก็ไม่ได้เริ่มต้นเพราะ NLog พยายามอ่านจาก App.config และโยนข้อยกเว้น เนื่องจากข้อยกเว้นนั้นอยู่ในเครื่องมือเริ่มต้นตัวบันทึกแบบคงที่UnhandledExceptionตัวจัดการไม่ได้ถูกจับ ฉันต้องดูตัวแสดงบันทึกเหตุการณ์ของ Windows เพื่อค้นหาสิ่งที่เกิดขึ้น ...
heltonbiker

ผมแนะนำชุดe.Handled = true;ที่UnhandledExceptionว่าโปรแกรมจะไม่ผิดพลาดในข้อยกเว้น UI
Apfelkuacha

30

AppDomain.UnhandledException Event

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

   public App()
   {
      AppDomain currentDomain = AppDomain.CurrentDomain;
      currentDomain.UnhandledException += new UnhandledExceptionEventHandler(MyHandler);    
   }

   static void MyHandler(object sender, UnhandledExceptionEventArgs args) 
   {
      Exception e = (Exception) args.ExceptionObject;
      Console.WriteLine("MyHandler caught : " + e.Message);
      Console.WriteLine("Runtime terminating: {0}", args.IsTerminating);
   }

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

ตัวอย่างเช่นสมมติว่าเธรดเริ่มต้นในโดเมนแอปพลิเคชัน "AD1" เรียกวิธีการในแอปพลิเคชันโดเมน "AD2" และจากนั้นจะเรียกวิธีการในแอปพลิเคชันโดเมน "AD3" ซึ่งจะมีข้อยกเว้น โดเมนแอปพลิเคชันแรกที่สามารถยกเหตุการณ์ UnhandledException ได้คือ "AD1" หากโดเมนแอปพลิเคชันนั้นไม่ใช่โดเมนแอปพลิเคชันเริ่มต้นสามารถเพิ่มกิจกรรมในโดเมนแอปพลิเคชันเริ่มต้นได้


คัดลอกจาก URL ที่คุณชี้ไป (ซึ่งพูดถึงแอพคอนโซลและแอพ WinForms เท่านั้นที่ฉันคิด): "เริ่มต้นด้วย. NET Framework 4 เหตุการณ์นี้ไม่ได้ถูกยกขึ้นสำหรับข้อยกเว้นที่ทำให้สถานะของกระบวนการเสียหายเช่นกองล้นหรือ การละเมิดการเข้าถึงเว้นแต่ว่าตัวจัดการเหตุการณ์สำคัญด้านความปลอดภัยและมีแอตทริบิวต์ HandleProcessCorruptedStateExceptionsAttribute
George Birbilis

1
@GeorgeBirbilis: คุณสามารถสมัครรับข้อมูล UnhandledException ในตัวสร้างแอป
CharithJ

18

นอกจากสิ่งที่คนอื่นพูดถึงที่นี่โปรดทราบว่าการรวม Application.DispatcherUnhandledException(และsimilars ) กับ

<configuration>
  <runtime>  
    <legacyUnhandledExceptionPolicy enabled="1" />
  </runtime>
</configuration>

ในการapp.configจะป้องกันข้อยกเว้นเธรดที่สองของคุณจากการปิดแอปพลิเคชัน


2

นี่คือตัวอย่างที่สมบูรณ์โดยใช้ NLog

using NLog;
using System;
using System.Windows;

namespace MyApp
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        private static Logger logger = LogManager.GetCurrentClassLogger();

        public App()
        {
            var currentDomain = AppDomain.CurrentDomain;
            currentDomain.UnhandledException += CurrentDomain_UnhandledException;
        }

        private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            var ex = (Exception)e.ExceptionObject;
            logger.Error("UnhandledException caught : " + ex.Message);
            logger.Error("UnhandledException StackTrace : " + ex.StackTrace);
            logger.Fatal("Runtime terminating: {0}", e.IsTerminating);
        }        
    }


}

-4

เช่นเดียวกับ "VB's On Error Error ต่อไปหรือไม่" ฟังดูน่ากลัว คำแนะนำแรกไม่ได้ทำ คำแนะนำที่สองไม่ได้ทำและไม่ได้คิดเกี่ยวกับมัน คุณต้องแยกความผิดพลาดของคุณให้ดีขึ้น สำหรับวิธีการแก้ไขปัญหานี้ขึ้นอยู่กับว่าโครงสร้างของคุณเป็นอย่างไร หากคุณกำลังใช้รูปแบบเช่น MVC หรือสิ่งที่คล้ายกันสิ่งนี้ไม่ควรยากเกินไปและแน่นอนว่าไม่จำเป็นต้องมีข้อยกเว้นระดับโลก ประการที่สองมองหาไลบรารีการบันทึกที่ดีเช่น log4net หรือใช้การติดตาม เราจำเป็นต้องทราบรายละเอียดเพิ่มเติมเช่นข้อยกเว้นประเภทใดที่คุณกำลังพูดถึงและส่วนใดของใบสมัครของคุณที่อาจส่งผลให้เกิดข้อยกเว้น


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

9
ฉันเข้าใจ แต่คุณต้องระมัดระวังอย่างมากในการดำเนินการต่อหลังจากข้อยกเว้นที่คุณไม่ได้ตรวจสอบมีความเป็นไปได้ที่ข้อมูลจะเสียหายและอื่น ๆ เพื่อพิจารณา
BobbyShaftoe

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

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

นี่ควรเป็นความเห็นไม่ใช่คำตอบ การตอบสนองต่อ "ฉันจะทำอย่างไร" กับ "คุณอาจไม่ควร" ไม่ใช่คำตอบมันเป็นความคิดเห็น
Josh Noe
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.