Konsol uygulamasında .NET Global özel durum işleyicisi


199

Soru: Konsol uygulamamda işlenmeyen özel durumlar için genel bir özel durum işleyici tanımlamak istiyorum. Asp.net'te global.asax dosyasında tanımlanabilir ve Windows uygulamalarında / hizmetlerinde aşağıdaki gibi tanımlanabilir.

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

Ancak bir konsol uygulaması için genel bir istisna işleyicisi nasıl tanımlayabilirim?
currentDomain çalışmıyor gibi görünüyor (.NET 2.0)?

Düzenle:

Ah, aptalca bir hata.
VB.NET'te, birinin "AddHandler" anahtar sözcüğünü currentDomain'in önüne eklemesi gerekir, aksi takdirde IntelliSense'te UnhandledException olayını görmez ...
VB.NET ve C # derleyicileri olay işlemeyi farklı şekilde ele alır.

Yanıtlar:


283

Hayır, bunu yapmanın doğru yolu bu. Bu tam olarak olması gerektiği gibi çalıştı, belki de çalışabileceğiniz bir şey:

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 tarafından oluşturulan tür ve dosya yükleme istisnalarını bu şekilde yakalayamayacağınızı unutmayın. Bunlar Main () yönteminiz çalışmaya başlamadan önce gerçekleşir. Bunları yakalamak, titremeyi geciktirmeyi gerektirir, riskli kodu başka bir yönteme taşıyın ve [MethodImpl (MethodImplOptions.NoInlining)] özniteliğini uygulayın.


3
Burada önerdiklerinizi uyguladım, ancak uygulamadan çıkmak istemiyorum. Sadece günlüğe kaydetmek ve sürece devam etmek istiyorum Console.ReadLine()(program akışını veya herhangi bir başka rahatsızlığı olmadan . Ama elde ettiğim şey, tekrar tekrar ve tekrar tekrar yükselterek istisnadır.

3
@Shahrooz Jefri: İşlenmemiş bir istisna aldıktan sonra devam edemezsiniz. Yığın bozulur ve bu terminaldir. Bir sunucunuz varsa, UnhandledExceptionTrapper içinde yapabilecekleriniz programı aynı komut satırı bağımsız değişkenleriyle yeniden başlatmaktır.
Stefan Steiger

6
Kesinlikle öyle! Burada Application.ThreadException olayından bahsetmiyoruz.
Hans Passant

4
Bu cevabı anlamanın anahtarı ve cevaplardaki yorumlar, bu kodun istisnayı tespit etmek için şimdi gösterildiğini fark etmektir, ancak devam etme seçeneğiniz olan normal bir deneme / yakalama bloğunda olabildiğince tam olarak "işlemez" . Ayrıntılar için diğer cevabıma bakın. Özel durumu bu şekilde "işlerseniz", başka bir iş parçacığında özel durum oluştuğunda çalışmaya devam etme seçeneğiniz yoktur. Bu anlamda, "işleyiş" demek "işlemek ve çalışmaya devam etmek" demek, bu cevabın istisnayı tam olarak "işlemediği" söylenebilir.
BlueMonkMN

4
Bahsetmiyorum bile farklı iş parçacıklarında çalıştırmak için birçok teknoloji vardır. Örneğin, Görev Paralel Kitaplığı'nın (TPL) işlenmeyen özel durumları yakalamak için kendi yolu vardır. Yani, bunun tüm durumlar için işe yaramadığını söylemek biraz gülünç, C # 'da her şey için tek bir yer yakalama yok, ancak kullandığınız teknolojilere bağlı olarak içine girebileceğiniz çeşitli yerler var.
Doug

23

Tek iş parçacıklı bir uygulamanız varsa, Ana işlevde basit bir deneme / yakalama kullanabilirsiniz, ancak bu, Ana işlevin dışında, diğer iş parçacıklarında (örneğin diğerlerinde belirtildiği gibi) atılabilecek özel durumları kapsamaz. yorum). Bu kod, Main'de işlemeye çalışmanıza rağmen bir özel durumun uygulamanın sonlandırılmasına neden olabileceğini gösterir (enter tuşuna basarsanız ve uygulamanın özel durum oluşmadan önce zarif bir şekilde çıkmasına izin verirseniz, ancak programın düzgün bir şekilde çıkmasına izin verirseniz, ancak çalışmasına izin verirseniz , oldukça mutsuz bir şekilde sona erer):

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

Uygulama çıkmadan önce başka bir iş parçacığının temizleme işlemi gerçekleştirmek için bir istisna attığına dair bildirim alabilirsiniz, ancak anlayabildiğim kadarıyla, bir konsol uygulamasından, özel durumu işlemezseniz uygulamayı çalışmaya devam etmeye zorlayamazsınız Uygulamanın .NET 1.x ile olduğu gibi davranmasını sağlamak için bazı uyumsuzluk seçenekleri kullanmadan atıldığı iş parçacığında. Bu kod, ana iş parçacığının diğer iş parçacıklarından gelen özel durumlardan nasıl haberdar edileceğini gösterir, ancak yine de mutsuz bir şekilde sonlandırılır:

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

