在运行时加载程序集,Autocad插件示例

时间:2015-07-24 13:31:56

标签: c# dll reflection .net-assembly autocad-plugin

在这个主题上花了好几个小时(也访问了几十页)我被迫寻求帮助。我已经看过很多关于这个主题的帖子,但我无法解决我得到的问题。

基本上,我想做一件非常简单的事情:从我的文件夹加载程序集到应用程序。

这是简短的问题(所有其他细节在其他文本中解释) 我想使用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 enter image description here

当我设置属性CopyLocal:true时,程序可以运行。但是,它从默认位置再次加载TestDll.dll,并忽略已加载的程序集。 enter image description here

我管理程序工作方式的唯一方法(使用程序集Extern \ TestDll.dll)是使用AppDomain.AssemblyResolve事件。

我有Autocad插件,它使用10种不同的dll,来自不同的解决方案。所以我有plugin.dll,它引用了Documents文件夹中的10个dll文件,并设置了CopyLocal:true。但是,当部署到客户时,我只在应用程序文件夹中保留plugin.dll,并且所有其他dll都在Libraries子目录中。在插件类的静态构造函数中,我将Assembly.LoadFrom从Libraries子目录中加载,一切正常。

很抱歉很长的帖子,但我想详细解释一下。

感谢任何反馈:)

2 个答案:

答案 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上下文中使用相同的类型(即使它们是   关于装配身份的相同装配。)

感谢大家的时间和帮助。