Bir WPF uygulamasında genel olarak istisnalar mı yakalanıyor?


242

Bazı bölümlerinin çalışma zamanında istisnalar atabileceği bir WPF uygulamamız var. Küresel olarak işlenmeyen bir istisna yakalamak ve bunları günlüğe kaydetmek istiyorum, ancak aksi takdirde hiçbir şey olmamış gibi program yürütmeye devam etmek istiyorum (VB'ler gibi On Error Resume Next).

Bu C # ile mümkün mü? Ve eğer öyleyse, istisna işleme kodunu tam olarak nereye koymam gerekir?

Şu anda ben try/ catchetrafında sarmak ve oluşabilecek tüm istisnaları yakalar tek bir nokta göremiyorum . Ve o zaman bile, yakalama nedeniyle yürütülen her şeyi bırakmış olurdum. Yoksa burada korkunç yanlış yönlerde mi düşünüyorum?

ETA: Aşağıdaki birçok kişi işaret etti: Uygulama nükleer santralleri kontrol etmek için değil. Eğer çökerse o kadar büyük bir şey değil ama çoğunlukla UI ile ilgili rastgele istisnalar, kullanılacağı bağlamda bir sıkıntıdır. Bunlardan birkaç tane vardı (ve muhtemelen hala) ve bir eklenti mimarisi kullandığından ve başkaları tarafından genişletilebildiğinden (bu durumda öğrenciler de; bu nedenle tamamen hatasız kod yazabilen deneyimli geliştiriciler yok ).

Yakalanan istisnalar gelince: Onları tam yığın izleme de dahil olmak üzere bir günlük dosyasına günlüğe kaydederim. Bu egzersizin bütün mesele buydu. VB'nin OERN'ine benzetimimi yapan insanlara tam anlamıyla karşı çıkmak için.

Bazı hata sınıflarını körü körüne görmezden gelmenin tehlikeli olduğunu ve uygulama örneğimi bozabileceğini biliyorum. Daha önce de belirtildiği gibi, bu program herkes için kritik bir iş değildir. Doğru akıllarında hiç kimse, insan medeniyetinin hayatta kalmasına bahse girmez. Sadece bazı tasarım yaklaşımlarını test etmek için küçük bir araçtır. yazılım Mühendisliği.

Uygulamanın hemen kullanımı için, bir istisnada gerçekleşebilecek çok fazla şey yoktur:

  • İstisna işleme yok - hata iletişim kutusu ve uygulama çıkışı. Muhtemelen başka bir konuda deney tekrarlanmalıdır. Hiç hata kaydedilmedi, bu talihsiz bir durum.
  • Genel istisna işleme - iyi huylu hata sıkıştı, zarar verilmedi. Bu, geliştirme sırasında gördüğümüz tüm hatalardan yargılanan genel bir durum olmalıdır. Bu tür hataları göz ardı etmenin derhal bir sonucu olmamalıdır; Çekirdek veri yapıları bundan kolayca kurtulabilecek kadar iyi test edilmiştir.
  • Genel istisna işleme - ciddi bir hata sıkıştı, muhtemelen sonraki bir noktada çökebilir. Bu nadiren olabilir. Daha önce hiç görmedik. Hata yine de günlüğe kaydedilir ve bir kilitlenme kaçınılmaz olabilir. Yani bu kavramsal olarak ilk duruma benzer. Bunun dışında bir yığın izimiz var. Ve çoğu durumda kullanıcı fark etmez bile.

Program tarafından oluşturulan deney verilerine gelince: Ciddi bir hata en kötü ihtimalle hiçbir verinin kaydedilmesine neden olmaz. Deneyin sonucunu bu kadar az değiştiren küçük değişiklikler pek olası değildir. Ve bu durumda bile, sonuçlar şüpheli görünüyorsa hata günlüğe kaydedilir; eğer toplam bir aykırı değerse, bu veri noktasını yine de atabilir.

Özetlemek gerekirse: Evet, kendimi hala en azından kısmen aklı başında görüyorum ve programın tümüyle kötü olmasını sağlayan bir global istisna işleme rutini düşünmüyorum. Daha önce iki kez söylendiği gibi, uygulamaya bağlı olarak böyle bir karar geçerli olabilir. Bu durumda, tam ve mutlak bir karar değil, geçerli bir karar olarak değerlendirildi. Diğer tüm başvurularda bu karar farklı görünebilir. Ama lütfen beni veya bu projede çalışan diğer insanları, sadece hataları görmezden geldiğimiz için dünyayı patlatmakla suçlamayın.

Yan not: Bu uygulama için tam olarak bir kullanıcı var. Milyonlarca kullanıcı tarafından istisnalar balonunun maliyetinin ilk etapta çok farklı olacağı milyonlarca kişi tarafından kullanılan bir şey değil.


Bu gerçekten düşünmüyor - evet, muhtemelen uygulamanın çıkmasını istiyorsunuz. AMA, istisnayı ilk olarak StackTrace ile kaydetmek iyi olmaz mıydı? Kullanıcıdan aldığınız tek şey "Uygulamanız bu düğmeye bastığımda çöktü" ise, yeterli bilgiye sahip olmayacağınız için sorunu asla çözemeyebilirsiniz. Ancak, uygulamayı daha hoş bir şekilde iptal etmeden önce istisnayı ilk kez kaydettiyseniz, önemli ölçüde daha fazla bilgiye sahip olursunuz.
Russ

Şimdi bu noktayı biraz ayrıntılandırdım. İlgili riskleri biliyorum ve bu özel uygulama için kabul edilebilir sayıldı. Ve UI bazı güzel animasyon yapmaya çalışırken, aut indeksi olarak basit olarak bir şey için uygulamayı iptal olduğu overkill ve gereksiz. Evet, kesin nedeni bilmiyorum ama hata vakalarının büyük çoğunluğunun iyi huylu olduğu iddiasını destekleyecek verilerimiz var. Maskelediğimiz ciddi olanlar uygulamanın çökmesine neden olabilir, ancak yine de küresel istisna işleme olmadan bu olurdu.
Joey

Başka bir yan not: Bu yaklaşımla herhangi bir çökmeyi önlerseniz, kullanıcılar büyük olasılıkla sevecektir.
lahjaton_j

Visual Studio 2010 için C # içindeki WPF (İşleyicilerin en eksiksiz koleksiyonu) örneğinde İşlenmeyen Özel Durumları İşleme konusuna bakın . AppDomain.CurrentDomain.FirstChanceException, Application.DispatcherUnhandledException ve AppDomain.CurrentDomain.UnhandledException dahil 5 örneği vardır.
user34660

On Error Resume NextC # VB benzeri kod akışının mümkün olmadığını eklemek istiyorum . Bir Exception(C # "hataları" içermedikten sonra) bir sonraki ifadeyle devam edemezsiniz: yürütme bir catchblokta veya aşağıdaki cevaplarda açıklanan olay işleyicilerinden birinde devam eder .
mike

Yanıtlar:


191

Kullanın Application.DispatcherUnhandledException Event. Özet için bu soruya bakın ( Drew Noakes'ın cevabına bakınız ).

Veritabanına kaydetmeye çalışırken yığın taşması, bitmiş bellek veya ağ bağlantısının kesilmesi gibi uygulamanızın başarılı bir şekilde devam etmesini engelleyen istisnalar olacağını unutmayın.


Teşekkürler. Ve evet, iyileşemediğim istisnalar olduğunun farkındayım, ancak olabileceklerin büyük çoğunluğu bu durumda iyi huylu.
Joey

14
Kural dışı durumunuz bir arka plan iş parçacığında (örneğin, ThreadPool.QueueUserWorkItem kullanarak) oluşursa, bu çalışmaz.
Szymon Rozga

@siz: iyi bir nokta, ama bunlar normal bir deneme bloğuyla halledilebilir, değil mi?
David Schmitt

1
@PitiOngmongkolkul: İşleyici, ana döngüden olay olarak adlandırılır. Olay işleyicileri geri döndüğünde, uygulamanız normal şekilde devam eder.
David Schmitt

4
Görünüşe göre e.Handled = true ayarlamamız gerekiyor, burada e, programlardan çıkan varsayılan işleyiciyi atlamak için bir DispatcherUnhandledExceptionEventArgs'dir. msdn.microsoft.com/en-us/library/…
Piti Ongmongkolkul

55

AppDomain'deki tüm iş parçacıklarından , UI dağıtım iş parçacığından ve zaman uyumsuz işlevlerden atılan özel durumları yakalayacak NLog kullanan örnek kod :

Uygulama.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
Bu en eksiksiz cevap! Taks zamanlayıcı istisnaları dahildir. Benim için en iyisi, temiz ve basit kod.
Nikola Jovic

3
Son zamanlarda bir müşteride bir App.config bozulması vardı ve NLog App.config'den okumaya ve bir istisna atmaya çalıştığı için Uygulaması bile başlamıyordu. Bu istisna statik logger başlatıcısında olduğundan , UnhandledExceptionişleyici tarafından yakalanmadı . Ne olduğunu bulmak için Windows Olay Günlüğü Görüntüleyicisi'ne bakmak zorunda kaldım ...
heltonbiker

Ben uygulama UI İstisnaları e.Handled = true;UnhandledException
çökmeyecek

30

AppDomain.UnhandledException Etkinliği

Bu olay yakalanmamış istisnaların bildirilmesini sağlar. Sistem varsayılan işleyicisi özel durumu kullanıcıya bildirmeden ve uygulamayı sonlandırmadan önce uygulamanın özel durum hakkındaki bilgileri günlüğe kaydetmesine olanak tanır.

   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 olayı varsayılan uygulama etki alanında işlenirse, iş parçacığının hangi uygulama etki alanında başladığından bağımsız olarak, herhangi bir iş parçacığında işlenmeyen özel durum için orada oluşturulur. İş parçacığı UnhandledException için bir olay işleyicisi olan bir uygulama etki alanında başlatılırsa, olay bu uygulama etki alanında oluşturulur. Bu uygulama etki alanı varsayılan uygulama etki alanı değilse ve varsayılan uygulama etki alanında da bir olay işleyicisi varsa, olay her iki uygulama etki alanında da yükseltilir.

Örneğin, bir iş parçacığının "AD1" uygulama etki alanında başladığını, "AD2" uygulama etki alanında bir yöntemi çağırdığını ve oradan "AD3" uygulama etki alanında bir istisna attığı bir yöntemi çağırdığını varsayalım. UnhandledException olayının yükseltilebileceği ilk uygulama etki alanı "AD1" dir. Bu uygulama etki alanı varsayılan uygulama etki alanı değilse, olay varsayılan uygulama etki alanında da yükseltilebilir.


işaret ettiğiniz URL'den kopyalama (bence yalnızca konsol uygulamalarından ve WinForms uygulamalarından bahsediyor): ".NET Framework 4'ten başlayarak, bu olay, yığın taşmaları veya olay işleyicisi güvenlik açısından kritik değilse ve HandleProcessCorruptedStateExceptionsAttribute özniteliğine sahip değilse erişim ihlalleri. "
George Birbilis

1
@GeorgeBirbilis: Uygulama yapıcısında UnhandledException olayına abone olabilirsiniz.
CharithJ

18

Buna ek olarak, burada başkalarının bahsettikleri, Application.DispatcherUnhandledException(ve benzerlerini )

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

içinde app.configuygulamayı kapanmasını ikincil iş parçacığı istisna önleyecektir.


2

İşte tam örnek kullanarak 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 Resume Next?" Kulağa korkutucu geliyor. İlk öneri bunu yapma. İkinci öneri bunu yapmayın ve düşünmeyin. Arızalarınızı daha iyi izole etmelisiniz. Bu soruna nasıl yaklaşılacağı konusunda, kodunuzun nasıl yapılandırıldığına bağlıdır. MVC veya benzeri bir desen kullanıyorsanız, bu çok zor olmamalı ve kesinlikle küresel bir istisna yutması gerektirmez. İkinci olarak, log4net gibi iyi bir günlük kütüphanesi arayın veya izleme kullanın. Ne tür istisnalar hakkında konuştuğunuz ve uygulamanızın hangi bölümlerinin istisnaların atılmasına neden olabileceği gibi daha fazla ayrıntıyı bilmemiz gerekir.


2
Mesele şu ki, yakalanmamış istisnalardan sonra bile uygulamayı çalışır durumda tutmak istiyorum. Ben sadece onları günlüğe kaydetmek istiyorum (ihtiyaçlarımıza göre bunun için özel bir günlükleme çerçevemiz var) ve bazı eklentilerin kullanıcı etkileşim kodunda garip bir şey yaptığı için tüm programı iptal etmem gerekmiyor. Şimdiye kadar gördüğüm kadarıyla, farklı parçalar birbirlerini gerçekten tanımadığı için nedenleri temiz bir şekilde izole etmek önemsiz değil.
Joey

9
Anlıyorum, ancak incelemediğiniz bir istisnadan sonra devam etmek konusunda çok dikkatli olmalısınız, veri bozulması olasılığı ve benzeri dikkate alınmalıdır.
BobbyShaftoe

3
BobbyShaftoe ile aynı fikirdeyim. Bu doğru yol değil. Uygulamanın çökmesine izin verin. Arıza bazı eklentide ise, eklentiyi düzeltmek için birisini onarın veya alın. Çalıştırmak son derece tehlikelidir. Garip yan etkiler elde edeceksiniz ve uygulamanızda meydana gelen hatalar için mantıklı bir açıklama bulamayacağınız bir noktaya ulaşacaksınız.

1
Şimdi bu noktayı biraz ayrıntılandırdım. İlgili riskleri biliyorum ve bu özel uygulama için kabul edilebilir sayıldı. Ve UI bazı güzel animasyonlar yapmaya çalışırken sınırların dışında bir indeks kadar basit bir şey için uygulamayı iptal etmek abartılı ve gereksiz. Evet, kesin nedeni bilmiyorum ama hata vakalarının büyük çoğunluğunun iyi huylu olduğu iddiasını destekleyecek verilerimiz var. Maskelediğimiz ciddi olanlar uygulamanın çökmesine neden olabilir, ancak yine de küresel istisna işleme olmadan bu olurdu.
Joey

Bu bir yorum olmalı, cevap değil. "Bunu nasıl yapmalıyım" ile "muhtemelen yapmamalısınız" yanıtını vermek bir cevap değildir, bu bir yorumdur.
Josh Noe
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.