首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >InvokeRequired异常处理

InvokeRequired异常处理
EN

Stack Overflow用户
提问于 2010-08-20 10:27:34
回答 3查看 2K关注 0票数 0

在Windows场景中,我注意到了一些涉及线程和UI的奇怪行为,因此,这自然意味着要使用InvokeRequired属性。情况:我的应用程序使用线程来做一些工作,并且线程将一个事件发送到UI中。UI显示基于国际化系统的消息,该系统由带有键的字典组成。I18N系统无法在字典中找到密钥并崩溃。

注意:应用程序处于调试模式,我对整个"Application.Run();“返回到Program.cs进行了尝试捕获。但是,没有达到试图捕获,因为我将在这里讨论的是基于内部异常处理,但我提到它只是以防万一。

现在,有趣的部分来了:

  1. 为什么在我的一生中,Visual“审查”来自我的异常信息?在下面的代码中,您将看到if (InvokeRequired)分支,这是一个尝试捕获。我记录了异常。ex.InnerException为NULL,所提供的ex.StackTrace为贫血(仅为其中的一个步骤)。现在,如果我注释尝试捕获,并简单地让它通过调试器崩溃,我会得到一个更大的堆栈跟踪。为什么会这样呢?
  2. 更糟糕的是,两个堆栈跟踪版本的都没有包含有关i18N崩溃的任何信息。他们只是说:“字典里没有给定的钥匙。”并给我一个关于调用声明的堆栈跟踪。
  3. 在其他分支(即InvokeRequired == false)上,如果我放置了一个尝试捕获,就可以成功地将异常捕获回i18n系统。如您所见,我尝试将InnerException异常发送回InvokeRequired == true分支。但是,即使如此,InnerException仍然是空的,我无法访问我的i18N错误。

我对这些事情感到困惑,也许有人能帮我弄清楚这里的情况。如果你有很强的灯笼,那就是。

这是函数的代码。

代码语言:javascript
复制
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);
  }
}
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2010-08-20 13:33:51

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

最好的解决方案是用后台Task替换后台线程。注意:此解决方案仅适用于.NET 4.0。Task类正确地封送异常。我编写了关于从任务中报告进度的博客条目,该博客条目中的代码将允许您捕获后台线程中的任何UI更新错误,从而保留原始异常及其调用堆栈。

如果您还不能升级到.NET 4.0,有一个解决办法。微软的Rx库包括一个CoreEx.dll,它为Exception提供了一个扩展方法,称为PrepareForRethrow。这在.NET 3.5 SP1和.NET 4.0 (以及SL 3和SL 4)中得到了支持。您需要用一些丑元素包装UI更新器方法:

代码语言:javascript
复制
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); 
  } 
} 

注意:我建议您开始远离ISynchronizeInvoke的迁移。它是一个过时的接口,不会被移植到新的UI框架中(例如,WPF、Silverlight)。取而代之的是SynchronizationContext,它支持WinForms、WPF、Silverlight、ASP.NET等。SynchronizationContext更适合作为业务层的抽象“线程上下文”。

票数 1
EN

Stack Overflow用户

发布于 2010-08-20 13:19:26

是的,这是Control.Invoke()的内置行为。它只将最深的嵌套InnerException封送回调用方。不太清楚他们为什么要这么做,除了避免报告由封送代码引发的异常之外,这会使读者感到困惑。它是显式的,您不能改变它的工作方式。

但是要注意球,真正的问题是在字典里确实找不到字符串。其原因是后台线程运行的区域性与UI线程不同。不同的区域性有不同的字符串比较规则。您需要为字典提供不同的比较器(StringComparer.InvariantCulture),或者将后台线程切换到与UI线程相同的区域性。

在UI线程中处理非系统默认区域性可能很困难,所有其他线程都将在系统默认情况下启动。特别是线程池线程是麻烦的,您并不总是控制它们的启动方式。文化不是Thread.ExecutionContext的一部分,所以不会被转发。这可能会引起一些微妙的问题,就像你遇到的那个。其他的缺点是,比方说,SortedList,当被使用不同文化的线程读取时,它会突然变得无序。强烈建议使用系统默认区域性。这是您的用户可能会使用的无论如何。

票数 2
EN

Stack Overflow用户

发布于 2010-08-20 10:53:42

对Windows.Forms对象的调用将导致在单独的线程上调用函数。如果在被调用的函数中抛出异常,则会捕获异常并引发新的TargetInvocationException。

这个TargetInvocationException在它的InnerException属性中包含初始的超值。

所以,试着这样做:

代码语言:javascript
复制
catch (TargetInvocationException ex) { MSASession.ErrorLogger.Log(ex.InnerException); }

编辑:此外,如果您在调试器中展开InnerException属性,您将能够访问它的堆栈跟踪,即使只是作为纯文本。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/3530011

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档