كيفية الحصول على سلسلة كاملة من الاستثناءات في معالج الأحداث Application.ThreadException؟

StackOverflow https://stackoverflow.com/questions/61354

  •  09-06-2019
  •  | 
  •  

سؤال

كنت أعمل للتو على إصلاح معالجة الاستثناءات في تطبيق .NET 2.0، وواجهت مشكلة غريبة في Application.ThreadException.

ما أريده هو أن أكون قادرًا على التقاط جميع الاستثناءات من الأحداث خلف عناصر واجهة المستخدم الرسومية (على سبيل المثال.Button_Click، وما إلى ذلك).أريد بعد ذلك تصفية هذه الاستثناءات حسب "الإماتة"، على سبيل المثال.مع بعض أنواع الاستثناءات، يجب أن يستمر التطبيق في العمل، ومع أنواع أخرى يجب الخروج.

في تطبيق .NET 2.0 آخر، تعلمت أنه بشكل افتراضي فقط في وضع التصحيح تترك الاستثناءات استدعاء Application.Run أو Application.DoEvents.في وضع الإصدار، لا يحدث هذا، ويجب "التقاط" الاستثناءات باستخدام الحدث Application.ThreadException.

لكن الآن لاحظت ذلك كائن الاستثناء الذي تم تمريره في ThreadExceptionEventArgs لحدث Application.ThreadException هو دائمًا الاستثناء الأعمق في سلسلة الاستثناءات.لأغراض التسجيل/تصحيح الأخطاء/التصميم، أريد حقًا سلسلة الاستثناءات بأكملها.ليس من السهل تحديد النظام الخارجي الذي فشل، على سبيل المثال، عندما تتمكن من التعامل مع استثناء المقبس:عندما تكون ملفوفة على سبيل المثال.NpgsqlException، فأنت على الأقل تعلم أنها مشكلة في قاعدة البيانات.

إذن، كيف يمكن الوصول إلى سلسلة الاستثناءات بأكملها من هذا الحدث؟ هل هذا ممكن أم أحتاج إلى تصميم التعامل مع الاستثناء الخاص بي بطريقة أخرى؟

لاحظ أنني - نوعًا ما - لدي الحل البديل استخدام Application.SetUnhandledExceptionMode, ، لكن هذا أبعد ما يكون عن المثالية لأنه سيتعين عليّ أن أقوم بتدوير حلقة الرسائل الخاصة بي.

يحرر:لمنع المزيد من الأخطاء، طريقة GetBaseException () لا تفعل ما أريد:إنه يُرجع فقط الاستثناء الأعمق، في حين أن الشيء الوحيد الذي لدي بالفعل هو الاستثناء الأعمق.أريد أن أصل إلى الاستثناء الأبعد!

هل كانت مفيدة؟

المحلول

تمت صياغة هذا السؤال بشكل أكثر فائدة والإجابة عليه هنا:

لماذا يصل الاستثناء الداخلي إلى معالج ThreadException وليس الاستثناء الفعلي الذي تم طرحه؟

نصائح أخرى

لقد حاولت إعادة إنتاج هذا السلوك (أحصل دائمًا على الاستثناء الأعمق)،
لكنني أحصل على الاستثناء الذي أتوقعه، مع الحفاظ على كافة الاستثناءات الداخلية سليمة.

إليك الرمز الذي استخدمته للاختبار:

   Private Shared Sub Test1()
      Try
         Test2()
      Catch ex As Exception
         Application.OnThreadException(New ApplicationException("test1", ex))
      End Try
   End Sub

   Private Shared Sub Test2()
      Try
         Test3()
      Catch ex As Exception
         Throw New ApplicationException("test2", ex)
      End Try
   End Sub

   Private Shared Sub Test3()
      Throw New ApplicationException("blabla")
   End Sub

Private Shared Sub HandleAppException(ByVal sender As Object, ByVal e As ThreadExceptionEventArgs)
...
End Sub

