在C#中动态加载dll

我有一个用于编辑的窗口。 编辑器应该加载一个dll(我完全控制它)以响应用户的选择,知道如何直观地显示信息。 (它们是dll,因为用户不一定想要或不需要每一个显示模型,并且还允许添加新的模型而不会弄乱主项目)

它们都将简单地存储在一个子目录中(现在无论如何)我很确定我可以枚举可用的dll但是我还需要做两件我不确定的事情。

1)从dll获取元数据的一些方法,所以我可以构建可能的显示选择列表…

2)加载选定的dll,并根据需要卸载它

任何建议将不胜感激。

如果您使用的是原始dll而不是.NET程序集,那么这里有一些方便的P / Invokes:

[DllImport("kernel32.dll", CharSet=CharSet.Auto)] private static extern IntPtr LoadLibrary(string lpFileName); [DllImport("kernel32.dll", CharSet=CharSet.Auto)] private static extern void SetDllDirectory(string lpPathName); [DllImport("kernel32.dll", CharSet=CharSet.Auto)] privatestatic extern int GetModuleFileName(IntPtr module, [Out] StringBuilder fileName, int size); [DllImport("kernel32.dll", CharSet=CharSet.Auto)] private static bool FreeLibrary(IntPtr module); [DllImport("kernel32.dll", CharSet=CharSet.Auto)] private IntPtr GetProcAddress(IntPtr hModule, string lpProcName); 

请注意,SetDllDirectory可能需要一些保护,因为它不适用于所有版本的Windows(Windows 2000,特别是没有它)。

并在使用中:

 SetDllDirectory(candidateFolder); IntPtr dllHandle = LoadLibrary(dllName); if (dllHandle != IntPtr.Zero) { _dllHandle = dllHandle; _location = candidateFolder; _fullPath = Path.Combine(candidateFolder, dllName); IntPtr p = GetProcAddress(_dllHandle, procName); if (p == IntPtr.Zero) throw new ArgumentException("procName"); SomeDelegateType d = (SomeDelegateType)Marshal.GetDelegateForFunctionPointer(p, typeof(SomeDelegateType)); d(/* args */); } 

否则,您将使用汇编方法。 查看程序集级别属性或对象级别属性是获取额外信息的好方法,但如果您想要的是插件系统,则应使用插件系统,如CodePlex上的托管外接程序框架 。 另见这个问题和答案 。

看看Castle Windsor框架。 它旨在处理您的所有要求,包括DLL卸载。 它也是免费和开源的。

我不知道改变你的程序是如何工作的,但是,你可以使用dependency injection,只要它们遵循某个接口。

用户选择,动态设置要加载的类,然后只获取该类的实例。

我不是在处理卸载,我只是在想你怎么可能上课,而且由于plinth已经给出了实际处理dll的函数的链接,我想我会在这里结束。

对于本机模块,获取“元数据”的最简单方法是定义一些返回所需信息的C导出(非名称损坏)函数。 最简单的是,这些将返回指向模块内静态数据的指针,例如:

 extern "C" const char* GetModuleDescription(); ... const char* GetModuleDescription() { return "Dummy Module"; } 

然后,您将使用LoadLibrary加载目录中的每个“.dll”文件,使用GetProcAddress从中加载并调用已知的导出。 如果你无法加载文件或找到导出,那么它不是一个有效的插件模块,所以跳过它。

完成模块后,您可以调用FreeLibrary 。 然后,Windows将从您的地址空间卸载该模块。

好吧,我已经想通了我需要使用第二个AppDomain,将dll加载到那个,然后我可以根据需要卸载AppDomain。

 string SignalSystemDLLPath = AppDomain.CurrentDomain.BaseDirectory + MyApp.Properties.Resources.SystemModuleFolder; AppDomainSetup info = new AppDomainSetup(); info.ApplicationBase = DLLPath; DLLDomain = AppDomain.CreateDomain("EditorDomain", null, info); 

DLLPath设置为保存dll的子目录。

然后我在所有dll上预先获取AssemblyName,然后我使用

 DLLDomain.Load(SelectedAssemblyName) 

加载DLL。 我不断收到FileNotFoundexception。 经过大量的谷歌搜索后,我决定它目前工作量很大,我可以稍后重构它如果我真的需要这样做……

谢谢你的回复!

使用MEF找到如何轻松完成此操作,只需使用指向您的插件目录的DirectoryCatalog,并且只要您匹配[导出]和[导入],它就可以很好地工作。