【问题标题】:Visual Studio 2010 doesn’t stop at an unhandled exception inside a Socket.BeginReceive() callback - why?Visual Studio 2010 不会在 Socket.BeginReceive() 回调中出现未处理的异常 - 为什么?
【发布时间】:2012-01-02 02:13:23
【问题描述】:

通常,当附加调试器时,Visual Studio 2010 会在未处理的异常处停止,即使“异常”对话框的“抛出”列中没有异常类型的标记。这里的关键字是unhandled;表示对话框只指处理过的异常。

但是,在以下最小示例中,Visual Studio 2010 并没有停止对我的异常,即使它作为第一次机会异常出现在即时窗口中:

编辑:我发布的第一个最小示例已由我收到的第一个答案修复,但不幸的是,以下示例仍然存在问题:

using System;
using System.Net.Sockets;

namespace SocketTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var listener = new TcpListener(8080);
            listener.Start();
            AsyncCallback accepter = null;
            accepter = ar =>
            {
                var socket = listener.EndAcceptSocket(ar);
                var buffer = new byte[65536];
                AsyncCallback receiver = null;
                receiver = ar2 =>
                {
                    var bytesRead = socket.EndReceive(ar2);
                    throw new InvalidOperationException();
                    socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, receiver, null);
                };
                socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, receiver, null);
                listener.BeginAcceptSocket(accepter, null);
            };
            listener.BeginAcceptSocket(accepter, null);

            Console.WriteLine("Feel free to connect to port 8080 now.");
            Console.ReadLine();
        }
    }
}

如果你运行它,通过运行telnet localhost 8080 连接到它,然后在 telnet 中输入任何字符,希望你会看到我所看到的:程序只是静默中止。

为什么 Visual Studio 显然会吞下这个异常?我可以像往常一样让它在异常时中断吗?

(有趣的是,在BeginAcceptSocket 回调中抛出确实会被捕获,以Thread.Start 开头的普通线程中的异常也会被捕获。我只能通过在BeginReceive 回调中抛出来重现该问题。)

【问题讨论】:

  • 你附加到新线程了吗?
  • @Hogan:那是什么意思?我知道附加到一个进程,但是一个线程?
  • 这仍然是个问题...我正在考虑提供赏金。有人热衷吗?
  • 刚刚尝试了您的设置,但它仍在为我闯入调试器...
  • 你能用选项->调试下的设置更新你的帖子吗?此外,您在 Debug->Exceptions 中检查了哪些(如果有)异常?

标签: c# .net visual-studio-2010 exception asyncsocket


【解决方案1】:

由于您在异常设置中只看到一个“Thrown”复选框,我怀疑您在调试设置中启用了“.Net Framework Source Stepping”。如果启用“仅我的代码”(禁用“.Net Framework Source Stepping”),您应该在异常设置对话框中获得“用户未处理”和“抛出”复选框,并且您还将在调试器中捕获异常。

胜利的截图:

【讨论】:

  • 太棒了,这就是解决方案。谢谢你。尽管它确实让我想知道:Framework source-stepping 与此有什么关系?为什么它不能在启用时中断用户异常?
  • @Timwi - 我没有任何支持信息,但我推测这与调试器无法理解用户代码和非用户代码之间的区别有关。对于任何导致调试器团队的问题,“安全”的选择是完全禁用捕获用户未处理的异常。
  • 恐怕我不得不再次收回我的标记,因为它仍然不起作用。我将编辑问题以包含一个更大的测试用例,在该测试用例中我仍然会得到一个静默终止并且没有调试器中断。
  • @Timwi - 我可以让你的例子通过和失败。
  • 那么肯定还有另外一个相关的选项,我们设置的不同。或者这是一个竞争条件,取决于硬件。或者别的什么。
【解决方案2】:

这是我目前发现的:

  1. 如果在“调试”->“异常”对话框中没有明确检查异常,并且“选项”->“调试”->“仅启用我的代码”(EJMC)被选中,则抛出异常在任何回调中不会与 First Chance Exception (FCE) 中断

  2. 如果在 Debug->Exceptions 对话框中没有明确检查异常并且 EJMC 检查,则在 TCPListener 的回调中抛出的异常中断FCE,但不会Socket 的回调中与 FCE 中断

    一个。抛出的异常不会Socket 的回调中与 FCE 中断,即使调用了 TCPListener 的阻塞 AcceptSocket()(因此没有侦听器的回调)。

  3. 如果System.InvalidOperationException(或System在Debug->Exceptions中被选中,那么在任何回调中抛出的适当异常中断使用 FCE,无论是否检查 EJMC。

  4. 无论回调是指定为 lambda 还是在显式用户函数中,上述情况都是正确的

如果在 Debug->Exceptions 中没有明确检查异常,我不知道为什么 VS 不会在套接字回调中使用 FCE 中断(是什么使套接字回调与侦听器回调不同)。

【讨论】:

  • 我能想到的一种(有点笨拙)的解决方法是将回调的主体包装在一个 try/catch 块中,该块将重新抛出异常并在重新放置一个断点扔。无论异常设置如何,断点似乎都有效
【解决方案3】:

这是 CLR 版本 4 中的一个已知错误。反馈文章 is here。一种可能的解决方法是将框架目标更改为 3.5 版。我将仅引用反馈响应的相关部分:

我们已调查此问题并确定 CLR 中存在错误 v4.0 导致了这种情况。该过程确实会引发异常(您可以 例如,使用 catch 处理程序捕获它)但调试器没有 正确通知未处理的异常。这导致进程 似乎在调试器没有任何指示的情况下退出 发生了。我们已经调查了可能的修复,但是所有的 修复程序有破坏其他功能的中等风险。因为它 在产品周期中很晚,我们决定不解决这个问题更安全 问题和风险产生新的错误。我们将继续跟踪此问题 作为我们下一个发布周期的一部分。

问题仅限于转义托管代码的异常,其中 该线程是使用一些特定的 API 调用创建的:
新的 System.Threading.Timer()
ThreadPool.UnsafeQueueNativeOverloapped
ThreadPool.BindHandle
ThreadPool.RegisterWaitForSingleObject。

在这种特定情况下是 RegisterWaitForSingleObject()。

【讨论】:

  • 有趣。甚至在发布此之前,我想出了将回调包装在 new Thread(() => { ... }).Start(); 中的解决方法,但对我而言,这仅修复了问题中的示例代码,而不是我实际遇到问题的“真实”代码。事实证明我错误地应用了修复程序;应该看看调用堆栈 :) 谢谢!您想将此解决方法添加到您的答案中吗?这样会更好。
  • 很遗憾你不会更新你的答案。我想我必须自己做。
猜你喜欢
  • 2011-05-31
  • 1970-01-01
  • 2012-10-16
  • 2018-04-07
  • 2012-02-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-22
相关资源
最近更新 更多