5

背景:

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

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

意图:

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

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

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

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

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

以下是问题:

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

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

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

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

我可能不了解基本原理,并且可能会混淆术语。我希望能深入了解 MEF、DLL 和程序集的一般加载行为。谢谢 !

4

1 回答 1

5

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

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

如果是这样,有没有办法打开每个 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;
    }

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

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

真的。

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

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

我可能不了解基本原理,并且可能会混淆术语。我希望能深入了解 MEF、DLL 和程序集的一般加载行为。谢谢 !

我上面提到的书籍详细包括如何解析/加载程序集等等。还可以查看Suzanne Cook 的博客

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

最后我建议看看微软的智能客户端软件工厂。它几乎可以完成您提到的所有事情以及更多。理解它并感到舒适需要一些努力,但从长远来看,它可能会为您节省大量时间。

于 2013-02-02T15:12:10.730 回答