在这个主题上花了好几个小时(也访问了几十页)我被迫寻求帮助。我已经看过很多关于这个主题的帖子,但我无法解决我得到的问题。
基本上,我想做一件非常简单的事情:从我的文件夹加载程序集到应用程序。
这是简短的问题(所有其他细节在其他文本中解释) 我想使用Assembly.LoadFrom方法加载我的程序集(相同的程序集文件用项目引用,但使用CopyLocal:false),但是虽然在调用使用它的方法时加载程序集,程序会尝试从默认位置加载程序集。如果找到,则加载2个相同的程序集,程序使用最后一个程序集,如果未找到,则引发FileNotFoundException。 但是,当我在Autocad插件中使用相同的想法时,一切都有效,并且不会引发异常,尽管找不到文件。
我有2个简单项目的测试解决方案:TestExe(控制台应用程序)和TestDll(dll库)。 我想在TestExe中使用TestDll中的类型,所以我添加了对TestDll的引用(我没有引用TestDll项目,但指定位置上的文件),但我想在运行时手动加载TestDll。为什么需要这个是在文本末尾解释的,使用Autocad示例。
据我所知,Assembly.LoadFrom方法可用于此目的。所以基本的想法是:在TestExe中的方法之前加载TestDll正在使用它,所以当调用方法时我们已经加载了程序集。这样,无论引用的dll是否存在于默认目录中,我们已经加载了程序集并将使用它。
来自MSDN:
如果已加载具有相同标识的程序集,则即使指定了不同的路径,LoadFrom也会返回已加载的程序集。
所以我明白,如果我加载一次dll,同一个程序集的每次下一次加载(也来自其他位置)都会知道它已经添加,所以首先使用它。
项目:TestExe
//File: Program.cs
using System;
namespace TestExe
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(new ClassExe().ExeMethod());
}
}
}
//File: ClassExe.cs
namespace TestExe
{
class ClassExe
{
public string ExeMethod()
{
return new TestDll.ClassDll().DllMethod();
}
}
}
Project TestDll
using System.Reflection;
namespace TestDll
{
public class ClassDll
{
public string DllMethod()
{
return Assembly.GetExecutingAssembly().Location;
}
}
}
如您所见,任务很简单:显示被调用程序集的位置。
让我们说TestDll.dll被复制到应用程序文件夹Extern \ TestDll.dll。
如果我在TestExe项目中设置属性CopyLocal:false对TestDll的引用,则程序将失败并出现FileNotFoundException。 (1)是否因为仅在默认目录(application dir和TestDll \ TestDll.dll)中搜索程序集?
然后我尝试在使用之前手动加载程序集:
static void Main(string[] args)
{
Assembly.LoadFrom(@"Extern\TestDll.dll");
Console.WriteLine(new ClassExe().ExeMethod());
}
但是我得到了相同的FileNotFoundException,尽管已经加载了TestDll
当我设置属性CopyLocal:true时,程序可以运行。但是,它从默认位置再次加载TestDll.dll,并忽略已加载的程序集。
我管理程序工作方式的唯一方法(使用程序集Extern \ TestDll.dll)是使用AppDomain.AssemblyResolve事件。
我有Autocad插件,它使用10种不同的dll,来自不同的解决方案。所以我有plugin.dll,它引用了Documents文件夹中的10个dll文件,并设置了CopyLocal:true。但是,当部署到客户时,我只在应用程序文件夹中保留plugin.dll,并且所有其他dll都在Libraries子目录中。在插件类的静态构造函数中,我将Assembly.LoadFrom从Libraries子目录中加载,一切正常。
很抱歉很长的帖子,但我想详细解释一下。
感谢任何反馈:)
答案 0 :(得分:1)
您可以尝试检查程序集的加载上下文。 Here是一个示例,here是一篇文章。
基础是AutoCad将其程序集和插件程序集加载到运行acad.exe进程的同一AppDomain中,因此它们对于所有其他已加载的程序集是可见的,因为它们是插件dll的引用。但是当你在acad进程中加载一个dll时,对于.exe是不可见的,反之亦然 - 它们都在不同的app domains中运行而且看不到对方。
答案 1 :(得分:1)
对于对此主题感兴趣的所有人,我找到了答案。
我将再次回顾一下:想法是从项目中引用dll并在代码中使用它,但稍后在部署应用程序以手动加载相同的dll时。因此加载引用的dll将失败,但在此之前,dll将手动加载,因此一切都应该工作。情况并非如此,因为虽然手动加载了dll,但仍然会搜索引用的那个,这会产生问题。
在关于CLR Binder的this篇文章中,本段解释了我的问题:
现在,让我们一起讨论装配的位置 如果通过LoadFrom()加载的程序集与程序集相同 通过Load()加载。即使两个组件中的类型相同, 如果两个程序集是从不同的路径加载的,则它们不是 就加载器上下文而言,它们被认为是相同的。这个 导致重复加载相同组件的情况 相同的应用程序域,但进入不同的上下文(负载和 LoadFrom)和Load上下文中的程序集中的类型不会 允许在LoadFrom上下文中使用相同的类型(即使它们是 关于装配身份的相同装配。)
感谢大家的时间和帮助。