InvokeRequired异常处理 [英] InvokeRequired Exception Handling

查看:116
本文介绍了InvokeRequired异常处理的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我注意到在Windows几个怪异的行为窗体方案涉及线程和UI,因此,很自然,这意味着利用该InvokeRequired属性。情况:我的应用程序使用一个线程做一些工作和线程发送一个事件到用户界面。用户界面显示基于一个国际化系统,包括带有按键的字典上的消息。该国际化系统无法找到在词典和崩溃的关键



注:应用程序在调试模式下,我有一个try-catch在整个Application.Run( );早在Program.cs中。然而,的try-catch没有达到,因为什么,我会在这里讨论是基于内部异常处理,但我只是在情况下提到它。



所以,现在这里来有趣的部分:




  1. 为什么,我用我的生命,还是从我的Visual Studio御史异常信息?在下面的代码,你将看到的如果(InvokeRequired)的分支,一个try-catch。我登录异常。 ex.InnerException是NULL,并且提供ex.StackTrace贫血(仅1步的话)。现在,如果我的注释在try-catch,只是让它通过调试器崩溃,我得到了很多更加宽裕的堆栈跟踪。这是为什么?


  2. 要更糟糕的是,没有两个堆栈跟踪版本包含有关国际化崩溃的任何信息。他们只是说:给定的关键是不存在的字典。并给我一个堆栈跟踪到调用声明。


  3. 其他的分支(即 InvokeRequired = = FALSE 的),如果我把一个try-catch,我可以成功地抓住我的异常给国际化的系统。正如你所看到的,我试着用的InnerException送我的异常给的 InvokeRequired ==真的分支。然而,即便如此,的InnerException保持NULL那里,我无法访问我的i18n错误。




我对所有这些困惑事情也许有人可以帮助一些线索在这里。如果你有真正强大的灯笼是。



下面是函数的代码。

 私人委托无效AddMessageToConsole_DELEGATE(frmMainPresenter.PresenterMessages消息); 
私人无效AddMessageToConsole(frmMainPresenter.PresenterMessages消息)
{
如果(InvokeRequired)
{//捕获调用的函数内部发生的任何错误。
尝试{调用(新AddMessageToConsole_DELEGATE(AddMessageToConsole),消息); }
赶上(例外前){MSASession.ErrorLogger.Log(除息); }
//调用(新AddMessageToConsole_DELEGATE(AddMessageToConsole),消息);
}
,否则
{
串MESSAGE_TEXT =; //将显示在控制台消息/写在日志中。

{
MESSAGE_TEXT = I18N.GetTranslatedText(消息)
}
赶上(异常前)
{
抛出新的异常(如.Message,前);
}

txtConsole.AppendText(MESSAGE_TEXT);
}
}


解决方案

的调用栈的问题是一个已知问题 Control.Invoke 。你失去调用堆栈。抱歉。这是因为它是使用罚球前在UI线程上重新抛出;



最好的解决办法是更换后台线程与背景工作。注意:此方法仅适用于.NET 4.0。在工作类正确老帅异常。我写有关从任务报告进度博客条目,并且在博客条目中的代码将让你捕捉任何UI更新错误在后台线程,保留原来的异常和调用堆栈。



如果你不能升级到.NET 4.0然而,有一个解决方法。 微软的接收库包括CoreEx.dll它有一个扩展方法例外名为 PrepareForRethrow 。这是支持.NET 3.5 SP1和.NET 4.0(和SL 3和SL 4)。你需要的东西来包装你的UI更新方法有点丑陋的:

 私人委托无效AddMessageToConsole_DELEGATE(frmMainPresenter.PresenterMessages消息) ; 
私人无效AddMessageToConsole(frmMainPresenter.PresenterMessages消息)
{
如果(InvokeRequired)
{
//调用目标方法,捕获该异常。
异常前= NULL;
调用((MethodInvoker)()=>
{

{
AddMessageToConsole(消息);
}
赶上(例外错误)
{
前=错误;
}
});

//如果被抛出
如果处理错误
{
MSASession.ErrorLogger.Log(前)(前!= NULL);

//重新抛出,保留异常堆栈
掷ex.PrepareForRethrow();
}
}
,否则
{
串MESSAGE_TEXT =; //将显示在控制台消息/写在日志中。

{
MESSAGE_TEXT = I18N.GetTranslatedText(消息)
}
赶上(异常前)
{
抛出新的异常(如.Message,前);
}

txtConsole.AppendText(MESSAGE_TEXT);
}
}

请注意:我建议你开始迁移远离 ISynchronizeInvoke 。这是延续到新的UI框架(例如,WPF,Silverlight的)一个过时的接口。替换为的SynchronizationContext ,它支持的WinForms,WPF,Silverlight中,ASP.NET等的SynchronizationContext 是更适合作为一个业务层一个抽象的线程上下文。


