如何捕捉到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[]& 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& 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]