ChinaDHF
学而不思则罔,思而不学则殆。
posts - 41,  comments - 220,  trackbacks - 5

引言
    .Net为我们提供了非常强大的异常处理功能,利用它,我们不仅可以非常方便的进行异常的处理,还可以自己定义并抛出异常,实现信息的特殊反馈。
应用场景
    MdiForm程序中,Children Form在Load的过程中是不能Close的,即使我们需要这样做。例如:如果Children Form在Load的过程中需要读取需要的数据,但是,读取数据失败了,继续Show已经没有意义。这时我们可以利用异常机制,在读取数据失败后立即抛出异常,Parent Form在捕捉到异常后将Children Form关闭。代码大致如下:
Parent Form中:

FrmChild frm=new FrmChild();
frm.MdiParent
=this;
try
{
    frm.Show();
}
catch(Exception ex)
{
    frm.Close();
    MessageBox.Show(ex.Message);
}
finally
{
    
//Other
}

Children Form中:

private void FrmChild_Load(object sender, EventArgs e)
{
    
//读取数据
    if(true)//如果读取数据失败
    {
        
throw new ApplicationException("Read Data Exception.");
    }
}

做完这些后,单击VS2003的“运行”按钮。Ok,正如我所想象的那样,Parent Form在Children Form抛出异常后立即将其捕获,并在关闭Children Form后显示了我想要的提示信息。
出现问题
    正当我在为成功应用了自定义异常而高兴时,问题出现了:我在Windows中直接运行刚才生成的应用程序时,.Net向我提示:应用程序中发生了未处理的异常!不会吧,刚才还正常运行了,怎么现在就变成未处理异常了呢?但事实的确如此。
    经过调试,发现.Net在Children Form抛出异常后立即报告了未处理异常的信息,而不是象刚才那样将异常抛到Parent Form中去,即Paren Form中根本没有任何异常发生。
    问题就解决到这儿了,我百思不得其解:应用程序在VS2003环境中运行和直接运行编译后的执行文件到底有什么区别呢?在以上应用场景中如果不能用异常机制对Children Form进行关闭,又应该如何处理呢?
致歉:按照园里的规矩,带提问性质的随笔是不应该发到首页的,我之所以发在这儿,一是问题急需解决,二是我觉得这个问题特别怪,希望发到首页和园里的更多博友进行交流。望dudu见谅。

 

posted on 2006-05-12 15:11 东海风 阅读(1245) 评论(19)  编辑 收藏 网摘

FeedBack:
2006-05-12 17:08 | lichdr      
明显有问题嘛,不知你在IDE环境下怎么会运行正常的。
把try往上移二行应该就OK。
  回复  引用  查看    
2006-05-12 17:20 | 鞠强      
修改为:
Application.ThreadException +=new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
FrmChild frm=new FrmChild();
frm.MdiParent=this;
try
{
frm.Show();
}
catch(Exception ex)
{
frm.Close();
MessageBox.Show(ex.Message);
}
finally
{
//Other
}

然后这个是handle的代码:
private void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
{
MessageBox.Show(e.Exception.Message);
}

你把close form之类的代码放在这里面即可。
  回复  引用  查看    
#3楼 [楼主]
2006-05-12 17:30 | 东海风      
@lichdr
如果在Children Form的构造函数中抛出异常,是应该往上移两行的,但不应只是移两行而已,变量还是应该在try之前声明的。但现在是在Load过程中抛出异常,因此和移两行是无关的。
谢谢解答。

  回复  引用  查看    
2006-05-12 17:41 | Terrylee      
@lichdr
“明显有问题嘛,不知你在IDE环境下怎么会运行正常的。”这个在IDE下是没有问题的。
  回复  引用  查看    
2006-05-12 17:48 | 鞠强      
晕,楼主还没修改好吗?照我写的那个方式,增加一个Application.ThreadException的handler即可(就在我写的代码的第一行上)
  回复  引用  查看    
2006-05-12 18:04 | 鞠强      
这是一篇很好的文章,你提到的问题就在里面。


The behavior represented in this application is surprising the first time you run across it. In particular, you might notice that pressing any of the bottom three buttons seems to have no effect on the application whatsoever, even though they are causing unhandled exceptions to be thrown! In certain cases, exceptions that are unhandled are swallowed whole by the CLR (although this behavior is changing in the .NET Framework 2.0). Here is a summary of the unhandled exception handler default behaviors that are executing in this application.

连接在这里:
http://msdn.microsoft.com/msdnmag/issues/04/06/NET/default.aspx
  回复  引用  查看    
2006-05-12 19:13 | 鞠强      
搞定了没?好久没回答问题了,心情激动ing……
哈哈!!!
  回复  引用  查看    
#8楼 [楼主]
2006-05-12 19:40 | 东海风      
@鞠强
非常感谢你的热情解答!
看到你的回复时正好要下班了,还没来得及测试,呵呵。
你所提供的解决方法是使用未捕获线程异常,但是在捕获到异常后,即在Handler代码中,已超出了frm变量的作用范围,而且参数中也没有对frm的传递,此时应该如何Close呢?还望指点。


  回复  引用  查看    
#9楼 [楼主]
2006-05-12 19:42 | 东海风      
@鞠强
你是在济南吧?我是日照的,园子里好像山东的并不多, 我们算是老乡了,呵呵。有空多向你请教!
  回复  引用  查看    
2006-05-12 20:31 | 维生素C.NET      
@东海风
强哥是在济南的。园子里山东的朋友也有一定数量哦。
  回复  引用  查看    
