Carl 发表于 2009-12-9 16:29:00

如何捕捉到Delegate中的异常???

Post by "CoreyKou",2006-11-01, 11:51
-----------------------------------------------------
在使用Delegate时发现异常无法捕捉到,在Debug状态下,IDE也无法捉到它,这是为什么?如何才能捕捉到代理函数中的异常呢?

Dim del As New DelegateException(AddressOf Me.ExDelegate)
del.BeginInvoke(AddressOf ExCallBack, Nothing)

Private Sub ExDelegate()
   Throw New Exception("Exception From Delegate!")
End Sub


Reply by "Carl",2006-11-01, 12:07
-----------------------------------------------------

Delegate中的异常是一定可以捕捉到的。

从这个case中的BeginInvoke 来看,似乎是新开启了一个线程。不能跨线程捕捉异常。


Reply by "CoreyKou",2006-11-01, 16:58
-----------------------------------------------------
Zhang Wenqing在来信中说到:

肯定是可以捕捉到的。但是在Debug时,因为IDE只能Attach当前活动的进程(线程),而使用BeginInvoke是异步调用,当主线程激活状态时,异步线程是在等待的,这时候Delegate还没有执行,当然catch不到异常。如果让主线程Sleep(非激活状态),异步线程就会启动,之后就会看到IDE catch到异常了。



----------------------------------------------------------------

>>当主线程激活状态时,异步线程是在等待的,这时候Delegate还没有执行,当然catch不到异常。

Delegate.BeginInvoke方法通过一个后台线程异步地运行代理函数,如果这个后台线程只有在主线程挂起的时候才会执行,异步从何而来呢

你使用的事VS2005,默认情况下,debugger会捕捉到任何User-Unhandled的Common Language Runtime Exception,因此这个时候主线程即使不去Sleep,Exception仍然会被debugger捉到。VS2003的默认设置似乎也应该这样,但是我只有在Exception Dialog(Ctrl+Alt+E)中将Runtime Exception设置为Break into debugger when the exception is thrown,delegate的exception才会被捉到,实在让人迷惑。

附件是一段反映Delegate和Thread在debug状态下对Exception不同响应的code。


Reply by "Carl", 2006-11-02, 9:17
-----------------------------------------------------
线程中的Exception在.NET中有一套很复杂的策略,而且.NET 2.0和.NET1.1的策略是不同的。详细情况可以参考MSDN中关于.NET2.0的策略变更的文章。

关于如何控制线程中未捕获的Exception,请使用System.Windows.Form.Application.SetUnhandledExceptionMode 和 Application.ThreadException。使用方法参见MSDN。


Reply by "St.Valentine",2006-11-02, 11:10
-----------------------------------------------------
补充一下:

还有一个AppDomain.UnhandledException可以用来捕捉CLR级别的未处理异常(顺序上在Application.ThreadException之后,由于在CLR级别,可以用来处理默认域的所有线程上的异常)。不过很有意思的是,即便异常被AppDomain.UnhandledException捉到,恐怖的JIT还是会出现,看来这个事件只能用来写Log了。

//Application.ThreadException仅限于主线程,AppDomain.UnhandledException仅限于默认域,有没有没有这么多限制的啊:)


Reply by "Carl", 2006-11-02, 13:54
-----------------------------------------------------
不对吧,Application.ThreadException可以捕获当前应用程序里的所有线程的Exception,AppDomain.UnhandledException也没有限制啊?

Application.SetUnhandledExceptionMode 可以设置clr处理Exception的方式,可以自动把所有的Exception吃掉。


Reply by "St.Valentine", 2006-11-02, 14:06
-----------------------------------------------------
试一下就知道了(昨晚上我的想法跟你一样), 按下Button1的时候,在主线程运行ErrorMaker1,异常被ExpectionHandle.CustomExpection的前一次重载抓到。而在按Button2的时候,在新线程(通过Delegate在线程池中找一个其他线程也是一样的道理)中运行ErrorMaker1,异常被ExpectionHandle.CustomExpection的前一次重载放过,而被第二次重载抓到。

理论上的说法在MSDN上能找到的(早上刚来的时候找到来着~)。其实,多线程的情况下使用Application.ThreadException一般都需要在非主线程用Try..Catch的