يعالج Sub HandleAppException Application.ThreadException.يتم استدعاء الأسلوب Test1() أولاً.
هذه هي النتيجة (e باسم ThreadExceptionEventArgs) التي أحصل عليها في HandleAppException:

ThreadException http://mediasensation.be/dump/?download=ThreadException.jpg

إذا قمت فقط بالتقاط و(إعادة) طرح الاستثناءات، فلن تظهر أي استثناءات InnerExceptions، ولكن سيتم إلحاقها بالاستثناء.تتبع المكدس, ، مثله:

في SO.Test3() في Test.vb:السطر 166
في SO.Test2() في Test.vb:السطر 159
في SO.Test1() في Test.vb:السطر 151

عادةً، ستفقد فقط سلسلة الاستثناءات بأكملها باستثناء الاستثناء الأساسي في معالج الاستثناء Application.ThreadException إذا حدث الاستثناء في مؤشر ترابط آخر.

من مكتبة MSDN:

يسمح هذا الحدث بتطبيق Windows Forms الخاص بك بالتعامل مع الاستثناءات غير المعروفة التي تحدث في نماذج ويندوز الخيوط.قم بإرفاق معالجات الأحداث الخاصة بك بحدث ThreadException للتعامل مع هذه الاستثناءات ، والتي ستترك طلبك في حالة غير معروفة.حيثما أمكن ، يجب التعامل مع الاستثناءات من خلال كتلة معالجة استثناء منظم.

حل:إذا قمت بإجراء عملية الترابط، فتأكد من أن جميع مكالماتك المتعلقة بسلاسل الرسائل/غير المتزامنة موجودة في كتلة محاولة/التقاط.أو كما قلت، يمكنك اللعب باستخدام Application.SetUnhandledExceptionMode.

اكتشفت للتو شيئا مثيرا للاهتمام.ستمنحك أحداث واجهة المستخدم الرسومية المختلفة نتائج مختلفة.سيؤدي الاستثناء الذي تم طرحه من معالج الأحداث Form.Shown إلى قيام Application.ThreadException بالتقاط الاستثناء الداخلي، ولكن نفس التعليمات البرمجية التي يتم تشغيلها في حدث Form.Load ستؤدي إلى حدوث الخارجي تم اكتشاف استثناء في Application.ThreadException.

هل جربت طريقة Exception.GetBaseException؟يؤدي هذا إلى إرجاع الاستثناء الذي أنشأ Application.TreadException.يمكنك بعد ذلك استخدام نفس العملية للارتقاء بالسلسلة للحصول على جميع الاستثناءات.

طريقة Exception.getbaseexception

استنادًا إلى بعض المعلومات الموجودة في هذه السلسلة، استخدمت UnhandledExceptionMode.ThrowException بدلاً من UnhandledExceptionMode.CatchException.ثم أقوم بالتقاط الاستثناء خارج Run() الخاص بالنموذج وهذا يعطيني سلسلة الاستثناءات بأكملها.

وفقًا لوثائق MSDN:

عند تجاوزه في فئة مشتقة، يتم إرجاع الاستثناء الذي يمثل السبب الجذري لواحد أو أكثر من الاستثناءات اللاحقة.

    Public Overridable Function GetBaseException() As Exception
        Dim innerException As Exception = Me.InnerException
        Dim exception2 As Exception = Me
        Do While (Not innerException Is Nothing)
            exception2 = innerException
            innerException = innerException.InnerException
        Loop
        Return exception2
    End Function

يمكنك استخدام صيغة مختلفة في هذا لتحليل سلسلة الاستثناءات.

Public Sub LogExceptionChain(ByVal CurrentException As Exception)

    Dim innerException As Exception = CurrentException.InnerException
    Dim exception2 As Exception = CurrentException

    Debug.Print(exception2.Message) 'Log the Exception

    Do While (Not innerException Is Nothing)

        exception2 = innerException
        Debug.Print(exception2.Message) 'Log the Exception

        'Move to the next exception
        innerException = innerException.InnerException
    Loop

End Sub

هذا من شأنه أن يذهلني بالضبط ما تبحث عنه.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top