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

时间:2013-01-31 05:24:09

标签: c# plugins dll metadata mef

背景

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

首先:我还没有在C#中构建DLL的工作,我对DLL程序集的概念以及DLL如何正常加载到内存中有点模糊(意思是,所有一次或部分加载需要)

意图:

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

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

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

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

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

以下是问题:

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

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

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

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

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

1 个答案:

答案 0 :(得分:5)

  

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

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

  

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

有办法做到这一点。

一种方法是创建新的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和相同名称/值对修饰的类型。

  

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

真。

  

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

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

  

我可能不了解基本面,也许令人困惑   条款。我很感激任何有关MEF负载行为的见解,   一般的DLL和程序集。谢谢!

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

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

最后我建议看看微软的Smart Client Software Factory。它可以完成您提到的所有内容以及更多内容。理解它并花些时间感觉很舒服但是从长远来看它可能会节省你的时间。