Benim görüşüme göre, bir konsol uygulamasında işlemek için en temiz yolu , her iş parçacığının kök düzeyinde bir özel durum işleyicisi olmasını sağlamaktır:

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 beklenmedik hatalar için sürüm modunda çalışmıyor: /
Muflix

12

Ayrıca iş parçacıklarındaki istisnaları da ele almanız gerekir:

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

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

Hata! Bu formlar için üzgünüm, bir konsol uygulamasında kullandığınız herhangi bir iş parçacığı için bir try / catch bloğu içine almanız gerekir. İşlenmeyen özel durumlarla karşılaşan arka plan iş parçacıkları uygulamanın sona ermesine neden olmaz.


1

Eski bir VB.NET konsolu uygulamasını devralmıştım ve bir Global Exception Handler kurmam gerekiyordu. Bu soru VB.NET birkaç kez bahseder ve VB.NET ile etiketlendi, ancak burada diğer tüm cevaplar C #, ben de bir VB.NET uygulaması için tam sözdizimi eklemek düşündüm.

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

Ben kullanılan REMyığın taşması biraz daha iyi olan sözdizimi vurgulama işlemek gibiydi çünkü işaretçisini yerine burada tek tırnak REM.


-13

Denediğiniz şey .Net 2.0 için MSDN belgelerine göre çalışmalıdır. Ayrıca, konsol uygulaması için giriş noktanızın etrafında bir deneme / yakalamayı deneyebilirsiniz.

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

Ve şimdi yakalamanız yakalanmayan her şeyi ele alacak ( ana iplikte ). Zarif olabilir ve hatta isterseniz yeniden başlayabilir veya uygulamanın ölmesine ve istisnayı kaydetmesine izin verebilirsiniz. Eğer herhangi bir temizlik yapmak istersen bir nihayet ekleyeceksin. Her bir iş parçacığı, ana makineye benzer şekilde kendi üst düzey istisna işlemeyi gerektirir.

BlueMonkMN tarafından işaret edilen ve cevabında ayrıntılı olarak gösterilen iplikler hakkındaki noktayı netleştirmek için düzenlendi.


1
Ne yazık ki, istisnalar aslında Main () bloğunun dışında atılabilir. Bu aslında düşündüğünüz gibi bir "hepsini yakala" değildir. @Hans'ın cevabına bakınız.
Mike Atlas

@Mike İlk olarak bunu yapmanın doğru olduğunu ve ana denemeyi / yakalamayı deneyebileceğini söyledim. Hans'la sadece bir çek almayı beklemediğim başka bir cevap sağlayarak benimle (veya başka biriyle) neden oy verdiğinden emin değilim. Bu gerçekten adil değildir ve daha sonra alternatifin yanlış olduğunu söylemek, Main'de bir try / catch'in AppDomain UnhandledException süreci tarafından nasıl yakalanabileceğine dair herhangi bir kanıt sağlamadan yanlış olduğunu söylemek. Neden yanlış olduğunu kanıtlamadan bir şeyin yanlış olduğunu söylemenin kaba olduğunu düşünüyorum, sadece öyle olduğunu söyleyerek böyle yapmıyor.
Rodney

5
İstediğiniz örneği yayınladım. Lütfen sorumlu olun ve alakasız aşağı oylarınızı Mike'ın eski cevaplarından kaldırın. (Kişisel ilgi yok, sadece sistemin bu tür kötüye kullanımını görmekten hoşlanmıyorum.)
BlueMonkMN

3
Yine de aynı "oyunu" oynuyorsunuz, sadece daha kötü bir şekilde çünkü bir cevabın kalitesine dayanmayan saf misilleme. Bu sorunu çözmenin bir yolu değil, sadece daha da kötüleştirin. Cevabınızla ilgili meşru bir kaygısı bile olan birisine misilleme yaptığınızda özellikle kötüdür (gösterdiğim gibi).
BlueMonkMN

3
Ah, aşağı oylamanın "tam bir aptal olan veya kuralları ihlal eden" insanlar için değil, bir cevabın kalitesini yargılamak için tasarlandığını da ekleyeceğim. Bana öyle geliyor ki, onları sağlayan kişiye "yorum yapmak" için verilen oylama cevapları, oylamanın doğru olup olmadığına bakılmaksızın, cevabın içeriğine dayalı olarak aşağı oylama cevaplarından çok daha büyük bir istismardır. Almayın / bu kadar kişisel yapmayın.
BlueMonkMN
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.