web-dev-qa-db-ja.com

ApplicationSettingsBaseのFileNotFoundException

アプリケーションをデバッグするときに、Visual Studioで例外の中断が有効になっていると、常に次のエラーが発生します。例外でbreakを使用しているので、これは本当に私を悩ませています。面白いのは、続行しても機能することです(StringCollectionがロードされます)。

メッセージは次のとおりです。

ファイルまたはアセンブリ 'System.XmlSerializers、Version = 4.0.0.0、Culture = neutral、PublicKeyToken = b77a5c561934e089'またはその依存関係の1つを読み込めませんでした。システムは、指定されたファイルを見つけることができません。

例外の原因となっているコードは次のとおりです(デザイナーが生成)

[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;
        }
    }

エラーを表示する空のテストアプリケーションを作成しようとしましたが、例外は発生しませんでした。私たちのプロジェクトは巨大なので、原因を見つけるのは難しいです。たぶん、このサイトの誰かがこれを解決する方法の手がかりを持っています。

29
testalino

この例外がスローされる理由の説明です。このサンプルWindowsフォームアプリを使用して、例外を再現できます。 StringCollectionタイプの「Setting」という名前の設定を追加することから始めます。 [値]列のドットをクリックして、いくつかの文字列を入力します。フォームクラスコードを次のようにします。

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);
    }
}

デバッグ+例外、CLR例外の[スロー]チェックボックスをオンにします。フォームを実行して閉じると、例外がスローされたときにデバッガーが停止します。コールスタックの最上位は次のようになります。

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  

StringCollectionクラスのXMLシリアライザーを含むアセンブリのXmlSerializerクラスハンティングを確認できます。 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)
    {
      ...
    }
  ....
}

そしてCompiler.GetTempAssemblyName():

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

この場合、このGetTempAssemblyNameは悪意のある人物です。 StringCollectionクラスはSystem.dllアセンブリにあり、メソッドは「System.XmlSerializers」という名前を生成します。このメソッドは、Sgen.exeによって生成された独自のクラスのアセンブリを見つけるように設計されています。サンプルプログラムのWindowsApplication1.XmlSerializers.dllのように。ただし、StringCollectionは.NET Frameworkのクラスであり、生成されるアセンブリ名は無効です。フレームワークには、実際には「System.XmlSerializers.dll」アセンブリはありません。

Connect.Microsoft.comでのこの動作に関するフィードバックレポートはすべて、「設計による」で閉じられています。元の設計者は、例外を防ぐためのコストが高すぎると考え、例外をキャッチすることにしました。すべて正常に機能しますが、例外は実際にキャッチされます。 [デバッグ+例外]ダイアログで[スロー]チェックボックスがオンになっているため、たまたま表示されます。

ここでは、Xmlシリアル化コードの動作を変えることはできません。 System.dllアセンブリの型を単純に除外するのは簡単だったでしょうが、それは終わりのない戦いになる可能性があり、フレームワークにはさらに多くのアセンブリがあります。回避策は、StringCollectionを使用する代わりに、独自のクラスを使用して設定を格納することです。

55
Hans Passant

これは実際には通常の操作の一部であるように思われるため(以下も参照: コンストラクターでFileNotFoundExceptionを与えるXmlSerializer )、2つの回避策しか提供できません。

この特定の例外を無効にします。[デバッグ/例外]に移動し、[追加]、[タイプ]、[C++例外]、[名前]:[EEFileLoadException](これが表示されている例外の場合)をクリックし、この例外の[スロー]チェックボックスをオフにします。

設定のタイプを文字列に変更してアクセスします。例:そのようです:

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

キャッチしている例外が多すぎると、System.XmlSerializerは常にこの例外を通常の操作の一部としてスローし、クラス自体によってキャッチおよび処理されます。デバッグオプションを変更して、.netファームワーククラス内でキャッチおよび処理される例外ではなく、例外のみをキャッチします。

1
Ben Robinson