2

我有一个在套接字处理线程中运行的事件处理程序,Invoke用于更新 UI 状态。

我在调用堆栈的某个地方有一个失控FormatException,我试图抓住它来分析它,但我发现我无法让调试器在 UI 线程中中断 - 异常似乎正在冒泡到调用不管我做什么线程。

Private Delegate Sub newDataDelegate(ByVal data As String)
Private Sub onNewData(ByVal data As String) Handles _server.clientHasData
   If Me.InvokeRequired Then
      Me.Invoke(New newDataDelegate(AddressOf onNewData), data)
      Exit Sub
   End If

   Try
      updateGuiWith(data)
   Catch ex As FormatException
      System.Diagnostics.Debugger.Break()
   End Try
End Sub

堆栈跟踪:

at System.Windows.Forms.Control.MarshaledInvoke(Control caller, Delegate method, Object[] args, Boolean synchronous)
at System.Windows.Forms.Control.Invoke(Delegate method, Object[] args)
at <X>.MainForm.onNewData(String data)
   in <X>.vb:line 377
at <X>.Server.onProbeData(String data)
   in <X>:line 104

<X>=已编辑)

结果是调试器分解调用堆栈(在onNewData套接字线程中调用的代码中)并且堆栈跟踪在调用站点结束。我无法找出导致异常的原因。(更糟糕的是,该调用大部分时间都适用于相同的参数,因此如果没有调试器的帮助,我无法预测和追踪它。)

在我进一步提取一个孤立的测试用例之前,这是委托驱动调用背后引发的异常的预期行为吗?

4

3 回答 3

1

这是委托驱动调用背后引发的异常的预期行为吗?

不,那是因为您实际上并没有使用委托的 Begin/Invoke() 方法。您正在使用 Control.Invoke(),这是 Control 类的一种方法,它仅将委托作为参数。它的行为与委托的 Begin/Invoke 方法非常不同,命名选择有点不幸。主要区别在于:

  • 调用 BeginInvoke 时无需调用 EndInvoke
  • Invoke 调用将异常封送回来,BeginInvoke 调用不会
  • 而现在让您感到痛苦的行为,为 Invoke 封送回来的异常是最内在的 InnerException。您只能看到引发事故的异常。如果它被另一个异常捕获并重新引发,并且它在其 InnerException 成员中传递了原始异常,那么您根本看不到这些异常。

是的,当您查看堆栈跟踪时,很难弄清楚您是如何从 A 点到达 B 点的。非常不幸的行为,设计选择并不明显。除了在调用的代码中捕获和处理异常之外,没有简单的解决方法。

或者为了支持 BeginInvoke,通常是您想要使用的方法,因为您不希望您的线程因 UI 更新延迟而陷入困境。带有完整堆栈跟踪诊断的 UI 线程将引发异常。

于 2012-11-26T16:01:34.600 回答
0

从读到这里,

http://charlieflowers.wordpress.com/2005/04/26/controlinvoke-and-exception-propogation-short-form/

我会说是的,这是为了冒泡这个例外。如果我正确理解了这篇文章,异常就会被吞没,异常信息会返回到重新抛出它的 Invoke 函数。但是,如果你使用BeginInvoke异步调用,异常不会通过BeginInvoke调用冒泡。

我没有使用过 VS Express,但通常在 Debug>Exceptions 下,选中“Common Language Runtime Exceptions”旁边的“Thrown”框。然后调试器应该在第一个异常发生的地方停止。

尽管情况可能并非如此,但您可以检查代码中的属性,例如System.Diagnostics.DebuggerStepThrough可能导致调试器跳过引发异常的代码。

于 2012-11-26T15:25:20.740 回答
0

替换Catch ex As FormatExceptionCatch ex As Exception给了我正在寻找的调用线程中断,而实际的异常结果是InvalidCastException. 内部异常是FormatException,这就是它没有被捕获的原因。

我对 .NET 中异常的理解仍然相当初级(首先是 C++ 开发人员),因此这种不同级别的异常包装器的概念是相当陌生的。但是,最终,这种机制正如汉斯在他的回答中所解释的那样。

从长远来看,转向BeginInvoke意志似乎完全避免了这种头痛。不过,现在,我有我的堆栈跟踪,我可以解决实际问题。

这个问题的答案可以在委托驱动的“Control.Invoke”调用之后处理异常吗?

于 2012-11-26T16:37:41.290 回答