【问题标题】:Under what circumctances will catch all (catch (Exception ex)) not really catch all?什么情况下会catch all (catch (Exception ex)) 不是真的catch all?
【发布时间】:2011-12-28 09:23:59
【问题描述】:

我的崩溃报告系统告诉我一个异常到达了 Unhandled Exception 处理程序,并且查看产生此异常的代码,我看不出这是如何发生的。代码是完全同步的,并且包含在 try/抓住。

澄清:此代码在 Windows Phone 7 上运行,异常发生在生产环境中不受控制的环境(即在用户的设备上)。

代码如下:

private void LoadUserData()   
{   
    try  
    {   
        UserData = UserData.Deserialize(UserDataFileName, true);   

        // We succeeded. Backup the file, if possible.   
        if (IsoFile.Store.FileExists(UserDataFileName))   
        {   
             try  
             {   
                 IsoFile.CopyFile(UserDataFileName, UserDataFileBackupName);   
             }   
             catch (Exception ex)   
             {   
                 EventManager.Current.ShipAssert("LoadUserData.Backup", ex);   
             }   
         }   
     }   
     catch (Exception ex)   
     {   
         string currentFile = "";   
         try  
         {   
             currentFile = IsoFile.ReadAllLines(UserDataFileName);   
         }   
         catch (Exception exInner)   
         {   
             currentFile = "Exception when trying to read file: " + exInner.ToString();   
         }   
    }   
}  

以及到达未处理异常处理程序的异常:

System.InvalidOperationException: There is an error in XML document (129, 54). ---> System.InvalidOperationException: There is an error in XML document (129, 54). ---> System.Xml.XmlException: Unexpected end of file has occurred. The following elements are not closed: value, item, VotedUrls, UserData. Line 129, position 54.    at    
System.Xml.XmlTextReaderImpl.Throw(Exception e)    at    
System.Xml.XmlTextReaderImpl.Throw(Int32 res, String resString, String arg)    at    
System.Xml.XmlTextReaderImpl.Throw(Int32 pos, Int32 res, String resString, String arg)    at    
System.Xml.XmlTextReaderImpl.ThrowUnclosedElements()    at    
System.Xml.XmlTextReaderImpl.ParseElementContent()    at    
System.Xml.XmlTextReaderImpl.Read()    at    
System.Xml.XmlTextReader.Read()    at    
System.Xml.XmlReader.ReadEndElement()    at    
System.Xml.Serialization.XmlSerializationReader.DeserializePrimitiveElement(LogicalType deserializeAs, Accessor accessor, Fixup fixup, Object fixupTarget, String identifier, Boolean emptyValue, Boolean nullValue)    at    
System.Xml.Serialization.XmlSerializationReader.deserializeElement(Accessor accessor, Fixup fixup, Object fixupTarget)    at    
System.Xml.Serialization.XmlSerializationReader.DeserializeElement(Accessor accessor, Fixup fixup)    at    
System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle)    at    
SocialEbola.Shared.Utils.SerializableDictionary`2.ReadXml(XmlReader reader)    at    
System.Xml.Serialization.XmlSerializationReader.DeserializeSerializableElement(LogicalType deserializeAs, Accessor accessor, Fixup fixup, Object fixupTarget, String identifier, Boolean emptyValue, Boolean nullValue)    at    
System.Xml.Serialization.XmlSerializationReader.deserializeElement(Accessor accessor, Fixup fixup, Object fixupTarget)    at    
System.Xml.Serialization.XmlSerializationReader.DeserializeElementMember(MemberValueCollection members, Object fixupTarget, Hashtable internalState, Boolean& firstElement)    at    
System.Xml.Serialization.XmlSerializationReader.deserializeMembers(MemberValueCollection members, Object fixupTarget)    at    
System.Xml.Serialization.XmlSerializationReader.DeserializeComplexElement(LogicalType deserializeAs, Accessor accessor, Fixup fixup, Object fixupTarget, String identifier, Boolean emptyValue, Boolean nullValue)    at    
System.Xml.Serialization.XmlSerializationReader.deserializeElement(Accessor accessor, Fixup fixup, Object fixupTarget)    at    
System.Xml.Serialization.XmlSerializationReader.DeserializeElement(Accessor accessor, Fixup fixup)    at    
System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle)    at    
System.Xml.Serialization.XmlSerializer.Deserialize(Stream stream)    at    
SocialEbola.Lib.Serialization.SerializeHelper`1.Deserialize(Stream stream)    at    
SocialEbola.Lib.Serialization.SerializeHelper`1.Deserialize(String file, Boolean createNew)    at    
Fails.App.LoadUserData()    at    
Fails.App.Application_Launching(Object sender, LaunchingEventArgs e)    at    
Microsoft.Phone.Shell.PhoneApplicationService.FireLaunching()    at    
Microsoft.Phone.Execution.NativeEmInterop.FireOnLaunching()     at    
System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle)    at    
SocialEbola.Shared.Utils.SerializableDictionary`2.ReadXml(XmlReader reader)    at    
System.Xml.Serialization.XmlSerializationReader.DeserializeSerializableElement(LogicalType deserializeAs, Accessor accessor, Fixup fixup, Object fixupTarget, String identifier, Boolean emptyValue, Boolean nullValue)    at    
System.Xml.Serialization.XmlSerializationReader.deserializeElement(Accessor accessor, Fixup fixup, Object fixupTarget)    at    
System.Xml.Serialization.XmlSerializationReader.DeserializeElementMember(MemberValueCollection members, Object fixupTarget, Hashtable internalState, Boolean& firstElement)    at    
System.Xml.Serialization.XmlSerializationReader.deserializeMembers(MemberValueCollection members, Object fixupTarget)    at    
System.Xml.Serialization.XmlSerializationReader.D   

