如何读取DLL插件中的MEF元数据而不将整个DLL复制到内存中?

use*_*175 5 c# dll plugins mef metadata

背景:

我有兴趣使用MEF在使用C#和.NET 4.0的WinForm应用程序中提供插件架构,但我不清楚一些事情.

第一:我还没有在C#中构建DLL的工作,我对DLL Assemblies的概念以及DLL如何正常加载到内存中有点模糊(意思是,根据需要一次性或分段)

意图:

该程序将是一个机器硬件控制框架,将由一个主要的WinForm GUI组成,这是一个具有基本工具栏,菜单等的通用环境 - 但没有批量GUI内容.(想想:MDI家长,但实际上并非如此).

插件提供特定机器的所有控件.许多可能的插件中的每一个都包含30到50个大型UserControl,每个用户控件都包含许多WinForm控件和大量支持代码,构成了各种机器控制面板.

这意味着主程序是一个轻量级的通用框架,插件包含将在主程序用户界面中显示的大量GUI控件和功能,包括许多图标,图像和其他资源.这将使插件DLL可能非常大.

目标是允许用户从菜单中选择一个插件,然后在选择时加载并运行插件,然后插件将大部分空的主GUI填充面板,菜单和工具箱.

为此,我需要首先从每个插件中提取元数据以填充程序的初始菜单,其中包括插件标题,描述,图标,版本号和其他信息位.

以下是问题:

使用MEF,如果我尝试从存储在插件文件夹中的每个大型DLL中读取元数据,是否会在访问元数据值之前将整个DLL复制到内存中?

如果是这样,有没有办法打开每个DLL文件,只读取元数据到内存中构建初始菜单 - 然后通过MEF加载完整选定的DLL?

我假设通过MEF读取插件的典型DirectoryCatalog和AggregateCatalog模板将所有发现的DLL复制到内存中并将它们存储在目录集合中.

DLL是否包含一个连续的代码块(程序集),或者它们是否包含多个单独的块,这些块根据需要单独索引并复制到内存(多个程序集)?

我可能不了解基本面,也许是令人困惑的条款.我将非常感谢对MEF,DLL和程序集的加载行为的任何了解.谢谢 !

Pan*_*nis 5

使用MEF,如果我尝试从存储在插件文件夹中的每个大型DLL中读取元数据,是否会在访问元数据值之前将整个DLL复制到内存中?

据我所知,整个DLL将被加载到内存中.这与MEF没有任何关系.DirectoryCatalogAssemblyCatalog使用对Assembly.Load的调用加载程序集(通过).此方法不是MEF的一部分,但它是.NET Framework的核心方法.大多数加载的程序集都是以这种方式加载的.如果使用Process Explorer监视运行应用程序的进程,则可以看到虚拟大小将增加加载的程序集的大小.因此,如果您加载大型程序集,您的进程的虚拟大小将很高.

如果是这样,有没有办法打开每个DLL文件,只读取元数据到内存中构建初始菜单 - 然后通过MEF加载完整选定的DLL?

有办法做到这一点.

一种方法是创建一个新的应用程序域,CompositionContainer在新的AppDomain中创建并检查发现的部分.然后将有关这些部分的信息序列化到主AppDomain.最后卸载新的AppDomain.然后,您可以检查您真正需要的部件,并仅加载包含它们的部件.有关如何执行此操作的示例可以在此答案中找到.

另一种方法是使用Mono.Cecil.这是一个很棒的库,可以帮助您检查程序集而不加载它们.您可以使用以下方法将其与MEF的导出元数据结合使用:

public static bool IncludesTypeWithSpecificExportMetadata<T>(string assemblyPath, string name, T value)
    {
        AssemblyDefinition assemblyDefinition = AssemblyDefinition.ReadAssembly(assemblyPath);

        bool typeWasFound = false;          

        foreach (TypeDefinition typeDefinition in assemblyDefinition.MainModule.GetTypes())
        {
            foreach (CustomAttribute customAttribute in typeDefinition.CustomAttributes)
            {
                if (customAttribute.AttributeType.FullName == typeof(ExportMetadataAttribute).FullName)
                {
                    string actualName = (string)customAttribute.ConstructorArguments[0].Value;
                    T actualValue = (T)((CustomAttributeArgument)customAttribute.ConstructorArguments[1].Value).Value;
                    if (actualName.Equals(name) && actualValue.Equals(value))                        
                    {
                        typeWasFound = true;                       
                    }
                }
            }
        }

        return typeWasFound;
    }
Run Code Online (Sandbox Code Playgroud)

给定一个汇编文件路径和名称/值对这种方法会使用Mono.Cecil能检查装配和寻找装饰类型与ExportMetadataAttribute,并使用相同的名称/值对.

我假设通过MEF读取插件的典型DirectoryCatalog和AggregateCatalog模板将所有发现的DLL复制到内存中并将它们存储在目录集合中.

真正.

DLL是否包含一个连续的代码块(程序集),或者它们是否包含多个单独的块,这些块根据需要单独索引并复制到内存(多个程序集)?

我不知道这个.您可以在Don Box的"Essential .NET Volume 1"或Jeffrey Richter的"C#via CLR"中找到答案.

我可能不了解基本面,也许是令人困惑的条款.我将非常感谢对MEF,DLL和程序集的加载行为的任何了解.谢谢 !

我上面提到的书籍详细说明了如何解决/加载装配等等.还可以看看Suzanne Cook的博客.

现在我想问你一件事.您真的需要将大文件嵌入到程序集中吗?如果你能找到另一种方式,那么你将不需要任何这种方式.你的插件引擎会有点简单.

最后我建议看看微软的智能客户端软件工厂.它可以完成您提到的所有内容以及更多内容.需要花费一些力气来理解它并对它感到舒服,但从长远来看它可能会节省你的时间.