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

背景:我有兴趣使用MEF在使用C#和.NET 4.0的WinForm应用程序中提供插件架构,但我不清楚一些事情.第一:我还没有在C#中构建DLL的工作,我对DLL Assemblies的概念以及DLL如何正常加载到内存中有点模糊(意思是,根据需要...

背景:

我有兴趣使用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和程序集的加载行为的任何了解.谢谢 !

解决方法:

With MEF, if I try to read the metadata from each of many large DLLs
that are stored in a plug-in folder, will the entire DLL be copied
into memory before the metadata values are accessed ?

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

If so, is there any way to open each DLL file and only read the
metadata into memory to build the initial menu – then later load the
full selected DLL through MEF ?

有办法做到这一点.

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

另一种方法是使用Mono.Cecil.这是一个很棒的库,可以帮助您检查程序集而不加载它们.您可以使用以下方法将其与MEF’s Export Metadata组合:

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;
    }

给定一个程序集文件路径和一个名称/值对,此方法将使用Mono.Cecil检查程序集,并查找使用ExportMetadataAttribute装饰并具有相同名称/值对的类型.

I am assuming that the typical DirectoryCatalog and AggregateCatalog
template for reading plugins through MEF will copy all of the
discovered DLLs into memory and store them in the catalog collection.

真正.

Do DLLs contain one contiguous code block ( assembly ), or can they
contain multiple separate blocks that are indexed and copied to memory
individually as needed ( multiple assemblies ) ?

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

I’m probably not understanding the fundamentals, and maybe confusing
terms. I would appreciate any insight into the load behavior of MEF,
DLLs, and Assemblies in general. Thanks !

我上面提到的书籍详细说明了如何解决/加载装配等等.另请看Suzanne Cook’s blog.

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

最后我建议看看微软的Smart Client Software Factory.它可以做你提到的几乎所有内容.需要花费一些力气来理解它并对它感到舒服,但从长远来看它可能会节省你的时间.

本文标题为:c# – 如何读取DLL插件中的MEF元数据而不将整个DLL复制到内存中?

基础教程推荐