0

当我在开发一个按需将程序集安装到 GAC 的应用程序(单击按钮时)并尝试在单击下一个按钮或尝试从那个新的 GAC-ed 程序集,但失败了。问题和重现步骤如下。

问题和重现步骤: 创建一个类似于以下的新类库,命名为 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. 运行控制台应用程序的第二个实例:  运行控制台应用程序的第二个实例。尝试加载 FooGreet.dll,现在程序集加载正常。
  4. 从 GAC 卸载 FooGreet.dll:  转到 GAC,卸载 FooGreet.dll。尝试在控制台应用程序的第二个实例中再次加载 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 实际上使用缓存。任何在代码中请求的程序集,Assembly Binder 都会按照其流程加载程序集。程序集绑定的结果由程序集绑定器缓存。当再次请求相同的程序集时,程序集绑定器会在缓存中查找,如果程序集正确加载,则返回对已加载程序集的引用,如果程序集绑定失败,则返回异常。

还有一点要注意,Binding Context 也用在缓存中。Assembly.Load,Assembly.LoadFromAssembly.Load(byte[]), 这 3 种方法使用 3 种不同的绑定上下文,并且缓存为每个加载类型进程单独存储。然而,推荐的方法是Assembly.Load.

要验证所有这些,您可以在您的机器上启用融合并在Assembly Binding Log Viewer中查看绑定结果。

于 2012-10-18T07:14:35.620 回答
1

“快照”这个词并不准确,尽管看起来是这样。程序集加载器只记住所有的 Assembly.Load/From() 请求。包括失败者。并确保它始终如一地返回相同的结果。包括失败者。它是一种强大的反DLL Hell 反措施。

使用场景非常奇怪,很难想出解决方法。没有办法强制重置“我已经看到它之前加载过”状态。但是 .NET 1.x 没有这样做,因此有一个 app.exe.config 设置可用于恢复 1.x 行为。使用<disableCachingBindingFailures>元素。但请注意,这会产生全球影响。

要将其本地化为仅此一个程序集,您可以创建一个 AppDomain,尝试将程序集加载到其中并再次卸载它。然后,您将知道加载到您的主域中是否会失败。

于 2012-10-18T09:15:15.313 回答