0

アセンブリをオンデマンドでGACにインストールし(ボタンをクリックすると)、アセンブリを試行するアプリケーションで作業しているときに、どういうわけか問題が発生しました。次のボタンをクリックしてロードするか、からメソッドを呼び出そうとします。その新しくGACされたアセンブリですが、失敗します。問題と再現の手順は以下のとおりです。

問題と再現の手順: 次の名前のFooGreet.dllのような新しいクラスライブラリを作成し、厳密な名前を付けます。後でアセンブリフォルダにドラッグしましょう。

namespace FooGreet
{
    public class Greeting
    {
        public string SayHello()
        {
            return "Hello";
        }
    }
}

次のコードのように、上記のクラスライブラリをロードするコンソールアプリケーションを作成します。

namespace FooGreetCaller
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Press Enter to load assembly..");
            while (true)
            {
                string input = Console.ReadLine();
                if (input == "q" || input == "Q")
                    break;
                try
                {
                    System.Reflection.Assembly.Load("FooGreet, Version=1.0.0.0, Culture=Neutral, PublicKeyToken=3f330bcf59df56c9");
                    Console.WriteLine("Assembly load success.");
                }
                catch (Exception eX)
                {
                    Console.WriteLine(eX.Message + Environment.NewLine + eX.StackTrace);
                }
            }
        }
    }
}
  1. コンソールアプリケーションを実行します。コンソールアプリケーションのインスタンスを実行します。FooGreet.dllをロードしようとすると、何度ロードしても「ファイルまたはアセンブリをロードできませんでした…。」という例外が発生します。コンソールウィンドウを閉じないでください。
  2. FooGreet.dllをGACにインストールします。FooGreet.dllをGACにドラッグします。コンソールアプリケーションの同じインスタンスで、FooGreet.dllを再度ロードしようとすると、同じ例外が発生します。FooGreet.dllがGACにあり、コンソールアプリケーションがそれを認識しているはずなので、これを異常と呼びます。持ってはいけませんか?
  3. コンソールアプリケーションの2番目のインスタンスを実行します。コンソールアプリケーションの2番目のインスタンスを実行します。FooGreet.dllをロードしてみてください。これで、アセンブリが正常にロードされます。
  4. GACからFooGreet.dllをアンインストールします。GACに移動し、FooGreet.dllをアンインストールします。コンソールアプリケーションの2番目のインスタンスでFooGreet.dllを再度ロードしてみてください。アセンブリが正常にロードされました。理想的には、「ファイルまたはアセンブリをロードできませんでした…」のような例外をスローするべきではありませんか?

質問: 質問はステップ2と4にあります。ステップ2とステップ4での出来事から、アプリケーションが読み込まれると、GACのスナップショットが作成されるように見えます(少なくともデフォルトのAppDomainでは、作成を試みていません)新しいAppDomainをロードします)。これは本当ですか?

(私のアプリでは、回避策として、特定のアセンブリをGACに登録した後にアプリケーションを再起動します。

System.EnterpriseServices.Internal.Publish().GacInstall(targetAssemblyName);

私はWinFormsアプリを使用していて、Form_Loadで再起動を行っています。これにより、メインフォームがほんの一瞬点滅します。これは気に入らないです)

4

2 に答える 2

1

Assembly Binderは、実際にはキャッシュを使用します。コードで要求されたアセンブリはすべて、AssemblyBinderがそのプロセスに従ってアセンブリをロードします。アセンブリバインディングの結果は、アセンブリバインダーによってキャッシュされます。同じアセンブリが再度要求されると、Assembly Binderはキャッシュを調べ、アセンブリが正しくロードされた場合は、ロードされたアセンブリへの参照が返され、アセンブリバインドが失敗した場合は、例外が返されます。

バインディングコンテキストはキャッシングでも使用されることに注意してください。Assembly.Load、、Assembly.LoadFromおよびAssembly.Load(byte[])、これらの3つのメソッドは、3つの異なるバインディングコンテキストを使用し、キャッシュはロードタイププロセスごとに個別に保存されます。ただし、推奨される方法はですAssembly.Load

これらすべてを確認するために、マシンでフュージョンを有効にして、アセンブリバインディングログビューアでバインディング結果を確認できます。

于 2012-10-18T07:14:35.620 に答える
1

「スナップショット」という言葉は正確ではありませんが、そのように見えます。アセンブリローダーは、すべてのAssembly.Load / From()要求を単に記憶します。失敗したものを含みます。そして、それが非常に一貫して同じ結果を返すことを確認してください。失敗したものを含みます。これは強力な反DLL地獄対策です。

使用シナリオは非常に奇妙なので、回避策を考え出すのは困難です。「以前にロードしたことがある」というステータスを強制的にリセットする方法はありません。ただし、.NET 1.xはこれを行わなかったため、1.xの動作を復元するために使用できるapp.exe.config設定があります。<disableCachingBindingFailures>要素を使用します。ただし、これにはグローバルな影響があることに注意してください。

この1つのアセンブリだけにローカライズするには、AppDomainを作成し、アセンブリをそのアセンブリにロードして、再度アンロードしてみてください。次に、プライマリドメインへのロードが失敗するかどうかがわかります。

于 2012-10-18T09:15:15.313 に答える