这是 UserData.Deserialize 的代码(来自模板基类):

public static T Deserialize(string file, bool createNew)
{
    IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication();
    T result;

    if (!store.FileExists(file))
    {
        if (createNew)
        {
            result = new T();
        }
        else
        {
            result = default(T);
        }
    }
    else
    {
        using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(file, FileMode.Open, store))
        {
            result = Deserialize(stream);
        }
    }

    return result;
}

public static T Deserialize(Stream stream)
{
    T t = (T)Serializer.Deserialize(stream);
    return t;
}

而Serializer在基类中定义为:

public static XmlSerializer Serializer = new XmlSerializer(typeof(T));

【问题讨论】:

  • 社交埃博拉病毒+1。我喜欢社交病毒。
  • 你能告诉我们UserData.Deserialize方法吗?它可能会做一些引发异常的异步工作。
  • @Shahar Prish 您确定堆栈跟踪源自您粘贴的代码吗?你是在 Visual Studio 下运行还是独立运行?
  • @Erno:在这种情况下,异常不会发生在异步线程上而不是该线程上吗? (我可以粘贴代码,但没有什么异步的。让我知道你的想法。
  • @Wal:这是我拥有的唯一具有该签名的方法。这在用户设备(Windows Phone)上作为独立的生产环境运行,并通过我拥有的崩溃报告机制向我报告[我将添加说明]

标签: c# .net exception windows-phone-7


【解决方案1】:

在 .Net 2.0 中,StackOverflowException 是无法捕获的。这很容易被您的代码绊倒。唯一无法捕获的异常是 .Net 4.0 中的 AccessViolationException。只有在您尝试读取受保护的内存时才会发生这种情况。

我会说 StackOverflowException 是您最有可能的嫌疑人。检查您的代码中是否存在未封顶的递归函数,并尝试单步调试以查看导致它的条件。

【讨论】:

  • 鉴于堆栈显示 InvalidOperationException,您如何得出这个结论?
  • @Brian Nickel:我可能遗漏了一些东西,但就像 wal 所说的那样,该过程并没有快速失败 - 它实际上遇到了运行未处理异常处理程序的麻烦。谢谢。
  • @Brian,异常处理在 StackOverflowException 上终止是绝对正确的,这是因为异常处理遍历调用堆栈,寻找与异常类型匹配的 catch 块和必须获取的 finally 块执行。如果堆栈处于错误状态,则无法进行该处理。但是,堆栈溢出异常将是由于深度递归或调用堆栈上的值类型的数量或大小非常大。从显示的代码中,我没有看到任何暗示这些问题的东西。我预计问题出在其他地方。
【解决方案2】:

更新:我首先要尝试的是清理您的项目并重建。您可能没有运行您认为正在运行的代码。 (即该堆栈没有行号,可能不是您最近编译的程序集)

如果还不能解决问题,请继续阅读

在您的 UnhandledException 方法处理程序上放置一个断点,并确定调用线程是否与引发异常的线程相同:

private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
    //add this line and put a breakpoint here.
    var threadId = System.Threading.Thread.CurrentThread.ManagedThreadId;
    Log.Error("CurrentDomain_UnhandledException", e.ExceptionObject as Exception);
}

如果两个 threadId 不同,则可以解释您看到的行为。 (正如@Erno 所说)

