简体   繁体   English

ApplicationSettingsBase 中的 FileNotFoundException

[英]FileNotFoundException in ApplicationSettingsBase

When debugging an application I always get the following error when break on exception is enabled in Visual Studio.在调试应用程序时,如果在 Visual Studio 中启用了异常中断,我总是会收到以下错误。 This is really bugging me, since we work with break on exception.这真的让我很烦恼,因为我们在处理异常时使用中断。 The funny thing is, that it still works when I continue (the StringCollection is loaded).有趣的是,当我继续时它仍然有效(加载了 StringCollection)。

The Message is:讯息是:

Could not load file or assembly 'System.XmlSerializers, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' or one of its dependencies.无法加载文件或程序集“System.XmlSerializers, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089”或其依赖项之一。 The system cannot find the file specified.该系统找不到指定的文件。

Here is the code that is causing the exception (designer generated)这是导致异常的代码(设计器生成)

[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public global::System.Collections.Specialized.StringCollection Mru {
        get {
            return ((global::System.Collections.Specialized.StringCollection)(this["Mru"]));
        }
        set {
            this["Mru"] = value;
        }
    }

I tried to create an empty test application that shows the error, but the exception didn't occur.我试图创建一个显示错误的空测试应用程序,但没有发生异常。 Our project is huge so it tough to find the cause.我们的项目很大,所以很难找到原因。 Maybe someone on this site has a clue how to solve this.也许这个网站上的某个人知道如何解决这个问题。

Just an explanation for why this exception is thrown.只是解释为什么抛出这个异常。 You can repro the exception with this sample Windows Forms app.您可以使用此示例 Windows 窗体应用程序重现异常。 Start by adding a setting named "Setting" of type StringCollection.首先添加 StringCollection 类型的名为“Setting”的设置。 Click the dots in the Value column and enter a couple of strings.单击“值”列中的点并输入几个字符串。 Make the form class code look like this:使表单类代码如下所示:

public partial class Form1 : Form {
    public Form1() {
        InitializeComponent();
    }
    protected override void OnFormClosing(FormClosingEventArgs e) {
        Properties.Settings.Default.Setting[0] = DateTime.Now.ToString();
        Properties.Settings.Default.Save();
        base.OnFormClosing(e);
    }
}

Debug + Exceptions, tick the Thrown checkbox for CLR exceptions.调试 + 异常,勾选 CLR 异常的抛出复选框。 Run the form and close it, the debugger will stop when the exception is thrown.运行窗体并关闭它,当抛出异常时调试器将停止。 The top of the call stack looks like this:调用堆栈的顶部如下所示:

mscorlib.dll!System.Reflection.Assembly.nLoad(System.Reflection.AssemblyName fileName, string codeBase, System.Security.Policy.Evidence assemblySecurity, System.Reflection.Assembly locationHint, ref System.Threading.StackCrawlMark stackMark, bool throwOnFileNotFound, bool forIntrospection) + 0x2c bytes 
mscorlib.dll!System.Reflection.Assembly.InternalLoad(System.Reflection.AssemblyName assemblyRef, System.Security.Policy.Evidence assemblySecurity, ref System.Threading.StackCrawlMark stackMark, bool forIntrospection) + 0x80 bytes   
mscorlib.dll!System.Reflection.Assembly.Load(System.Reflection.AssemblyName assemblyRef) + 0x1d bytes   
System.Xml.dll!System.Xml.Serialization.TempAssembly.LoadGeneratedAssembly(System.Type type = {Name = "StringCollection" FullName = "System.Collections.Specialized.StringCollection"}, string defaultNamespace = null, out System.Xml.Serialization.XmlSerializerImplementation contract = null) + 0xcd bytes  
System.Xml.dll!System.Xml.Serialization.XmlSerializer.XmlSerializer(System.Type type = {Name = "StringCollection" FullName = "System.Collections.Specialized.StringCollection"}, string defaultNamespace = null) + 0x105 bytes  

You can see the XmlSerializer class hunting for an assembly that contains the XML serializer for the StringCollection class.您可以看到 XmlSerializer 类正在寻找包含 StringCollection 类的 XML 序列化程序的程序集。 The LoadGeneratedAssembly method looks like this with the boring bits removed: LoadGeneratedAssembly 方法看起来像这样,去掉了无聊的部分:

internal static Assembly LoadGeneratedAssembly(Type type, string defaultNamespace, out XmlSerializerImplementation contract)
{
    ...
    AssemblyName parent = GetName(type.Assembly, true);
    partialName = Compiler.GetTempAssemblyName(parent, defaultNamespace);
    parent.Name = partialName;
    parent.CodeBase = null;
    parent.CultureInfo = CultureInfo.InvariantCulture;
    try
    {
        serializer = Assembly.Load(parent);      // <=== here
    }
    catch (Exception exception)
    {
      ...
    }
  ....
}

And Compiler.GetTempAssemblyName():和 Compiler.GetTempAssemblyName():

internal static string GetTempAssemblyName(AssemblyName parent, string ns)
{
    return (parent.Name + ".XmlSerializers" + (((ns == null) || (ns.Length == 0)) ? "" : ("." + ns.GetHashCode())));
}

This GetTempAssemblyName is the evil-doer in this case.在这种情况下,这个 GetTempAssemblyName 是邪恶的。 The StringCollection class lives in the System.dll assembly, the method generates the name "System.XmlSerializers". StringCollection 类位于 System.dll 程序集中,该方法生成名称“System.XmlSerializers”。 This method is designed to find the assembly for your own classes, the one generated by Sgen.exe.此方法旨在为您自己的类查找程序集,即由 Sgen.exe 生成的程序集。 Like WindowsApplication1.XmlSerializers.dll for your sample program.就像示例程序的 WindowsApplication1.XmlSerializers.dll 一样。 But StringCollection is a class in the .NET Framework, the assembly name it generates just isn't valid.但是 StringCollection 是 .NET Framework 中的一个类,它生成的程序集名称无效。 There isn't actually a "System.XmlSerializers.dll" assembly in the framework.框架中实际上没有“System.XmlSerializers.dll”程序集。

Feedback reports about this behavior at connect.microsoft.com have all been closed with "By Design". connect.microsoft.com 上有关此行为的反馈报告均已通过“按设计”关闭。 It was, the original designers considered the cost of preventing the exception too high and decided to just catch the exception.原来,最初的设计者认为防止异常的成本太高,决定只捕获异常。 Which all works fine, the exception is indeed caught.一切正常,异常确实被捕获。 You just happen to see it because you got the Thrown checkbox turned on in the Debug + Exceptions dialog.您只是碰巧看到它,因为您在“调试 + 异常”对话框中打开了“抛出”复选框。

Making the Xml serialization code behave differently here is not an option.使 Xml 序列化代码在此处表现不同不是一种选择。 It would have been easy enough for them to simply filter out types in the System.dll assembly, but that's a potentially never-ending battle, there are a lot more assemblies in the framework.对他们来说,简单地过滤掉 System.dll 程序集中的类型已经很容易了,但这可能是一场永无止境的战斗,框架中有更多的程序集。 A workaround is to use your own class to store the setting instead of using a StringCollection.解决方法是使用您自己的类来存储设置,而不是使用 StringCollection。

As this really seems to be part of the normal operation (see also: XmlSerializer giving FileNotFoundException at constructor ), I can only offer two workarounds:由于这似乎确实是正常操作的一部分(另请参阅: XmlSerializer 在构造函数中给出 FileNotFoundException ),我只能提供两种解决方法:

Disable this specific exception: goto Debug/Exceptions, click Add, Type: C++ Exceptions, Name: EEFileLoadException (if this is the exception you're seeing), uncheck the Thrown checkbox for this exception.禁用此特定异常:转到调试/异常,单击添加,键入:C++ 异常,名称:EEFileLoadException(如果这是您看到的异常),取消选中此异常的抛出复选框。

Change the type of the setting to string and access it eg like so:将设置的类型更改为字符串并访问它,例如像这样:

var mru = Settings.Default.Mru.Split('|');
Settings.Default.Mru = string.Join("|", mru.ToArray());

You are catching too many exceptions, the System.XmlSerializer will always throw this exception as part of it's normal operation, it is caught and handled by the class itself.您捕获了太多异常,System.XmlSerializer 将始终抛出此异常作为其正常操作的一部分,它由类本身捕获和处理。 Change your debugging options to only catch your exceptions, not exceptions that are caught and handled within the .net farmework classes.更改您的调试选项以仅捕获您的异常,而不是在 .net farmework 类中捕获和处理的异常。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM