将组件embedded到另一个组件中

如果您创build一个使用其他程序集的类库,是否可以将这些其他程序集embedded到类库中作为某种资源?

即,而不是让MyAssembly.dllSomeAssembly1.dllSomeAssembly2.dll坐在文件系统上,这些其他两个文件捆绑到MyAssembly.dll ,并在其代码中可用。


我也有点困惑,为什么.NET程序集是.dll文件。 .NET之前没有这种格式? 是所有的.NET程序集DLL,但不是所有的DLL都是.NET程序集? 为什么他们使用相同的文件格式和/或文件扩展名?

查看ILMerge来合并程序集。

我也有点困惑,为什么.NET程序集是.dll文件。 .NET之前没有这种格式?

是。

是所有.NET程序集DLL,

通常是DLL或EXE – 但也可以是netmodule。

但不是所有的DLL都是.NET程序集?

正确。

为什么他们使用相同的文件格式和/或文件扩展名?

为什么它会有所不同 – 它服务于同样的目的!

ILMerge不会合并程序集,这很好,但有时候并不是你想要的。 例如,当有问题的程序集是一个强命名的程序集,并且没有密钥时,则不能在不破坏签名的情况下执行ILMerge。 这意味着你必须部署多个程序集。

作为ilmerge的替代方法,您可以将一个或多个程序集作为资源embedded到您的exe或DLL中。 然后,在运行时,当程序集被加载时,可以以编程方式提取embedded程序集并加载并运行它。 这听起来很棘手,但只是一些样板代码。

为此,embedded一个程序集,就像embedded其他资源(图像,翻译文件,数据等)一样。 然后,设置一个在运行时调用的AssemblyResolver。 它应该在启动类的静态构造函数中设置。 代码非常简单。

static NameOfStartupClassHere() { AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(Resolver); } static System.Reflection.Assembly Resolver(object sender, ResolveEventArgs args) { Assembly a1 = Assembly.GetExecutingAssembly(); Stream s = a1.GetManifestResourceStream(args.Name); byte[] block = new byte[s.Length]; s.Read(block, 0, block.Length); Assembly a2 = Assembly.Load(block); return a2; } 

ResolveEventArgs参数上的Name属性是要parsing的程序集的名称。 这个名字是指资源,而不是文件名。 如果您embedded名为“MyAssembly.dll”的文件,并调用embedded的资源“Foo”,那么您要在这里的名称是“Foo”。 但是这会让人困惑,所以我build议使用程序集的文件名作为资源的名称。 如果已经embedded并正确命名了程序集,则可以使用程序集名称调用GetManifestResourceStream(),并以此方式加载程序集。 很简单。

这适用于多个程序集,就像使用单个embedded程序集一样。

在一个真正的应用程序,你会想要更好的error handling在那个例程 – 如果没有给定的名称stream? 如果读取失败会发生什么? 等等,但是这是留给你去做的。

在其余的应用程序代码中,您可以像使用程序集那样正常使用types。

当您构build应用程序时,您需要像正常情况下一样添加对有问题的程序集的引用。 如果您使用命令行工具,请使用csc.exe中的/ r选项; 如果您使用Visual Studio,则需要在项目的popup菜单中“添加引用…”。

在运行时,程序集版本检查和validation将照常运行。

唯一的区别在于分配。 在部署或分发应用程序时,您不需要为embedded(和引用)程序集分发DLL。 只需部署主要组件; 不需要分发其他程序集,因为它们embedded到主DLL或EXE中。

可以embedded一​​个程序集(或其他任何文件)作为一个资源(然后使用ResourceManager类来访问它们),但是如果你只是想将程序集合在一起,最好使用像ILMerge这样的工具。

EXE和DLL文件是Windows可移植的可执行文件 ,它们通用性足以容纳未来types的代码,包括任何.NET代码(它们也可以在DOS下运行,但只显示一条消息,说它们不应该在DOS下运行)。 如果.NET运行时尚未运行,它们包含启动.NET运行时的说明。 单个程序集也可以跨越多个文件,尽pipe这种情况几乎不存在。

注意ILMerge不能用于像XAML这样的embedded式资源,所以WPF应用程序等需要使用Cheeso的方法。

还有Mono项目提供的mkbundle实用程序

为什么他们使用相同的文件格式和/或文件扩展名?

为什么它会有所不同 – 它服务于同样的目的!

我在这里澄清2¢位:DLL是dynamic链接库。 旧式.dll(C代码)和.net风格.dll都是根据定义的“dynamic链接”库。 所以.dll是两个适当的描述。

关于Cheeso将程序集作为资源embedded并使用AssemblyResolve事件处理程序使用Load(byte [])重载dynamic加载它们的答案,您需要修改parsing程序以检查AppDomain是否要加载现有的Assembly实例如果它已经加载,则返回现有的程序集实例。

使用该重载加载的程序集没有上下文,这会导致框架尝试重新装载程序集多次。 在不返回一个已经加载的实例的情况下,可以最终得到相同的汇编代码和types的多个实例,这些代码和types应该是相等的,但不会是这样,因为框架认为它们来自两个不同的程序集。

至less有一种方法,将多个AssemblyResolve事件加载到“无上下文”的同一个程序集中,这是因为当您从加载到您的AppDomain中的多个程序集公开的types引用时,执行代码需要parsing这些types。

https://msdn.microsoft.com/en-us/library/dd153782%28v=vs.110%29.aspx

链接中的几个要点:

“其他程序集不能绑定到没有上下文加载的程序集,除非您处理AppDomain.AssemblyResolve事件”

“在没有上下文的情况下加载具有相同标识的多个程序集可能会导致types身份问题,类似于将具有相同标识的程序集加载到多个上下文中引起的问题。请参阅避免将程序集加载到多个上下文中。