什么情况下 try catch 不是 try catch?

5

我有一个有趣的问题,就是在应用程序关闭期间,栈中似乎忽略了try/catch块。

由于时间紧迫,我还没有一个可行的测试项目(否则我肯定会试图重现这个问题),但请考虑以下代码片段。

class IndexNotFoundException : Exception { }

public static string RunAndPossiblyThrow(int index, bool doThrow)
{
    try
    {
        return Run(index);
    }
    catch(IndexNotFoundException e)
    {
         if(doThrow)
             throw;
    }
    return "";
}

public static string Run(int index)
{
    if(_store.Contains(index))
        return _store[index];
    throw new IndexNotFoundException ();
}

public static string RunAndIgnoreThrow(int index)
{
    try
    {
        return Run(index);
    }
    catch(IndexNotFoundException e)
    {
    }
    return "";
}

在运行时,这个模式非常有效。我们可以支持依赖异常来控制程序的遗留代码(不好),并且可以向前移动并逐步删除用于程序控制的异常。
然而,在关闭UI时,我们会看到从“Run”抛出异常,即使当前对“RunAndPossiblyThrow”的所有使用中,“doThrow”都为false。我甚至已经修改了代码,看起来像“RunAndIgnoreThrow”,但是在UI关闭后仍然会崩溃。
Eric Lippert先生,我每天都读你的博客,我真希望听到它是某个已知的bug,而我没有疯掉。
编辑: 这是多线程的,并且我已经验证了所有对象在被访问时都没有被修改。
编辑: 明确显示异常是我们自己的。
编辑: 忘了提到,这是在关闭时,不幸的是,Visual Studio无法直接捕获崩溃。它可能在除UI线程以外的线程上崩溃,一旦主线程关闭,就会关闭。我只能通过反复运行和关闭应用程序,打开任务管理器,“创建转储文件”并查看Windbg中产生的超过400MB混乱结果来调试。 Win7 64作为参考。请确保您理解了这一点。
编辑: 关闭时的以下代码仍然显示相同的异常。
class IndexNotFoundException : Exception { }

public static string RunAndPossiblyThrow(int index, bool doThrow)
{
    try
    {
        return Run(index);
    }
    catch
    {
    }
    return "";
}

public static string Run(int index)
{
    if(_store.Contains(index))
        return _store[index];
    throw new IndexNotFoundException ();
}

似乎唯一能消除异常的方法是直接转到


class IndexNotFoundException : Exception { }

public static string RunAndPossiblyThrow(int index, bool doThrow)
{
    try
    {
        return Run(index);
    }
    catch
    {
    }
    return "";
}

public static string Run(int index)
{
    if(_store.Contains(index))
        return _store[index];
    return "";
}

虽然异常已经消失,但我仍然担心自己会变得疯狂。

编辑

情况变得更糟了... 这仍然会导致崩溃...

class IndexNotFoundException : Exception { }

public static string RunAndPossiblyThrow(int index, bool doThrow)
{
    try
    {
        throw new IndexNotFoundException();
    }
    catch
    {
    }
    return "";
}

编辑 我有一种强烈的感觉这不会帮助我解决问题。除了奇怪的行为外,我还可以注意到在上述情况下执行UI时,try catch被忠实地执行。我的UI不会崩溃,并且它充满了空字符串。然而,一旦我开始关闭UI,崩溃就会显现出来,try catch不再阻止异常。

编辑和最终版 显然,转储文件列出了最近的一次first-chance异常。我通过创建一个在try catch中抛出并睡眠10秒的新项目进行了验证。在等待期间,我得到了.dmp文件,确实发现我的完全捕获的异常正在显示。

我会给有用的答案打分,但不幸的是,我的代码仍然没有任何原因或逻辑崩溃...


1
你能发布异常信息吗?堆栈跟踪,异常类型,消息等? - Jake
这是一个Windows崩溃对话框。事件日志中出现了APPCRASH事件,列出了故障应用程序名称和“RPCRT4.dll”故障模块名称。我可以向您保证我们不直接使用此dll,我认为它来自C#的深处。 - Dearmash
2
发布堆栈跟踪非常重要 - Hans Passant
你是在什么上下文中调用这个 RunAndPossiblyThrow 的?应用程序有可能会 FailFast,这会跳过所有的 catch 和 finally 块,并强制终止应用程序。你可能正在一个假定在关闭期间抛出它被认为是足够糟糕以执行这个 FailFast 的上下文中调用它。 - Dan Bryant
我有一个调试工具,在创建转储之前会冻结所有线程。通常我可以找出故障发生的位置和源头。 - lsalamon
显示剩余3条评论
5个回答

4

添加额外的异常捕获机制。我认为您得到的是除ApplicationException之外的其他异常,这就是导致应用程序崩溃的原因。


我之前说过,我希望其他异常仍然是异常并导致崩溃。这是我们正在寻找和缺失的异常。 - Dearmash
嗯,还有其他的异常情况 - 它们正在导致你的应用程序崩溃。 - Otávio Décio
在生成的 .dmp 文件中,唯一包含的异常是我的异常。我不确定我还能更清楚地表达了。 - Dearmash
当您使用catch{ }(也就是没有异常类型的catch)时,您能使其发生吗? - Jake
为了不遗漏任何可能,我尝试了Jake,但异常仍然被抛出。如果我太着急了,对不起Otavio。 - Dearmash
这并没有解决问题,但它确实带来了一些有用的信息。谢谢。 - Dearmash

3

这是一个很好的资源。不幸的是,我相对确定每一个这些异常仍然可以在调试器中被捕获。不幸的是,无论我使用多少调试器都无法捕获导致崩溃的问题。那看起来是一个不错的博客,有很多内容,尽管更新频率不到每个月1次。我会将其加入列表中。谢谢。 - Dearmash

0

你尝试过添加“finally”子句吗?只是为了看看它是否不会完全忽略try/catch?理论上,无论如何,它应该总是跳转到该行之前,然后退出try/catch。

如果它仍然忽略它,那么肯定有一些奇怪的事情发生。


0

可能你从Run中捕获并再次抛出了其他异常。可能_store为空或者出现了其他问题。


0
一些可能有助于诊断问题的事项:
- 注册AppDomain.CurrentDomain.UnhandledException,这样您就可以看到是否有其他线程崩溃而不是UI线程。 - 注册Application.ThreadException以捕获在UI线程中没有被捕获的任何异常。 - 由于这发生在关闭期间,请问您是否使用了任何可能会在终结器线程上引发异常的Finalizers?

两个处理程序都已注册,但都没有被触发。否则,这只是解析日志的简单问题。在我添加这些处理程序到代码后,生活变得更加容易。据我所知,我们的代码中没有明确声明任何终结器。我们非常依赖IDispose来在完成任务后移除对象。 - Dearmash
你可能需要检查一下那些在应用程序关闭之后仍然存在的线程,尽管我会感到惊讶如果 AppDomain 在所有线程终止之前释放其未处理异常处理程序。 - Dan Bryant

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接