2006-05-12 20:49 | michael.sanni.yang      
前几天我刚好也有这样的问题,我的处理是在构造里抛一个异常,然后在MainForm里Catch它,这样如果真的有异常了,根本就不给子窗体show的机会
  回复  引用  查看    
2006-05-12 23:10 | chocoboboy [未注册用户]
对底层不熟胡说一句,Load事件好象不是由Show触发的
  回复  引用    
2006-05-13 09:27 | 鞠强      
// 这句声明放在外面
FrmChild frm;

// 你其他的处理代码,放在某个函数里面,不就搞定啦!
private void button1_Click(object sender, EventArgs e){
Application.ThreadException +=new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
// 这里面用外面声明的frm
frm=new FrmChild();
frm.MdiParent=this;
try
{
frm.Show();
}
catch(Exception ex)
{
frm.Close();
MessageBox.Show(ex.Message);
}
finally
{
//Other
}
}

还有一种方法,你定制自己的ThreadException类,代码最终类似这样:
if(null != AppThreadSEH)AppThreadSEH(this, new AppThreadSEHEventArgs(frm,msg));

// 这个EventArgs你自己定义的一个类,inherited from EventArgs
然后你的handler处理这个event:
private void OnAppThreadSEH(object sender, AppThreadSEHEventArgs e)
{
e.BadForm.Close();
MessageBox.Show(e.Message);
}

  回复  引用  查看    
#14楼 [楼主]
2006-05-13 10:28 | 东海风      
@鞠强
鞠老师,您好。非常感谢您再次为我耐心解答。
将Children Form在函数外声明,可以解决这个问题,但是因为子窗体太多,这样做不太合适。
第二种方法,我没太看明白,应该指的是在Children Form内定义AppThreadSEH事件,然后在读取数据失败时引发该事件,对吗?如果是这样的话,Parent Form在执行e.BadForm.Close();时仍然会出错,因为此时仍处在CreateHandle过程中。
也可能是我的理解有误,希望你能不胜其烦,再次指点。

  回复  引用  查看    
#15楼 [楼主]
2006-05-13 10:36 | 东海风      
@鞠强
我对上面第二种方法应该确实理解错了,否则无法解释
if(null != AppThreadSEH)AppThreadSEH(this, new AppThreadSEHEventArgs(frm,msg));
但这句该在哪儿出现呢,我还是没弄明白。
  回复  引用  查看    
2006-05-13 14:59 | 鞠强      
靠,用了一种很拐弯的方法,终于搞定了。
// 这块先handle异常
// 看第二句
public Form1()
{
    InitializeComponent();
            Application.ThreadException 
+=new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
}


//你的这块没有动
private void button1_Click(object sender, System.EventArgs e)
{                        this.IsMdiContainer = true;
    ChildForm frm 
= null;
    
try
    
{
        frm
=new ChildForm();
        frm.MdiParent
=this;
        frm.Show();
    }

    
catch(Exception ex)
    
{
        frm.Close();
        MessageBox.Show(ex.Message);
    }

    
finally
    
{
        
//Other
    }
            
}


// 这块对异常进行了处理
// 找了好多事件,如Enter、ParentChanged等,如果在这些里面Close,那么会有CreateHandle的时候无法Close得异常出来。最终,选择了这个VisibleChanged,这个是可以的。
private void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
{            
    System.Reflection.MethodBase mb 
= e.Exception.TargetSite;
    Type t 
= mb.DeclaringType;            
                        
    
if(t.BaseType == typeof(System.Windows.Forms.Form))
    
{
        Form form 
= AppDomain.CurrentDomain.CreateInstanceAndUnwrap(System.Reflection.Assembly.GetEntryAssembly().FullName,t.FullName) as Form;
        
foreach(Form f in this.MdiChildren)
        
{
            
if(f.Name == form.Name)
            
{    
// 免得窗口闪一下,所以藏起来先。
                f.Location = new Point(-10000,-10000);
                f.VisibleChanged 
+=new EventHandler(f_VisibleChanged);
                MessageBox.Show(e.Exception.Message);
// 触发事件
                f.Visible = true;

                
return;
            }

        }

    }
            
}


// 关闭有问题的窗口
private void f_VisibleChanged(object sender, EventArgs e)
{
    (sender 
as Form).Close();
}


  回复  引用  查看    
2006-05-13 15:01 | 鞠强      
Handle exception的地方,sender是System.Thread.Threading类型,我得不到正在run的form。TargetSite可以得到抛出异常的method的type,然后通过DeclaringType得到form的type。然后通过CreateInstanceAndUnwrap得到form的实例,比较父窗体中每个mdi child,如果name一致,就认为找到了。

  回复  引用  查看    
#18楼 [楼主]
2006-05-13 15:27 | 东海风      
@鞠强
非常感谢鞠老师,终于搞定!
今天拜读了鞠老师的一些文章,非常敬佩。同时我对浪潮GSP非常感兴趣,我们单位是贵公司的代理,但是刚刚开始做。
再次表示感谢!

  回复  引用  查看    
2006-05-14 01:15 | 鞠强      
juqiang1975@msn.com

u can contact me by this msn account
  回复  引用  查看    

标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2006-05-12 16:04 编辑过
"五向定位"职业成长路线公开课(上海、南京、大连)
Google站内搜索


相关链接:
 
计数器:

阿里妈妈再掀疯狂采购风,网站广告位严重告急,急召天下站长


<2006年5月>
30123456
78910111213
14151617181920
21222324252627
28293031123
45678910

与我联系

搜索

 

常用链接

留言簿(5)

随笔档案(39)

收藏夹(12)

技术网站

阅读排行榜