【问题标题】:Dynamically loading a dll in C#在 C# 中动态加载 dll
【发布时间】:2009-11-17 18:40:11
【问题描述】:

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

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

1) 从 dll 上获取元数据的某种方式,这样我就可以构建可能的显示选择列表...

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

任何建议将不胜感激。

【问题讨论】:

  • dll是如何编译的——是clr还是native?
  • CLR,现在它只是一个简单的类,有几个字符串,最终都会有IEditor接口,或者什么......

标签: c# dll


【解决方案1】:

如果您使用的是原始 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 */);
}

否则,您将使用汇编方法。查看程序集级别属性或对象级别属性是获取额外信息的好方法,但如果您想要的是插件系统,您应该使用一个插件系统,例如@987654321 @在 CodePlex。另见this SO question and answer

【讨论】:

    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      我不知道是否可以选择更改程序的工作方式,但是,您可以为此使用依赖注入,只要它们遵循某个接口。

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

      我不是在处理卸载,我只是在考虑如何获得类,并且由于 plinth 已经给出了实际处理 dll 的函数的链接,我想我就到此结束。

      【讨论】:

        【解决方案4】:

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

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

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

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

        【讨论】:

          【解决方案5】:

          好的,我发现我需要使用第二个 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 进行 foreach 以获取 AssemblyName,然后稍后 我用

          DLLDomain.Load(SelectedAssemblyName)
          

          加载 DLL。我不断收到 FileNotFound 异常。 经过多次谷歌搜索后,我决定目前要做的工作很多,如果我真的需要,我可以稍后重构它......

          感谢您的回复!

          【讨论】:

            【解决方案6】:

            了解如何使用 MEF 轻松完成此操作,只需使用指向插件目录的 DirectoryCatalog,只要您有匹配的 [Export] 和 [Import],它就可以很好地工作。

            【讨论】:

              猜你喜欢
              • 2019-05-31
              • 1970-01-01
              • 2011-09-06
              • 1970-01-01
              • 2010-09-30
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多