将组件嵌入另一个组件中

时间:2008-10-21 17:01:47

标签: c# .net dll assemblies

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

即。而不是将 MyAssembly.dll SomeAssembly1.dll SomeAssembly2.dll 放在文件系统上,其他两个文件被捆绑到< em> MyAssembly.dll ,可在其代码中使用。


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

8 个答案:

答案 0 :(得分:55)

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中。

答案 1 :(得分:35)

请查看 ILMerge 以合并程序集。

  

我也对.NET程序集为.dll文件的原因感到有些困惑。在.NET之前,这种格式不存在吗?

  

是所有.NET程序集DLL,

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

  

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

正确。

  

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

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

答案 2 :(得分:7)

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

EXE和DLL文件是Windows portable executables,它们通用到足以容纳未来类型的代码,包括任何.NET代码(它们也可以在DOS中运行但只显示一条消息,表明它们不应该在DOS中运行)。它们包括启动.NET运行时的指令(如果尚未运行)。单个程序集也可以跨越多个文件,但事实并非如此。

答案 3 :(得分:5)

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

答案 4 :(得分:4)

还有the Mono project

提供的mkbundle实用程序

答案 5 :(得分:1)

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

  
    

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

  

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

答案 6 :(得分:0)

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

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

至少有一种方法可以为加载到&#34; No context&#34;中的相同程序集创建多个AssemblyResolve事件。是当你从加载到AppDomain中的多个程序集中公开的类型的引用时,因为代码执行需要解析这些类型。

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

链接中的几个要点:

&#34;其他程序集无法绑定到没有上下文加载的程序集,除非您处理AppDomain.AssemblyResolve事件&#34;

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

答案 7 :(得分:0)

我建议您尝试Costura.Fody。只是不要忘记在Costura.Fody之前安装Install-Package Fody(以获取最新的Fody!)