如果它们相同,那么作为测试,我会在您调用 Deserialize 之前在您的代码中抛出一个显式异常以查看是否被捕获。例如:

throw new Exception("test");
UserData = UserData.Deserialize(UserDataFileName, true); 

然后报告回来。

【讨论】:

  • 谢谢。正如我在上面澄清的那样,这个异常只发生在用户设备的生产中。我不能让它在我的本地模拟器/机器上重现,所以我不能尝试所有这些。调用堆栈是从这些设备报告的(因此调试器不是如何影响结果的)。感谢您的回复。
  • @Shahar 您的代码看起来像它应该捕获该异常,因此我会改变方向并询问您正在查看的代码是否与生产中的版本相同?如果不确定,请使用反射器反编译您的生产 dll,以确保代码具有与源代码一样的 try catch 块。
  • 我非常确定,但这不是一个坏主意。我去看看。
【解决方案3】:

我建议学习和应用一些异常处理最佳实践。

不要将异常用于流控制(就像您正在做的那样),使用它们来报告和处理错误情况。

Catch 块应部分或完全从错误条件中恢复,并可能重新抛出异常或抛出新异常(可能更具体的异常)。不要将业务逻辑放在 catch 块中(就像你正在做的那样)。这样做可以使一个异常掩盖另一个异常。

仅捕获您将要(至少部分)恢复的异常,或者为了抛出更具体的异常。由于异常是按类型捕获的,这意味着您实际上不应该捕获 System.Exception:它太笼统了,您无法从中恢复。

同理,不要抛出过于笼统的异常,比如System.Exception。研究 .Net Framework 类库中已定义的异常,或创建直接或间接从 System.Exception 派生的特定异常类型。

由于给定 try 块的 catch 块是按顺序计算的,因此请确保将更多派生异常类型的 catch 块放在派生较少的异常类型的 catch 块之前。

任何好的 C# 编程参考都应该提供正确使用的指导以及引发和捕获异常的最佳实践。

考虑到这一点: - 第 14 行捕获了哪些异常类型?明确表示。 - 第 16 行的代码是否有可能抛出异常?如果是这样,会发生什么异常处理? - 为什么在第 22 行声明的变量的范围是 catch 块?在第 29 行分配给它的价值是什么,因为它在那之后就超出了范围?

在你的代码中整理出这些东西,然后用调试器逐步检查它,我相信你马上就发现了罪魁祸首。

【讨论】:

  • 感谢您的 101。:) 有几个问题可以澄清您的答案,以便我更好地回答...您所说的“不要使用异常进行流量控制”是什么意思?我在这里做什么是流量控制?没有办法检查有效的 XML 文件的有效性——唯一的方法是调用 parse 方法——如果失败,则意味着 XML 发生了错误并且无法解析。你会建议我如何处理那个特殊情况?
  • 这是个好问题。在 LoadUserData 最外层的 catch 块内,您有逻辑,只有在最外层的 try 块中抛出异常时才会执行该逻辑。这就是流程控制——指导在什么情况下执行哪些语句。相反,使用 catch-blocks 专门用于从异常中恢复、记录它们、将一种异常类型转换为另一种异常类型。 C# 中的流控制语句包括 if-then、foreach 等。不要使用异常处理来尽您所能使用流控制结构。
  • 明白了。因此,您正在谈论的问题是,在 catch 中将“errorOccurred”局部变量设置为 true 之后,catch() 包含可能已移出 catch 的逻辑。我说得对吗?如果是这样,我明白你在说什么 - 好点。但是,我遇到的问题在此之前 - catch(Exception ex) 没有捕获所有异常,或者看起来如此,这意味着这里有一个更基本的问题,通过重新设计代码无法解决更多正确的异常处理。
  • 嵌套异常处理(try-和catch-blocks)的问题之一是一个异常很容易被另一个异常屏蔽。你相信你所看到的行为很可能是由这种现象解释的。
  • 好吧,我现在明白你的意思了,尽管我从来没有见过你描述的行为,其中一个被捕获的异常“逃脱”了 catch 块而不是另一个 - 但话又说回来,我的大部分经验是在 .NET 中进行服务器开发,而不是 Silverlight。我将重构代码并发布另一个版本,看看它是否能解决问题 - 仍然 - 似乎比其他任何东西都更像一个 .NET 错误(除非你能看到一个具体的例子,你所描述的事情可能在现实世界中发生)
猜你喜欢
  • 2013-08-12
  • 1970-01-01
  • 2012-09-22
  • 2013-06-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-12-27
  • 2010-11-29
相关资源
最近更新 更多