using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace Study_DelegateExpection
{
    static class Program
    {
      /// <summary>
      /// The main entry point for the application.
      /// </summary>
      
      static void Main()
      {
            Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
            Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(ExpectionHandle.CustomExpection);
            AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(ExpectionHandle.CustomExpection);
            
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
      }
    }

----------------------------------------------------

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace Study_DelegateExpection
{
    public partial class Form1 : Form
    {
      public Form1()
      {
            InitializeComponent();
      }

      private void button1_Click(object sender, EventArgs e)
      {
            this.ErrorMaker1();
      }

      public void ErrorMaker1()
      {
                int a = 1;
                int b = 0;
                int c;
                c = a / b;
      }

      private void button2_Click(object sender, EventArgs e)
      {
            Thread th=new Thread(new ThreadStart(ErrorMaker1));
            th.Start();
      }

    }

    public class ExpectionHandle
    {
      public static void CustomExpection(object sender, ThreadExceptionEventArgs e)
      {
            MessageBox.Show(e.Exception.Message, "Custom Expection handled(Thread)");
      }

      public static void CustomExpection(object sender, UnhandledExceptionEventArgs e)
      {
            MessageBox.Show(((Exception)e.ExceptionObject).Message, "Custom Expection handled(CLR)");
      }
    }
}


Reply by "WantSong",2006-11-03, 15:01
-----------------------------------------------------
具体Delegate的机制不太清楚,不过从测试代码来看,的确需要区分是否还是主线程。工作线程中有异常时,需要用代理在传回来“BeginInvoke(new WorkThreadHandlerDelegate(WorkThreadHandler));”


测试代码很土,将就着看吧。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace StudyWinApp {
    public partial class Form1: Form {
      public Form1() {
            InitializeComponent();
      }
      #region
      public delegate void TestDelegate();
      public delegate void WorkThreadHandlerDelegate();
      public event TestDelegate testEvent;
      #endregion

      //using main thread directly
      public void throwException() {            
            throw new Exception();         
      }

      public void processThread() {

            try {
                Thread th = new Thread(new ThreadStart(throwExceptionT));
                th.Start();
            } catch(Exception) {
                MessageBox.Show("aaa");
            }
      }

      public void throwExceptionT() {
            try {
                int a = 0, b = 0, c;
                c = a / b;
            } catch(Exception) {
                BeginInvoke(new WorkThreadHandlerDelegate(WorkThreadHandler));
            }
      }

      public void WorkThreadHandler() {
            label1.Text = "error";
            MessageBox.Show("work thread error!");
      }

      private void button2_Click(object sender, EventArgs e) {
            label1.Text = "start";
            try {
                //testEvent = new TestDelegate(throwException); 主线程
                testEvent = new TestDelegate(processThread); //工作线程
                if(testEvent != null) {
                  callback(testEvent);
                }

            } catch(Exception ee) {
                MessageBox.Show(ee.Message);
            }
      }

      private void callback(TestDelegate tmp) {
            tmp();
      }

    }
}


Reply by " WantSong",2006-11-03, 15:42
-----------------------------------------------------
From Yang Ning:



我试了一下,

大家说得都很对。

Application的ThreadException只能获取主线程的Exception。

BeginInvoke中发生了的Exception,也会调用CallBack函数。


如果要获取BeginInvoke中的Exception,调用EndInvoke就可以。

-----------------------------------------------------------------------------------------------------------------

我把上面的程序改了下 :

public void throwExceptionT() {
         //   try {
                int a = 0, b = 0, c;
                c = a / b;
      //    } catch(Exception) {
               
            //BeginInvoke(new WorkThreadHandlerDelegate(WorkThreadHandler));
      //    }
      }

private void button2_Click(object sender, EventArgs e) {
            label1.Text = "start";
            TestDelegate td = delegate() {
                //MessageBox.Show("error");
                int a = 0, b = 0, c;
                c = a / b;
            };
            try {
                //testEvent = new TestDelegate(throwException);
                testEvent = new TestDelegate(processThread);
                //if(testEvent != null) {
                //    callback(testEvent);
                //}
                //IAsyncResult ir = BeginInvoke(td ); //主线程,异常异步截没问题
                IAsyncResult ir = BeginInvoke(testEvent); //工作线程,异常截有问题
                EndInvoke(ir);
            } catch(Exception ee) {
                MessageBox.Show(ee.Message);
            }
      }

-----------------------------------------------------------------------------------------------------------------

我想,异常在不同线程间传递时还是需要使用代理的。


Reply by "WantSong",2006-11-03, 16:09
-----------------------------------------------------
还需要再查查。如果没有记错的话,异步和多线程是不一样的。异步调整了主线程的消息队列,相当于没有启动新的线程。所以异步的错误是可以捕获的,但是一旦启动了新的线程,则需要通过代理捕获。


Reply by "CoreyKou",2006-11-05, 11:24
-----------------------------------------------------
正如Yang Ning所说,在Endinvoke中可以catch到代理函数中的异常:

Dim del As New DelegateException(AddressOf Me.ExDelegate)
del.BeginInvoke(AddressOf ExCallBack, del)

Private Sub ExDelegate()
   Throw New Exception("Exception From Delegate!")
End Sub

Private Sub ExCallBack(ByVal ar As IAsyncResult)
   Try
         Dim del As DelegateException = DirectCast(ar.AsyncState, DelegateException)
         del.EndInvoke(ar)
   Catch ex As Exception
         Console.WriteLine(Ex.ToString())
   End Try
End Sub

--------------------------------------------------------------------------------
捕获到的异常信息:

System.Exception: Exception From Delegate!

Server stack trace:
   at ThreadException.ThreadException.ExDelegate() in C:\Documents and Settings\CoreyKou\Desktop\ThreadException\ThreadException\Form1.vb:line 103
   at System.Runtime.Remoting.Messaging.StackBuilderSink.PrivateProcessMessage(MethodBase mb, Object[] args, Object server, Int32 methodPtr, Boolean fExecuteInContext, Object[]&amp; outArgs)
   at System.Runtime.Remoting.Messaging.StackBuilderSink.AsyncProcessMessage(IMessage msg, IMessageSink replySink)

Exception rethrown at :
   at System.Runtime.Remoting.Proxies.RealProxy.EndInvokeHelper(Message reqMsg, Boolean bProxyCase)
   at System.Runtime.Remoting.Proxies.RemotingProxy.Invoke(Object NotUsed, MessageData&amp; msgData)
   at ThreadException.DelegateException.EndInvoke(IAsyncResult DelegateAsyncResult)
   at ThreadException.ThreadException.ExCallBack(IAsyncResult ar) in C:\Documents and Settings\CoreyKou\Desktop\ThreadException\ThreadException\Form1.vb:line 125



Reply by "CoreyKou",2006-11-05, 11:38
-----------------------------------------------------
多谢各位的帮助,这个问题终于找到了答案。但是对于Delegate,多线程,异步的学习与讨论仍然继续......
页: [1]
查看完整版本: 如何捕捉到Delegate中的异常???