1. 程式人生 > >NET環境下的未處理異常(unhandled exception)的解決方案

NET環境下的未處理異常(unhandled exception)的解決方案

NET環境下的未處理異常(unhandled exception )的解決方案

     .Net 框架提供了非常強大的異常處理機制,同時對一些非託管程式碼很難控制的系統問題比如指標越界,記憶體洩漏等提供了很好的解決方案。相比非託管程式碼構建的系 統,.Net構建的系統更加穩定。不過這並不是說.Net構建的系統就完全無懈可擊,很多由於程式碼的不嚴謹或者系統問題引發的故障將會導致.Net應用程 序產生未捕獲異常,從而導致應用程式異常終止。

      本文將對三種最常見的.Net應用的未捕獲異常處理進行闡述。

      在開始本文之前,讓我們來看看.Net在什麼情況下會產生未捕獲異常。未捕獲異常從定義上說就是結構化異常處理未能捕獲的異常。通俗的講就是發生在Try Catch塊意外的異常。那麼是不是我們在Main函式中加一個Try Catch 塊就可以捕獲全部未捕獲異常了呢?答案是否定的。這裡面有兩種情況無法通過這種方法捕獲:

1. GC 產生的異常,這種異常通常因為Finalize函式中引發未捕獲異常引起。當然這並不絕對,一些系統問題比如記憶體耗盡有時候也會造成GC異常。

2. 主執行緒以為的執行緒引發的未捕獲異常。這些異常我們往往可以線上程的主函式中用Try Catch 來捕獲,但如果系統中使用了外部的元件,或者甚至是.Net 框架自帶的一些系統元件,由這些元件的執行緒引發的異常,呼叫程式碼無法通過Try Catch來捕獲。

從上面兩點來看,即使我們的程式碼在每個地方都加了Try Catch ,也不能百分百杜絕未捕獲異常的發生。

鑑於此,為了提高系統的健壯性和可維護性,我們需要通過一種方法來截獲這些未捕獲異常,並進行適當的處理。

.Net 的設計者已經考慮到這些問題,並且為我們提供了一個叫 UnhandledExceptionEventHandler 的事件,通過這個事件,我們可以截獲未捕獲異常,並進行處理。

這個事件的事件引數UnhandledExceptionEventArgs e, 有兩個屬性,一個是ExceptionObject,這個屬性返回為截獲異常的物件例項。還有一個屬性是IsTerminating,這個屬性告訴我們這 個異常是否會導致應用終止。這裡需要說明的是,對於.Net1.1 和 .Net2.0及以上,情況是不一樣的,.Net1.1 只有在主執行緒中的未捕獲異常才會終止應用程式,而.Net2.0及以上版本則是始終終止應用程式。如果不終止應用程式,而是有CLR 將當前異常消化,系統的執行狀態很可能不可控,最後可能會發生更大的故障,所以.Net2.0以後,對於所有未捕獲異常,一律終止當前應用。這樣看來,對 於.net2.0以上的應用似乎我們截獲未捕獲異常已經毫無意義,其實不然。通過截獲為未捕獲異常,我們可以記錄下程式是再哪裡產生這種未捕獲異常的,以 便程式的開發者改程序序。我們也可以在當前應用退出前為系統做一些其他的保護工作,比如備份資料,告警提示等等。

下面我們來看看三種常見的.Net應用分別如何來截獲未捕獲異常。

  • 控制檯應用

     首先為當前AppDomain 新增  UnhandledExceptionEventHandler

AppDomain.CurrentDomain.UnhandledException  +=

             new  UnhandledExceptionEventHandler(UnhandledExceptionEventHandler);

     再新增事件響應函式

static void  UnhandledExceptionEventHandler( object  sender, UnhandledExceptionEventArgs e)
        
{
            
try
            
{

                 using  (System.IO.FileStream fs  = new  System.IO.FileStream( @" c:/testme.log "

                         System.IO.FileMode.Append, System.IO.FileAccess.Write))

                 {

                     using  (System.IO.StreamWriter w  = new  System.IO.StreamWriter(fs, 

                             System.Text.Encoding.UTF8))

                     {
                        w.WriteLine(e.ExceptionObject);
                    }

                }

            }

            
catch
            
{
            }

        }

現在我們就可以截獲未捕獲異常了

下面是完整的測試程式碼:

public class  TestRaiseException
        
{
            
~ TestRaiseException()
            
{
                
int  i  = 0 ;
                
int  j  = 1 /  i;
            }

        }


        
static void  UnhandledExceptionEventHandler( object  sender, UnhandledExceptionEventArgs e)
        