I noticed a few strange behaviors in a Windows Forms scenario which involves threads and UI, so, naturally, this means making use of the InvokeRequired property. The situation: my application makes use of a thread to do some work and the thread sends an event into the UI. The UI displays a message based on an Internationalization system which consists of a dictionary with keys. The I18N system cannot find a key in the dictionary and crashes.

Notes: application is in Debug Mode and I have a try-catch over the entire "Application.Run();" back in Program.cs. However, that try-catch is not reached, as what I will discuss here is based on inner Exception handling, but I mentioned it just in case.

So now here comes the fun parts:

  1. Why, for the life of me, does Visual Studio "censor" exception information from me? In the code below, you will see on the if (InvokeRequired) branch, a try-catch. I log the exception. ex.InnerException is NULL and the provided ex.StackTrace is anemic (only 1 step in it). Now if I comment the try-catch and simply let it crash via the Debugger, I get a much ampler stack trace. Why is that?

  2. To make things worse, neither of the two stack traces versions contain any information about the i18N crash. They just say "The given key was not present in the dictionary." and give me a stack trace up to the Invoke declaration.

  3. On the else branch (that is, InvokeRequired == false), if I put a try-catch, I can successfully catch my Exception back to the i18n system. As you can see, I tried to send my exception with InnerException back to the InvokeRequired == true branch. However, even so, InnerException stays NULL there and I cannot access my i18N error.

I am puzzled by all these things and maybe somebody can help shed some light over here. If you got really strong lanterns that is.

Here is the function's code.

private delegate void AddMessageToConsole_DELEGATE (frmMainPresenter.PresenterMessages message);
private void AddMessageToConsole (frmMainPresenter.PresenterMessages message)
{
  if (InvokeRequired)
  { //Catching any errors that occur inside the invoked function.
    try { Invoke(new AddMessageToConsole_DELEGATE(AddMessageToConsole), message); }
    catch (Exception ex) { MSASession.ErrorLogger.Log(ex); }
    //Invoke(new AddMessageToConsole_DELEGATE(AddMessageToConsole), message);
  }
  else
  {
    string message_text = ""; //Message that will be displayed in the Console / written in the Log.
    try
    {
      message_text = I18N.GetTranslatedText(message)
    }
    catch (Exception ex)
    {
      throw new Exception(ex.Message, ex);
    }

    txtConsole.AppendText(message_text);
  }
}

解决方案

The call stack problem is a known issue with Control.Invoke. You lose the call stack. Sorry. This is because it is rethrown on the UI thread using throw ex;.

The best solution would be to replace the background thread with a background Task. Note: this solution is only available for .NET 4.0. The Task class properly marshals exceptions. I wrote a blog entry about reporting progress from tasks, and the code in that blog entry will allow you to catch any UI update errors in the background thread, preserving the original exception and its call stack.

If you can't upgrade to .NET 4.0 yet, there is a workaround. Microsoft's Rx library includes a CoreEx.dll which has an extension method for Exception called PrepareForRethrow. This is supported in .NET 3.5 SP1 and .NET 4.0 (and SL 3 and SL 4). You'll need to wrap your UI updater method with something a little uglier:

private delegate void AddMessageToConsole_DELEGATE (frmMainPresenter.PresenterMessages message); 
private void AddMessageToConsole (frmMainPresenter.PresenterMessages message) 
{ 
  if (InvokeRequired) 
  {
    // Invoke the target method, capturing the exception.
    Exception ex = null;
    Invoke((MethodInvoker)() =>
    {
       try
       {
         AddMessageToConsole(message);
       }
       catch (Exception error)
       {
         ex = error;
       }
    });

    // Handle error if it was thrown
    if (ex != null)
    {
      MSASession.ErrorLogger.Log(ex);

      // Rethrow, preserving exception stack
      throw ex.PrepareForRethrow();
    }
  } 
  else 
  { 
    string message_text = ""; //Message that will be displayed in the Console / written in the Log. 
    try 
    { 
      message_text = I18N.GetTranslatedText(message) 
    } 
    catch (Exception ex) 
    { 
      throw new Exception(ex.Message, ex); 
    } 

    txtConsole.AppendText(message_text); 
  } 
} 

Note: I recommend you start a migration away from ISynchronizeInvoke. It is an outdated interface that is not carried forward into newer UI frameworks (e.g., WPF, Silverlight). The replacement is SynchronizationContext, which supports WinForms, WPF, Silverlight, ASP.NET, etc. SynchronizationContext is much more suitable as an abstract "thread context" for a business layer.

这篇关于InvokeRequired异常处理的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