41

如果您创建一个使用来自其他程序集的东西的类库,是否可以将这些其他程序集作为某种资源嵌入到类库中?

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


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

4

8 回答 8

59

ILMerge 确实合并程序集,这很好,但有时不是您想要的。例如,当所讨论的程序集是一个强名称程序集,而您没有它的密钥时,您将无法在不破坏该签名的情况下执行 ILMerge。这意味着您必须部署多个程序集。

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

为此,请嵌入程序集,就像嵌入任何其他资源(图像、翻译文件、数据等)一样。然后,设置一个在运行时调用的 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 属性是要解析的程序集的名称。此名称指的是资源,而不是文件名。如果嵌入名为“MyAssembly.dll”的文件,并调用嵌入的资源“Foo”,那么这里需要的名称是“Foo”。但这会令人困惑,所以我建议使用程序集的文件名作为资源的名称。如果您已正确嵌入并命名程序集,则只需使用程序集名称调用 GetManifestResourceStream() 并以这种方式加载程序集。非常简单。

这适用于多个程序集,就像使用单个嵌入式程序集一样好。

在一个真实的应用程序中,您会希望在该例程中进行更好的错误处理 - 比如如果没有给定名称的流怎么办?如果读取失败会发生什么?等等,但这留给你做。

在其余的应用程序代码中,您照常使用程序集中的类型。

构建应用程序时,您需要像往常一样添加对相关程序集的引用。如果使用命令行工具,请使用 csc.exe 中的 /r 选项;如果您使用 Visual Studio,则需要在项目的弹出菜单中添加“添加引用...”。

在运行时,程序集版本检查和验证照常工作。

唯一的区别是分布。当您部署或分发您的应用程序时,您不需要分发嵌入(和引用)程序集的 DLL。只需部署主程序集;无需分发其他程序集,因为它们已嵌入到主 DLL 或 EXE 中。

于 2009-03-09T05:27:20.253 回答
35

查看ILMerge以合并程序集。

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

是的。

都是 .NET 程序集 DLL,

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

但并非所有 DLL 都是 .NET 程序集?

正确的。

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

为什么它应该有所不同 - 它的目的是相同的!

于 2008-10-21T17:11:05.417 回答
8

可以将程序集(或任何文件,实际上)嵌入为资源(然后使用ResourceManager类来访问它们),但如果您只想组合程序集,最好使用像ILMerge这样的工具。

EXE 和 DLL 文件是Windows 可移植可执行文件,它们足够通用,可以容纳未来的代码类型,包括任何 .NET 代码(它们也可以在 DOS 中运行,但只显示一条消息,指出它们不应该在 DOS 中运行)。它们包括启动 .NET 运行时(如果它尚未运行)的说明。单个程序集也有可能跨越多个文件,尽管这种情况几乎从未发生过。

于 2008-10-21T17:11:24.807 回答
5

注意 ILMerge 不适用于 XAML 等嵌入式资源,因此 WPF 应用程序等需要使用 Cheeso 的方法。

于 2009-05-13T22:51:13.760 回答
4

Mono 项目还提供了 mkbundle 实用程序

于 2008-10-21T17:19:42.810 回答
1

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

为什么它应该有所不同 - 它的目的是相同的!

我在这里澄清一下 2 美分:DLL 是动态链接库。旧式 .dll(C 代码)和 .net 式 .dll 都是定义为“动态链接”库。所以 .dll 是对两者的正确描述。

于 2012-12-10T19:53:44.523 回答
0

关于 Cheeso 将程序集嵌入为资源并使用 Load(byte[]) 重载使用 AssemblyResolve 事件处理程序动态加载它们的答案,您需要修改解析器以检查 AppDomain 以获取要加载和如果已经加载,则返回现有的程序集实例。

使用该重载加载的程序集没有上下文,这可能导致框架尝试多次重新加载程序集。如果不返回已加载的实例,您最终可能会得到相同程序集代码和类型的多个实例,这些实例应该相等但不相等,因为框架认为它们来自两个不同的程序集。

为加载到“无上下文”中的同一个程序集生成多个 AssemblyResolve 事件的至少一种方法是,当您引用从加载到 AppDomain 中的多个程序集中公开的类型时,因为代码执行需要解析这些类型。

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

链接中的几个要点:

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

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

于 2016-01-14T22:52:21.897 回答
0

我建议你试试Costura.Fody。只是不要忘记在 Costura.Fody 之前安装包 Fody(以获得最新的 Fody!)

于 2018-11-14T06:33:56.440 回答