{
            
try
            
{

                 using  (System.IO.FileStream fs  = new  System.IO.FileStream( @" c:/testme.log " ,

                      System.IO.FileMode.Append, System.IO.FileAccess.Write))

                   {

                     using  (System.IO.StreamWriter w  = new  System.IO.StreamWriter(fs, 

                         System.Text.Encoding.UTF8))

                       {

                        w.WriteLine(e.ExceptionObject);
                    }

                }

            }

            
catch
            
{
            }

        }


        
static void  Main( string [] args)
        
{

            AppDomain.CurrentDomain.UnhandledException  +=

                    new  UnhandledExceptionEventHandler(UnhandledExceptionEventHandler);

 

            TestRaiseException testRaiseException  = new  TestRaiseException();

        }

 程式執行後記錄下日誌如下

System.DivideByZeroException: Attempted to divide by zero.
   at TestCosole.Program.TestRaiseException.Finalize()

  • WinForm

    WinForm 應用通過 Application.ThreadException 事件來截獲未捕獲異常

  詳見 園子裡面另一篇部落格,這裡就不再冗訴。

  • Asp.net

     ASP.NET 應用和前兩種應用有所不同,ASP.NET 一般在後臺執行緒或者執行緒池中產生未捕獲異常,才會導致W3WP.exe終止,並在事件檢視器中產生一條類似下面內容的事件:EventType clr20r3, P1 w3wp.exe, P2 6.0.3790.1830, P3 42435be1, P4 app_web_ncsnb2-n, P5 0.0.0.0, P6 440a4082, P7 5, P8 1, P9 system.nullreferenceexception, P10 NIL. 

     要截獲ASP.NET 的未捕獲異常,我們需要為每個應用程式域安裝事件鉤子

     這個過程需要分兩步完成:

     首先建立一個實現IHttpModule介面的類 

using  System;
using  System.Data;
using  System.Configuration;
using  System.Web;
using  System.Web.Security;
using  System.Web.UI;
using  System.Web.UI.WebControls;
using  System.Web.UI.WebControls.WebParts;
using  System.Web.UI.HtmlControls;

namespace  WebMonitor
{
    
/// <summary>
    
///  Summary description for UnhandledExceptionModule
    
/// </summary>

public class  UnhandledExceptionModule : IHttpModule
    
{
        
static object  _initLock  = new object ();
        
static bool  _initialized  = false ;

        
public  UnhandledExceptionModule()
        
{
            
//
            
//  TODO: Add constructor logic here
            
//
        }



        
void  OnUnhandledException( object  o, UnhandledExceptionEventArgs e)
        
{
            
// Do some thing you wish to do when the Unhandled Exception raised.

            
try
            
{

                 using  (System.IO.FileStream fs  = new  System.IO.FileStream( @" c:/testme.log "

                        System.IO.FileMode.Append, System.IO.FileAccess.Write))

                   {

                     using  (System.IO.StreamWriter w  = new  System.IO.StreamWriter(fs, System.

                            Text.Encoding.UTF8))

                       {

                        w.WriteLine(e.ExceptionObject);
                    }

                }

            }

            
catch
            
{
            }

        }

        
IHttpModule Members
    }

}

 第二步:

 修改web.config

在 system.web 段中加入

< httpModules >
      
< add  name ="UnhandledExceptionModule"  type ="WebMonitor.UnhandledExceptionModule" />
      
    
</ httpModules >

 完成這兩步後,你的ASP.NET 應用就可以截獲未捕獲異常了。

 下面是測試程式碼

using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

public partial class _Default : System.Web.UI.Page 
{
    protected void Page_Load(object sender, EventArgs e)
    {

    }

    protected void TestMe(object state)
    {
        byte[] buf = new byte[2];
        buf[2] = 0;
    }

    protected void Button1_Click(object sender, EventArgs e)
    {

        System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(TestMe), 

                  null);

      }

}

 按下Button1後,w3wp.exe被終止,testme.log 中記錄下了異常資訊如下:

System.IndexOutOfRangeException: Index was outside the bounds of the array.

   at _Default.TestMe(Object state) in c:"ApolloWorkFolder"test"laboratory

"TestWebSite"Default.aspx.cs:line 21

   at System.Threading._ThreadPoolWaitCallback.WaitCallback_Context(Object state)
   at System.Threading.ExecutionContext.runTryCode(Object userData)

   at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(

TryCode code, CleanupCode backoutCode, Object userData)

   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext,

ContextCallback callback, Object state)

   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext,

ContextCallback callback, Object state)

   at System.Threading._ThreadPoolWaitCallback.PerformWaitCallbackInternal(

_ThreadPoolWaitCallback tpWaitCallBack)

   at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback(Object state)。

PS:摘自網路