如何在DLL插件中读取MEF元数据而不将整个DLL复制到内存? [英] How to read MEF metadata in a DLL plugin without copying the entire DLL into memory?

查看:214
本文介绍了如何在DLL插件中读取MEF元数据而不将整个DLL复制到内存?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

背景



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



第一:我还没有在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和程序集的负载行为有所了解。谢谢!

解决方案


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


据我所知,整个DLL将被加载到内存中。这与MEF没有任何关系。 DirectoryCatalog 将通过调用 AssemblyCatalog )。 microsoft.com/en-us/library/x4cw969y.aspxrel =nofollow noreferrer> 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;
}

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


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


True


执行DLL包含一个连续的代码块(程序集),或者
包含多个单独的块进行索引和复制根据需要单独记录
(多个程序集)?


我不知道这一点。你可能会在Don Box的Essential .NET Volume 1或Jeffrey Richter的C#via CLR中找到答案。


m可能不了解基本面,也许可能会混淆
的条款。我希望对MEF,
DLL和程序集的负载行为有所了解。谢谢!


我上面提到的书包括了如何解决/装载blah blah的程序集。另请参阅 Suzanne Cook的博客



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



最后我建议您查看Microsoft的智能客户端软件工厂。它可以做很多事情你提到和更多。这将需要一些努力来了解它,并感到舒适,但从长远来看,它可能会节省你的时间。


Background:

I'm interested in using MEF to provide a plug-in architecture in a WinForm application using C# with .NET 4.0, but I am not clear on a couple of things.

First: I have not worked at all with building DLLs yet in C#, and I'm a little fuzzy on the concept of DLL Assemblies and how a DLL is normally loaded into memory ( meaning, all at once or in pieces as needed )

Intent:

The program will be a machine hardware control framework, and will be made up of a primary WinForm GUI that is a generic environment with basic toolbars, menus, etc - but no bulk GUI content. ( Think: MDI Parent but not actually ).

The plug-ins provide all of the controls for a specific machine. Each of many possible plug-ins will contain potentially 30 to 50 large UserControls, each populated with many dozens of WinForm Controls and tons of support code that makes up the various machine control panels.

This means that the main program is a lightweight general framework and the plug-ins contain the bulk of the GUI controls and functionality that will be shown in the main program user interface, including lots of icons, images, and other resources. This will make the plug-in DLLs potentially quite large.

The goal is to allow the user to select a plug-in from a menu, and then upon selection, load and run the plug-in which will then fill the mostly empty main GUI with panels, menus, and toolboxes galore.

To do this, I need to first extract metadata from each plug-in to populate the initial menu for the program, which will include the plug-in title, description, icon, version numbers, and other bits of info.

Here are the questions:

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 ?

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 ?

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 ) ?

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 !

解决方案

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 ?

As far as I know the entire DLL will be loaded into memory. This does not have anything to do with MEF though. DirectoryCatalog will load the assembly (through AssemblyCatalog) using a call to Assembly.Load. This method is not part of MEF but it is a core method of the .NET Framework. Most of the loaded assemblies are loaded this way. If you monitor the process running your application using Process Explorer you can see that the Virtual Size will be increased by the size of the loaded assembly. So if you load huge assemblies the Virtual Size of your process will be high.

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 ?

There are ways to do this.

One way is to create a new application domain, create the CompositionContainer in the new AppDomain and check the discovered parts. Then serialize information about these parts to the main AppDomain. Finally unload the new AppDomain. Then you can check which parts you really need and only load the assemblies that contain them. An example on how to do this can be found in this answer.

Another approach is to use Mono.Cecil. This is great library that helps you inspect assemblies without loading them. You can combine it with MEF's Export Metadata in a method like this:

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

Given an assembly file path and a name/value pair this method will inspect the assembly using Mono.Cecil and look for types decorated with the ExportMetadataAttribute and with the same name/value pair.

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.

True.

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 ) ?

I don't know about this. You might find the answer in "Essential .NET Volume 1" by Don Box or "C# via CLR" by Jeffrey Richter.

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 !

The books I mentioned above include in detail how assemblies are resolved/loaded blah blah. Also have a look at Suzanne Cook's blog.

Now I would like to ask you something. Do you really need to embed large files to your assemblies? If you can find another way then you will not need any of this. Your plug-in engine will be a little bit simple.

Finally I would suggest to have a look at Microsoft's Smart Client Software Factory. It can do pretty much everything you mention and more. It will take some effort to understand it and feel comfortable with it but in the long run it will probably save you loads of time.

这篇关于如何在DLL插件中读取MEF元数据而不将整个DLL复制到内存?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