You know, I haven't seen a good answer for this anywhere. Is it possible to embed a pre-existing DLL into a compiled C# executable (so that you only have one file to distribute)? If it is possible, how would one go about doing it?
你知道,我在任何地方都没看到一个好的答案。是否有可能将一个预先存在的DLL嵌入到一个编译好的c#可执行文件中(以便您只有一个要分发的文件)?如果这是可能的,人们会怎么去做呢?
Normally, I'm cool with just leaving the DLLs outside and having the setup program handle everything, but there have been a couple of people at work who have asked me this and I honestly don't know.
通常情况下,我很乐意把dll留在外面,让安装程序来处理所有的事情,但是工作中有几个人问过我这个问题,老实说我也不知道。
19 个解决方案
#1
595
I highly recommend to use Costura.Fody - by far the best and easiest way to embed resources in your assembly. It's available as NuGet package.
我强烈推荐使用Costura。Fody——到目前为止,在程序集中嵌入资源的最佳和最简单的方法。它可以作为NuGet包使用。
Install-Package Costura.Fody
After adding it to the project, it will automatically embed all references that are copied to the output directory into your main assembly. You might want to clean the embedded files by adding a target to your project:
在将其添加到项目之后,它将自动将复制到输出目录的所有引用嵌入到您的主程序集中。您可能希望通过向项目添加目标来清理嵌入式文件:
Install-CleanReferencesTarget
You'll also be able to specify whether to include the pdb's, exclude certain assemblies, or extracting the assemblies on the fly. As far as I know, also unmanaged assemblies are supported.
您还可以指定是否包括pdb,排除某些程序集,或者动态地提取程序集。据我所知,还支持非托管程序集。
Update
更新
Currently, some people are trying to add support for DNX.
目前,一些人正在尝试增加对DNX的支持。
#2
72
If they're actually managed assemblies, you can use ILMerge. For native DLLs, you'll have a bit more work to do.
如果它们实际上是托管的程序集,您可以使用ILMerge。对于本地dll,您将有更多的工作要做。
See also: How can a C++ windows dll be merged into a C# application exe?
参见:如何将c++ windows dll合并到c#应用程序exe中?
#3
70
Just right-click your project in Visual Studio, choose Project Properties -> Resources -> Add Resource -> Add Existing File… And include the code below to your App.xaml.cs or equivalent.
在Visual Studio中右键单击您的项目,选择project Properties ->资源->添加资源->添加现有文件,并将代码包含在您的App.xaml中。计算机科学或同等学历。
public App()
{
AppDomain.CurrentDomain.AssemblyResolve +=new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}
System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
string dllName = args.Name.Contains(',') ? args.Name.Substring(0, args.Name.IndexOf(',')) : args.Name.Replace(".dll","");
dllName = dllName.Replace(".", "_");
if (dllName.EndsWith("_resources")) return null;
System.Resources.ResourceManager rm = new System.Resources.ResourceManager(GetType().Namespace + ".Properties.Resources", System.Reflection.Assembly.GetExecutingAssembly());
byte[] bytes = (byte[])rm.GetObject(dllName);
return System.Reflection.Assembly.Load(bytes);
}
Here's my original blog post: http://codeblog.larsholm.net/2011/06/embed-dlls-easily-in-a-net-assembly/
这是我最初的博客文章:http://codeblog.larsholm.net/2011/06/- dlls-- net-
#4
24
Yes, it is possible to merge .NET executables with libraries. There are multiple tools available to get the job done:
是的,可以将. net可执行文件与库合并。有多种工具可以完成这项工作:
- ILMerge is a utility that can be used to merge multiple .NET assemblies into a single assembly.
- ILMerge是一个实用工具,可用于将多个. net程序集合并到一个程序集中。
- Mono mkbundle, packages an exe and all assemblies with libmono into a single binary package.
- Mono mkbundle,将exe和所有使用libmono的程序集打包为一个二进制包。
- IL-Repack is a FLOSS alterantive to ILMerge, with some additional features.
- IL-Repack是一种对ILMerge的FLOSS alterantive,具有一些额外的特性。
In addition this can be combined with the Mono Linker, which does remove unused code and therefor makes the resulting assembly smaller.
此外,还可以与Mono链接器结合使用,该链接器可以删除未使用的代码,从而使生成的程序集更小。
Another possibility is to use .NETZ, which does not only allow compressing of an assembly, but also can pack the dlls straight into the exe. The difference to the above mentioned solutions is that .NETZ does not merge them, they stay separate assemblies but are packed into one package.
另一种可能是使用. netz,它不仅可以压缩程序集,还可以将dll直接打包到exe中。上面提到的解决方案的不同之处在于。netz不合并它们,它们保持独立的程序集,但是打包成一个包。
.NETZ is a open source tool that compresses and packs the Microsoft .NET Framework executable (EXE, DLL) files in order to make them smaller.
netz是一个开源工具,它压缩和打包Microsoft . net Framework可执行文件(EXE, DLL),以便使它们更小。
#5
16
ILMerge can combine assemblies to one single assembly provided the assembly has only managed code. You can use the commandline app, or add reference to the exe and programmatically merge. For a GUI version there is Eazfuscator, and also .Netz both of which are free. Paid apps include BoxedApp and SmartAssembly.
如果程序集只有托管代码,则ILMerge可以将程序集合并到单个程序集中。您可以使用commandline应用程序,或添加对exe的引用,并以编程方式合并。对于GUI版本,Eazfuscator和。netz都是免费的。付费应用包括BoxedApp和SmartAssembly。
If you have to merge assemblies with unmanaged code, I would suggest SmartAssembly. I never had hiccups with SmartAssembly but with all others. Here, it can embed the required dependencies as resources to your main exe.
如果必须用非托管代码合并程序集,我建议使用SmartAssembly。我从来没有和SmartAssembly一起打嗝,而是和其他所有人一起。在这里,它可以将所需的依赖性嵌入到您的主exe中。
You can do all this manually not needing to worry if assembly is managed or in mixed mode by embedding dll to your resources and then relying on AppDomain's Assembly ResolveHandler
. This is a one stop solution by adopting the worst case, ie assemblies with unmanaged code.
通过将dll嵌入到资源中,然后依赖AppDomain的assembly ResolveHandler,您可以手工完成所有这些操作,而无需担心程序集是否被管理或处于混合模式。这是采用最坏情况的一站式解决方案,即使用非托管代码的程序集。
static void Main()
{
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
string assemblyName = new AssemblyName(args.Name).Name;
if (assemblyName.EndsWith(".resources"))
return null;
string dllName = assemblyName + ".dll";
string dllFullPath = Path.Combine(GetMyApplicationSpecificPath(), dllName);
using (Stream s = Assembly.GetEntryAssembly().GetManifestResourceStream(typeof(Program).Namespace + ".Resources." + dllName))
{
byte[] data = new byte[stream.Length];
s.Read(data, 0, data.Length);
//or just byte[] data = new BinaryReader(s).ReadBytes((int)s.Length);
File.WriteAllBytes(dllFullPath, data);
}
return Assembly.LoadFrom(dllFullPath);
};
}
The key here is to write the bytes to a file and load from its location. To avoid chicken and egg problem, you have to ensure you declare the handler before accessing assembly and that you do not access the assembly members (or instantiate anything that has to deal with the assembly) inside the loading (assembly resolving) part. Also take care to ensure GetMyApplicationSpecificPath()
is not any temp directory since temp files could be attempted to get erased by other programs or by yourself (not that it will get deleted while your program is accessing the dll, but at least its a nuisance. AppData is good location). Also note that you have to write the bytes each time, you cant load from location just 'cos the dll already resides there.
这里的关键是将字节写入文件并从其位置加载。为了避免鸡和蛋的问题,您必须确保在访问程序集之前声明处理程序,并且不访问装载(程序集解析)部分中的程序集成员(或实例化任何必须处理程序集的内容)。也要注意确保getmyapplicationspec路径()不是任何临时目录,因为temp文件可能试图被其他程序或自己删除(而不是在程序访问dll时被删除,但至少它是一个麻烦)。AppData位置)有好处。还要注意,每次都要写入字节,不能从位置加载,因为dll已经驻留在那里。
For managed dlls, you need not write bytes, but directly load from the location of the dll, or just read the bytes and load the assembly from memory. Like this or so:
对于托管dll,您不需要编写字节,而是直接从dll的位置加载,或者只是读取字节并从内存加载程序集。这样左右:
using (Stream s = Assembly.GetEntryAssembly().GetManifestResourceStream(typeof(Program).Namespace + ".Resources." + dllName))
{
byte[] data = new byte[stream.Length];
s.Read(data, 0, data.Length);
return Assembly.Load(data);
}
//or just
return Assembly.LoadFrom(dllFullPath); //if location is known.
If the assembly is fully unmanaged, you can see this link or this as to how to load such dlls.
如果程序集完全不受管理,您可以看到关于如何加载此类dll的链接。
#6
11
The excerpt by Jeffrey Richter is very good. In short, add the library's as embedded resources and add a callback before anything else. Here is a version of the code (found in the comments of his page) that I put at the start of Main method for a console app (just make sure that any calls that use the library's are in a different method to Main).
杰弗里·里希特的选段很好。简而言之,将库作为嵌入式资源添加,并在其他任何东西之前添加回调。这里有一个版本的代码(在他的页面注释中可以找到),我把它放在控制台应用程序的Main方法的开头(只要确保使用库的任何调用都在Main的另一个方法中)。
AppDomain.CurrentDomain.AssemblyResolve += (sender, bargs) =>
{
String dllName = new AssemblyName(bargs.Name).Name + ".dll";
var assem = Assembly.GetExecutingAssembly();
String resourceName = assem.GetManifestResourceNames().FirstOrDefault(rn => rn.EndsWith(dllName));
if (resourceName == null) return null; // Not found, maybe another handler will find it
using (var stream = assem.GetManifestResourceStream(resourceName))
{
Byte[] assemblyData = new Byte[stream.Length];
stream.Read(assemblyData, 0, assemblyData.Length);
return Assembly.Load(assemblyData);
}
};
#7
10
To expand on @Bobby's asnwer above. You can edit your .csproj to use IL-Repack to automatically package all files into a single assembly when you build.
展开上面@Bobby的asnwer。您可以编辑.csproj,以便在构建时使用IL-Repack自动将所有文件打包到一个程序集中。
- Install the nuget ILRepack.MSBuild.Task package with
Install-Package ILRepack.MSBuild.Task
- 安装nuget ILRepack.MSBuild。任务包包含安装包ILRepack.MSBuild.Task
- Edit the AfterBuild section of your .csproj
- 编辑.csproj的AfterBuild部分
Here is a simple sample that merges ExampleAssemblyToMerge.dll into your project output.
下面是一个合并示例程序集的简单示例。在项目输出中加入dll。
<!-- ILRepack -->
<Target Name="AfterBuild" Condition="'$(Configuration)' == 'Release'">
<ItemGroup>
<InputAssemblies Include="$(OutputPath)\$(AssemblyName).exe" />
<InputAssemblies Include="$(OutputPath)\ExampleAssemblyToMerge.dll" />
</ItemGroup>
<ILRepack
Parallel="true"
Internalize="true"
InputAssemblies="@(InputAssemblies)"
TargetKind="Exe"
OutputFile="$(OutputPath)\$(AssemblyName).exe"
/>
</Target>
#8
7
I would recommend you check out the .NETZ utility, which also compresses the assembly with a scheme of your choice:
我建议您看看。netz实用程序,它也压缩程序集与您选择的方案:
http://madebits.com/netz/help.php#single
http://madebits.com/netz/help.php单
#9
6
You could add the DLLs as embedded resources, and then have your program unpack them into the application directory on startup (after checking to see if they're there already).
您可以将dll添加为嵌入式资源,然后让您的程序在启动时将它们解压到应用程序目录中(在检查它们是否已经存在之后)。
Setup files are so easy to make, though, that I don't think this would be worth it.
安装文件非常容易制作,但是,我认为这不值得。
EDIT: This technique would be easy with .NET assemblies. With non-.NET DLLs it would be a lot more work (you'd have to figure out where to unpack the files and register them and so on).
编辑:这种技术对于。net程序集来说很简单。与非。NET dll需要做更多的工作(您必须弄清楚在哪里解压文件并注册它们,等等)。
#10
6
Check boxedapp
检查boxedapp
It can embed a dll into any app. Written in C# too, of course :)
它可以将dll嵌入到任何应用程序中。
Hope it helps.
希望它可以帮助。
#11
6
Neither the ILMerge approach nor Lars Holm Jensen's handling the AssemblyResolve event will work for a plugin host. Say executable H loads assembly P dynamically and accesses it via interface IP defined in an separate assembly. To embed IP into H one shall need a little modification to Lars's code:
ILMerge方法和Lars Holm Jensen处理assembly - resolve事件都不能用于插件主机。说可执行H动态加载程序集P并通过在单独程序集中定义的接口IP访问它。要将IP嵌入H,需要对Lars的代码做一点修改:
Dictionary<string, Assembly> loaded = new Dictionary<string,Assembly>();
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{ Assembly resAssembly;
string dllName = args.Name.Contains(",") ? args.Name.Substring(0, args.Name.IndexOf(',')) : args.Name.Replace(".dll","");
dllName = dllName.Replace(".", "_");
if ( !loaded.ContainsKey( dllName ) )
{ if (dllName.EndsWith("_resources")) return null;
System.Resources.ResourceManager rm = new System.Resources.ResourceManager(GetType().Namespace + ".Properties.Resources", System.Reflection.Assembly.GetExecutingAssembly());
byte[] bytes = (byte[])rm.GetObject(dllName);
resAssembly = System.Reflection.Assembly.Load(bytes);
loaded.Add(dllName, resAssembly);
}
else
{ resAssembly = loaded[dllName]; }
return resAssembly;
};
The trick to handle repeated attempts to resolve the same assembly and return the existing one instead of creating a new instance.
处理多次尝试解析同一程序集并返回现有程序集而不是创建新实例的技巧。
EDIT: Lest it spoil .NET's serialization, make sure to return null for all assemblies not embedded in yours, thereby defaulting to the standard behaviour. You can get a list of these libraries by:
编辑:为了避免它破坏。net的序列化,请确保没有嵌入到您的程序集中的所有程序集都返回null,从而默认为标准行为。你可透过以下途径索取这些图书馆的名单:
static HashSet<string> IncludedAssemblies = new HashSet<string>();
string[] resources = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceNames();
for(int i = 0; i < resources.Length; i++)
{ IncludedAssemblies.Add(resources[i]); }
and just return null if the passed assembly does not belong to IncludedAssemblies
.
如果所传递的程序集不属于包含程序集,则返回null。
#12
5
Another product that can handle this elegantly is SmartAssembly, at SmartAssembly.com . This product will, in addition to merging all dependencies into a single DLL, (optionally) obfuscate your code, remove extra meta-data to reduce the resulting file size, and can also actually optimize the IL to increase runtime performance. There is also some kind of global exception handling / reporting feature it adds to your software (if desired) that I didn't take the time to understand, but could be useful. I believe it also has a command line API so you can make it part of your build process.
另一款可以在SmartAssembly网站上优雅地使用的产品是SmartAssembly。除了将所有依赖项合并到一个单独的DLL中(可选地)混淆代码之外,该产品还将删除额外的元数据以减少产生的文件大小,并且实际上还可以优化IL以提高运行时性能。它还添加了一些全局异常处理/报告特性(如果需要的话),我没有花时间去理解它们,但可能会很有用。我相信它还有一个命令行API,所以您可以将它作为构建过程的一部分。
#14
3
Besides ILMerge, if you don't want to bother with command line switches, I really recommend ILMerge-Gui. It's an open source project, really good!
除了ILMerge,如果您不想麻烦使用命令行开关,我真的推荐ILMerge- gui。这是一个开源项目,真的很好!
#15
2
It may sound simplistic, but WinRar gives the option to compress a bunch of files to a self-extracting executable.
It has lots of configurable options: final icon, extract files to given path, file to execute after extraction, custom logo/texts for popup shown during extraction, no popup window at all, license agreement text, etc.
May be useful in some cases.
这听起来可能过于简单,但WinRar提供了将一堆文件压缩到自提取可执行文件的选项。它有很多可配置的选项:最终图标、提取到给定路径的文件、提取后要执行的文件、提取过程中显示的自定义标识/文本、完全没有弹出窗口、许可协议文本等。
#16
1
I use the csc.exe compiler called from a .vbs script.
我使用了csc。从.vbs脚本调用的exe编译器。
In your xyz.cs script, add the following lines after the directives (my example is for the Renci SSH):
在你的xyz。cs脚本,在指令后面添加以下几行(我的例子是针对Renci SSH的):
using System;
using Renci;//FOR THE SSH
using System.Net;//FOR THE ADDRESS TRANSLATION
using System.Reflection;//FOR THE Assembly
//+ref>"C:\Program Files (x86)\Microsoft\ILMerge\Renci.SshNet.dll"
//+res>"C:\Program Files (x86)\Microsoft\ILMerge\Renci.SshNet.dll"
//+ico>"C:\Program Files (x86)\Microsoft CAPICOM 2.1.0.2 SDK\Samples\c_sharp\xmldsig\resources\Traffic.ico"
The ref, res and ico tags will be picked up by the .vbs script below to form the csc command.
ref、res和ico标记将由下面的.vbs脚本获取,以形成csc命令。
Then add the assembly resolver caller in the Main:
然后将汇编解析程序调用程序添加到主程序中:
public static void Main(string[] args)
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
.
...and add the resolver itself somewhere in the class:
…并将解析器本身添加到类中的某个位置:
static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { String resourceName = new AssemblyName(args.Name).Name + ".dll"; using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName)) { Byte[] assemblyData = new Byte[stream.Length]; stream.Read(assemblyData, 0, assemblyData.Length); return Assembly.Load(assemblyData); } }
I name the vbs script to match the .cs filename (e.g. ssh.vbs looks for ssh.cs); this makes running the script numerous times a lot easier, but if you aren't an idiot like me then a generic script could pick up the target .cs file from a drag-and-drop:
我将vbs脚本命名为匹配.cs文件名(例如ssh)。根据查找ssh.cs);这使得多次运行脚本变得容易得多,但是如果您不是像我这样的白痴,那么通用脚本可以从拖放中获取目标.cs文件:
Dim name_,oShell,fso Set oShell = CreateObject("Shell.Application") Set fso = CreateObject("Scripting.fileSystemObject") 'TAKE THE VBS SCRIPT NAME AS THE TARGET FILE NAME '################################################ name_ = Split(wscript.ScriptName, ".")(0) 'GET THE EXTERNAL DLL's AND ICON NAMES FROM THE .CS FILE '####################################################### Const OPEN_FILE_FOR_READING = 1 Set objInputFile = fso.OpenTextFile(name_ & ".cs", 1) 'READ EVERYTHING INTO AN ARRAY '############################# inputData = Split(objInputFile.ReadAll, vbNewline) For each strData In inputData if left(strData,7)="//+ref>" then csc_references = csc_references & " /reference:" & trim(replace(strData,"//+ref>","")) & " " end if if left(strData,7)="//+res>" then csc_resources = csc_resources & " /resource:" & trim(replace(strData,"//+res>","")) & " " end if if left(strData,7)="//+ico>" then csc_icon = " /win32icon:" & trim(replace(strData,"//+ico>","")) & " " end if Next objInputFile.Close 'COMPILE THE FILE '################ oShell.ShellExecute "c:\windows\microsoft.net\framework\v3.5\csc.exe", "/warn:1 /target:exe " & csc_references & csc_resources & csc_icon & " " & name_ & ".cs", "", "runas", 2 WScript.Quit(0)
#17
0
It's possible but not all that easy, to create a hybrid native/managed assembly in C#. Were you using C++ instead it'd be a lot easier, as the Visual C++ compiler can create hybrid assemblies as easily as anything else.
在c#中创建一个混合的本机/托管程序集是可能的,但并不容易。如果您使用c++,那么就会容易得多,因为Visual c++编译器可以很容易地创建混合程序集。
Unless you have a strict requirement to produce a hybrid assembly, I'd agree with MusiGenesis that this isn't really worth the trouble to do with C#. If you need to do it, perhaps look at moving to C++/CLI instead.
除非你有一个严格的要求,生产一个混合装配,我同意音乐起源,这并不是真的值得麻烦与c#。如果您需要这样做,可以考虑迁移到c++ /CLI。
#18
0
Generally you would need some form of post build tool to perform an assembly merge like you are describing. There is a free tool called Eazfuscator (eazfuscator.blogspot.com/) which is designed for bytecode mangling that also handles assembly merging. You can add this into a post build command line with Visual Studio to merge your assemblies, but your mileage will vary due to issues that will arise in any non trival assembly merging scenarios.
通常,您需要某种形式的post构建工具来执行程序集合并,就像您所描述的那样。有一个免费的工具叫Eazfuscator (eazfuscator.blogspot.com/),它是为字节码管理而设计的,也可以处理程序集合并。您可以将此添加到一个与Visual Studio一起构建的post构建命令行来合并您的程序集,但是您的里程将因在任何非trival程序集合并场景中出现的问题而有所不同。
You could also check to see if the build make untility NANT has the ability to merge assemblies after building, but I am not familiar enough with NANT myself to say whether the functionality is built in or not.
您还可以检查构建make untility是否有在构建之后合并程序集的能力,但是我对NANT本身还不够熟悉,无法确定该功能是否在构建过程中构建。
There are also many many Visual Studio plugins that will perform assembly merging as part of building the application.
还有许多Visual Studio插件将执行程序集合并,作为构建应用程序的一部分。
Alternatively if you don't need this to be done automatically, there are a number of tools like ILMerge that will merge .net assemblies into a single file.
或者,如果您不需要自动完成这个操作,那么有很多工具,比如ILMerge,可以将.net程序集合并到一个文件中。
The biggest issue I've had with merging assemblies is if they use any similar namespaces. Or worse, reference different versions of the same dll (my problems were generally with the NUnit dll files).
对于合并程序集,我遇到的最大问题是它们是否使用任何类似的名称空间。或者更糟糕的是,引用相同dll的不同版本(我的问题通常是关于NUnit dll文件的)。
#19
0
I tried out this solution at code-project which embeds the DLL: http://www.codeproject.com/Articles/528178/Load-DLL-From-Embedded-Resource
我在code-project中尝试了这个解决方案,它嵌入了DLL: http://www.codeproject.com/articles/528178 / load-dll-from- embedatedresource
And it worked just fine.
而且效果很好。
#1
595
I highly recommend to use Costura.Fody - by far the best and easiest way to embed resources in your assembly. It's available as NuGet package.
我强烈推荐使用Costura。Fody——到目前为止,在程序集中嵌入资源的最佳和最简单的方法。它可以作为NuGet包使用。
Install-Package Costura.Fody
After adding it to the project, it will automatically embed all references that are copied to the output directory into your main assembly. You might want to clean the embedded files by adding a target to your project:
在将其添加到项目之后,它将自动将复制到输出目录的所有引用嵌入到您的主程序集中。您可能希望通过向项目添加目标来清理嵌入式文件:
Install-CleanReferencesTarget
You'll also be able to specify whether to include the pdb's, exclude certain assemblies, or extracting the assemblies on the fly. As far as I know, also unmanaged assemblies are supported.
您还可以指定是否包括pdb,排除某些程序集,或者动态地提取程序集。据我所知,还支持非托管程序集。
Update
更新
Currently, some people are trying to add support for DNX.
目前,一些人正在尝试增加对DNX的支持。
#2
72
If they're actually managed assemblies, you can use ILMerge. For native DLLs, you'll have a bit more work to do.
如果它们实际上是托管的程序集,您可以使用ILMerge。对于本地dll,您将有更多的工作要做。
See also: How can a C++ windows dll be merged into a C# application exe?
参见:如何将c++ windows dll合并到c#应用程序exe中?
#3
70
Just right-click your project in Visual Studio, choose Project Properties -> Resources -> Add Resource -> Add Existing File… And include the code below to your App.xaml.cs or equivalent.
在Visual Studio中右键单击您的项目,选择project Properties ->资源->添加资源->添加现有文件,并将代码包含在您的App.xaml中。计算机科学或同等学历。
public App()
{
AppDomain.CurrentDomain.AssemblyResolve +=new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}
System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
string dllName = args.Name.Contains(',') ? args.Name.Substring(0, args.Name.IndexOf(',')) : args.Name.Replace(".dll","");
dllName = dllName.Replace(".", "_");
if (dllName.EndsWith("_resources")) return null;
System.Resources.ResourceManager rm = new System.Resources.ResourceManager(GetType().Namespace + ".Properties.Resources", System.Reflection.Assembly.GetExecutingAssembly());
byte[] bytes = (byte[])rm.GetObject(dllName);
return System.Reflection.Assembly.Load(bytes);
}
Here's my original blog post: http://codeblog.larsholm.net/2011/06/embed-dlls-easily-in-a-net-assembly/
这是我最初的博客文章:http://codeblog.larsholm.net/2011/06/- dlls-- net-
#4
24
Yes, it is possible to merge .NET executables with libraries. There are multiple tools available to get the job done:
是的,可以将. net可执行文件与库合并。有多种工具可以完成这项工作:
- ILMerge is a utility that can be used to merge multiple .NET assemblies into a single assembly.
- ILMerge是一个实用工具,可用于将多个. net程序集合并到一个程序集中。
- Mono mkbundle, packages an exe and all assemblies with libmono into a single binary package.
- Mono mkbundle,将exe和所有使用libmono的程序集打包为一个二进制包。
- IL-Repack is a FLOSS alterantive to ILMerge, with some additional features.
- IL-Repack是一种对ILMerge的FLOSS alterantive,具有一些额外的特性。
In addition this can be combined with the Mono Linker, which does remove unused code and therefor makes the resulting assembly smaller.
此外,还可以与Mono链接器结合使用,该链接器可以删除未使用的代码,从而使生成的程序集更小。
Another possibility is to use .NETZ, which does not only allow compressing of an assembly, but also can pack the dlls straight into the exe. The difference to the above mentioned solutions is that .NETZ does not merge them, they stay separate assemblies but are packed into one package.
另一种可能是使用. netz,它不仅可以压缩程序集,还可以将dll直接打包到exe中。上面提到的解决方案的不同之处在于。netz不合并它们,它们保持独立的程序集,但是打包成一个包。
.NETZ is a open source tool that compresses and packs the Microsoft .NET Framework executable (EXE, DLL) files in order to make them smaller.
netz是一个开源工具,它压缩和打包Microsoft . net Framework可执行文件(EXE, DLL),以便使它们更小。
#5
16
ILMerge can combine assemblies to one single assembly provided the assembly has only managed code. You can use the commandline app, or add reference to the exe and programmatically merge. For a GUI version there is Eazfuscator, and also .Netz both of which are free. Paid apps include BoxedApp and SmartAssembly.
如果程序集只有托管代码,则ILMerge可以将程序集合并到单个程序集中。您可以使用commandline应用程序,或添加对exe的引用,并以编程方式合并。对于GUI版本,Eazfuscator和。netz都是免费的。付费应用包括BoxedApp和SmartAssembly。
If you have to merge assemblies with unmanaged code, I would suggest SmartAssembly. I never had hiccups with SmartAssembly but with all others. Here, it can embed the required dependencies as resources to your main exe.
如果必须用非托管代码合并程序集,我建议使用SmartAssembly。我从来没有和SmartAssembly一起打嗝,而是和其他所有人一起。在这里,它可以将所需的依赖性嵌入到您的主exe中。
You can do all this manually not needing to worry if assembly is managed or in mixed mode by embedding dll to your resources and then relying on AppDomain's Assembly ResolveHandler
. This is a one stop solution by adopting the worst case, ie assemblies with unmanaged code.
通过将dll嵌入到资源中,然后依赖AppDomain的assembly ResolveHandler,您可以手工完成所有这些操作,而无需担心程序集是否被管理或处于混合模式。这是采用最坏情况的一站式解决方案,即使用非托管代码的程序集。
static void Main()
{
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
string assemblyName = new AssemblyName(args.Name).Name;
if (assemblyName.EndsWith(".resources"))
return null;
string dllName = assemblyName + ".dll";
string dllFullPath = Path.Combine(GetMyApplicationSpecificPath(), dllName);
using (Stream s = Assembly.GetEntryAssembly().GetManifestResourceStream(typeof(Program).Namespace + ".Resources." + dllName))
{
byte[] data = new byte[stream.Length];
s.Read(data, 0, data.Length);
//or just byte[] data = new BinaryReader(s).ReadBytes((int)s.Length);
File.WriteAllBytes(dllFullPath, data);
}
return Assembly.LoadFrom(dllFullPath);
};
}
The key here is to write the bytes to a file and load from its location. To avoid chicken and egg problem, you have to ensure you declare the handler before accessing assembly and that you do not access the assembly members (or instantiate anything that has to deal with the assembly) inside the loading (assembly resolving) part. Also take care to ensure GetMyApplicationSpecificPath()
is not any temp directory since temp files could be attempted to get erased by other programs or by yourself (not that it will get deleted while your program is accessing the dll, but at least its a nuisance. AppData is good location). Also note that you have to write the bytes each time, you cant load from location just 'cos the dll already resides there.
这里的关键是将字节写入文件并从其位置加载。为了避免鸡和蛋的问题,您必须确保在访问程序集之前声明处理程序,并且不访问装载(程序集解析)部分中的程序集成员(或实例化任何必须处理程序集的内容)。也要注意确保getmyapplicationspec路径()不是任何临时目录,因为temp文件可能试图被其他程序或自己删除(而不是在程序访问dll时被删除,但至少它是一个麻烦)。AppData位置)有好处。还要注意,每次都要写入字节,不能从位置加载,因为dll已经驻留在那里。
For managed dlls, you need not write bytes, but directly load from the location of the dll, or just read the bytes and load the assembly from memory. Like this or so:
对于托管dll,您不需要编写字节,而是直接从dll的位置加载,或者只是读取字节并从内存加载程序集。这样左右:
using (Stream s = Assembly.GetEntryAssembly().GetManifestResourceStream(typeof(Program).Namespace + ".Resources." + dllName))
{
byte[] data = new byte[stream.Length];
s.Read(data, 0, data.Length);
return Assembly.Load(data);
}
//or just
return Assembly.LoadFrom(dllFullPath); //if location is known.
If the assembly is fully unmanaged, you can see this link or this as to how to load such dlls.
如果程序集完全不受管理,您可以看到关于如何加载此类dll的链接。
#6
11
The excerpt by Jeffrey Richter is very good. In short, add the library's as embedded resources and add a callback before anything else. Here is a version of the code (found in the comments of his page) that I put at the start of Main method for a console app (just make sure that any calls that use the library's are in a different method to Main).
杰弗里·里希特的选段很好。简而言之,将库作为嵌入式资源添加,并在其他任何东西之前添加回调。这里有一个版本的代码(在他的页面注释中可以找到),我把它放在控制台应用程序的Main方法的开头(只要确保使用库的任何调用都在Main的另一个方法中)。
AppDomain.CurrentDomain.AssemblyResolve += (sender, bargs) =>
{
String dllName = new AssemblyName(bargs.Name).Name + ".dll";
var assem = Assembly.GetExecutingAssembly();
String resourceName = assem.GetManifestResourceNames().FirstOrDefault(rn => rn.EndsWith(dllName));
if (resourceName == null) return null; // Not found, maybe another handler will find it
using (var stream = assem.GetManifestResourceStream(resourceName))
{
Byte[] assemblyData = new Byte[stream.Length];
stream.Read(assemblyData, 0, assemblyData.Length);
return Assembly.Load(assemblyData);
}
};
#7
10
To expand on @Bobby's asnwer above. You can edit your .csproj to use IL-Repack to automatically package all files into a single assembly when you build.
展开上面@Bobby的asnwer。您可以编辑.csproj,以便在构建时使用IL-Repack自动将所有文件打包到一个程序集中。
- Install the nuget ILRepack.MSBuild.Task package with
Install-Package ILRepack.MSBuild.Task
- 安装nuget ILRepack.MSBuild。任务包包含安装包ILRepack.MSBuild.Task
- Edit the AfterBuild section of your .csproj
- 编辑.csproj的AfterBuild部分
Here is a simple sample that merges ExampleAssemblyToMerge.dll into your project output.
下面是一个合并示例程序集的简单示例。在项目输出中加入dll。
<!-- ILRepack -->
<Target Name="AfterBuild" Condition="'$(Configuration)' == 'Release'">
<ItemGroup>
<InputAssemblies Include="$(OutputPath)\$(AssemblyName).exe" />
<InputAssemblies Include="$(OutputPath)\ExampleAssemblyToMerge.dll" />
</ItemGroup>
<ILRepack
Parallel="true"
Internalize="true"
InputAssemblies="@(InputAssemblies)"
TargetKind="Exe"
OutputFile="$(OutputPath)\$(AssemblyName).exe"
/>
</Target>
#8
7
I would recommend you check out the .NETZ utility, which also compresses the assembly with a scheme of your choice:
我建议您看看。netz实用程序,它也压缩程序集与您选择的方案:
http://madebits.com/netz/help.php#single
http://madebits.com/netz/help.php单
#9
6
You could add the DLLs as embedded resources, and then have your program unpack them into the application directory on startup (after checking to see if they're there already).
您可以将dll添加为嵌入式资源,然后让您的程序在启动时将它们解压到应用程序目录中(在检查它们是否已经存在之后)。
Setup files are so easy to make, though, that I don't think this would be worth it.
安装文件非常容易制作,但是,我认为这不值得。
EDIT: This technique would be easy with .NET assemblies. With non-.NET DLLs it would be a lot more work (you'd have to figure out where to unpack the files and register them and so on).
编辑:这种技术对于。net程序集来说很简单。与非。NET dll需要做更多的工作(您必须弄清楚在哪里解压文件并注册它们,等等)。
#10
6
Check boxedapp
检查boxedapp
It can embed a dll into any app. Written in C# too, of course :)
它可以将dll嵌入到任何应用程序中。
Hope it helps.
希望它可以帮助。
#11
6
Neither the ILMerge approach nor Lars Holm Jensen's handling the AssemblyResolve event will work for a plugin host. Say executable H loads assembly P dynamically and accesses it via interface IP defined in an separate assembly. To embed IP into H one shall need a little modification to Lars's code:
ILMerge方法和Lars Holm Jensen处理assembly - resolve事件都不能用于插件主机。说可执行H动态加载程序集P并通过在单独程序集中定义的接口IP访问它。要将IP嵌入H,需要对Lars的代码做一点修改:
Dictionary<string, Assembly> loaded = new Dictionary<string,Assembly>();
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{ Assembly resAssembly;
string dllName = args.Name.Contains(",") ? args.Name.Substring(0, args.Name.IndexOf(',')) : args.Name.Replace(".dll","");
dllName = dllName.Replace(".", "_");
if ( !loaded.ContainsKey( dllName ) )
{ if (dllName.EndsWith("_resources")) return null;
System.Resources.ResourceManager rm = new System.Resources.ResourceManager(GetType().Namespace + ".Properties.Resources", System.Reflection.Assembly.GetExecutingAssembly());
byte[] bytes = (byte[])rm.GetObject(dllName);
resAssembly = System.Reflection.Assembly.Load(bytes);
loaded.Add(dllName, resAssembly);
}
else
{ resAssembly = loaded[dllName]; }
return resAssembly;
};
The trick to handle repeated attempts to resolve the same assembly and return the existing one instead of creating a new instance.
处理多次尝试解析同一程序集并返回现有程序集而不是创建新实例的技巧。
EDIT: Lest it spoil .NET's serialization, make sure to return null for all assemblies not embedded in yours, thereby defaulting to the standard behaviour. You can get a list of these libraries by:
编辑:为了避免它破坏。net的序列化,请确保没有嵌入到您的程序集中的所有程序集都返回null,从而默认为标准行为。你可透过以下途径索取这些图书馆的名单:
static HashSet<string> IncludedAssemblies = new HashSet<string>();
string[] resources = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceNames();
for(int i = 0; i < resources.Length; i++)
{ IncludedAssemblies.Add(resources[i]); }
and just return null if the passed assembly does not belong to IncludedAssemblies
.
如果所传递的程序集不属于包含程序集,则返回null。
#12
5
Another product that can handle this elegantly is SmartAssembly, at SmartAssembly.com . This product will, in addition to merging all dependencies into a single DLL, (optionally) obfuscate your code, remove extra meta-data to reduce the resulting file size, and can also actually optimize the IL to increase runtime performance. There is also some kind of global exception handling / reporting feature it adds to your software (if desired) that I didn't take the time to understand, but could be useful. I believe it also has a command line API so you can make it part of your build process.
另一款可以在SmartAssembly网站上优雅地使用的产品是SmartAssembly。除了将所有依赖项合并到一个单独的DLL中(可选地)混淆代码之外,该产品还将删除额外的元数据以减少产生的文件大小,并且实际上还可以优化IL以提高运行时性能。它还添加了一些全局异常处理/报告特性(如果需要的话),我没有花时间去理解它们,但可能会很有用。我相信它还有一个命令行API,所以您可以将它作为构建过程的一部分。
#13
#14
3
Besides ILMerge, if you don't want to bother with command line switches, I really recommend ILMerge-Gui. It's an open source project, really good!
除了ILMerge,如果您不想麻烦使用命令行开关,我真的推荐ILMerge- gui。这是一个开源项目,真的很好!
#15
2
It may sound simplistic, but WinRar gives the option to compress a bunch of files to a self-extracting executable.
It has lots of configurable options: final icon, extract files to given path, file to execute after extraction, custom logo/texts for popup shown during extraction, no popup window at all, license agreement text, etc.
May be useful in some cases.
这听起来可能过于简单,但WinRar提供了将一堆文件压缩到自提取可执行文件的选项。它有很多可配置的选项:最终图标、提取到给定路径的文件、提取后要执行的文件、提取过程中显示的自定义标识/文本、完全没有弹出窗口、许可协议文本等。
#16
1
I use the csc.exe compiler called from a .vbs script.
我使用了csc。从.vbs脚本调用的exe编译器。
In your xyz.cs script, add the following lines after the directives (my example is for the Renci SSH):
在你的xyz。cs脚本,在指令后面添加以下几行(我的例子是针对Renci SSH的):
using System;
using Renci;//FOR THE SSH
using System.Net;//FOR THE ADDRESS TRANSLATION
using System.Reflection;//FOR THE Assembly
//+ref>"C:\Program Files (x86)\Microsoft\ILMerge\Renci.SshNet.dll"
//+res>"C:\Program Files (x86)\Microsoft\ILMerge\Renci.SshNet.dll"
//+ico>"C:\Program Files (x86)\Microsoft CAPICOM 2.1.0.2 SDK\Samples\c_sharp\xmldsig\resources\Traffic.ico"
The ref, res and ico tags will be picked up by the .vbs script below to form the csc command.
ref、res和ico标记将由下面的.vbs脚本获取,以形成csc命令。
Then add the assembly resolver caller in the Main:
然后将汇编解析程序调用程序添加到主程序中:
public static void Main(string[] args)
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
.
...and add the resolver itself somewhere in the class:
…并将解析器本身添加到类中的某个位置:
static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { String resourceName = new AssemblyName(args.Name).Name + ".dll"; using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName)) { Byte[] assemblyData = new Byte[stream.Length]; stream.Read(assemblyData, 0, assemblyData.Length); return Assembly.Load(assemblyData); } }
I name the vbs script to match the .cs filename (e.g. ssh.vbs looks for ssh.cs); this makes running the script numerous times a lot easier, but if you aren't an idiot like me then a generic script could pick up the target .cs file from a drag-and-drop:
我将vbs脚本命名为匹配.cs文件名(例如ssh)。根据查找ssh.cs);这使得多次运行脚本变得容易得多,但是如果您不是像我这样的白痴,那么通用脚本可以从拖放中获取目标.cs文件:
Dim name_,oShell,fso Set oShell = CreateObject("Shell.Application") Set fso = CreateObject("Scripting.fileSystemObject") 'TAKE THE VBS SCRIPT NAME AS THE TARGET FILE NAME '################################################ name_ = Split(wscript.ScriptName, ".")(0) 'GET THE EXTERNAL DLL's AND ICON NAMES FROM THE .CS FILE '####################################################### Const OPEN_FILE_FOR_READING = 1 Set objInputFile = fso.OpenTextFile(name_ & ".cs", 1) 'READ EVERYTHING INTO AN ARRAY '############################# inputData = Split(objInputFile.ReadAll, vbNewline) For each strData In inputData if left(strData,7)="//+ref>" then csc_references = csc_references & " /reference:" & trim(replace(strData,"//+ref>","")) & " " end if if left(strData,7)="//+res>" then csc_resources = csc_resources & " /resource:" & trim(replace(strData,"//+res>","")) & " " end if if left(strData,7)="//+ico>" then csc_icon = " /win32icon:" & trim(replace(strData,"//+ico>","")) & " " end if Next objInputFile.Close 'COMPILE THE FILE '################ oShell.ShellExecute "c:\windows\microsoft.net\framework\v3.5\csc.exe", "/warn:1 /target:exe " & csc_references & csc_resources & csc_icon & " " & name_ & ".cs", "", "runas", 2 WScript.Quit(0)
#17
0
It's possible but not all that easy, to create a hybrid native/managed assembly in C#. Were you using C++ instead it'd be a lot easier, as the Visual C++ compiler can create hybrid assemblies as easily as anything else.
在c#中创建一个混合的本机/托管程序集是可能的,但并不容易。如果您使用c++,那么就会容易得多,因为Visual c++编译器可以很容易地创建混合程序集。
Unless you have a strict requirement to produce a hybrid assembly, I'd agree with MusiGenesis that this isn't really worth the trouble to do with C#. If you need to do it, perhaps look at moving to C++/CLI instead.
除非你有一个严格的要求,生产一个混合装配,我同意音乐起源,这并不是真的值得麻烦与c#。如果您需要这样做,可以考虑迁移到c++ /CLI。
#18
0
Generally you would need some form of post build tool to perform an assembly merge like you are describing. There is a free tool called Eazfuscator (eazfuscator.blogspot.com/) which is designed for bytecode mangling that also handles assembly merging. You can add this into a post build command line with Visual Studio to merge your assemblies, but your mileage will vary due to issues that will arise in any non trival assembly merging scenarios.
通常,您需要某种形式的post构建工具来执行程序集合并,就像您所描述的那样。有一个免费的工具叫Eazfuscator (eazfuscator.blogspot.com/),它是为字节码管理而设计的,也可以处理程序集合并。您可以将此添加到一个与Visual Studio一起构建的post构建命令行来合并您的程序集,但是您的里程将因在任何非trival程序集合并场景中出现的问题而有所不同。
You could also check to see if the build make untility NANT has the ability to merge assemblies after building, but I am not familiar enough with NANT myself to say whether the functionality is built in or not.
您还可以检查构建make untility是否有在构建之后合并程序集的能力,但是我对NANT本身还不够熟悉,无法确定该功能是否在构建过程中构建。
There are also many many Visual Studio plugins that will perform assembly merging as part of building the application.
还有许多Visual Studio插件将执行程序集合并,作为构建应用程序的一部分。
Alternatively if you don't need this to be done automatically, there are a number of tools like ILMerge that will merge .net assemblies into a single file.
或者,如果您不需要自动完成这个操作,那么有很多工具,比如ILMerge,可以将.net程序集合并到一个文件中。
The biggest issue I've had with merging assemblies is if they use any similar namespaces. Or worse, reference different versions of the same dll (my problems were generally with the NUnit dll files).
对于合并程序集,我遇到的最大问题是它们是否使用任何类似的名称空间。或者更糟糕的是,引用相同dll的不同版本(我的问题通常是关于NUnit dll文件的)。
#19
0
I tried out this solution at code-project which embeds the DLL: http://www.codeproject.com/Articles/528178/Load-DLL-From-Embedded-Resource
我在code-project中尝试了这个解决方案,它嵌入了DLL: http://www.codeproject.com/articles/528178 / load-dll-from- embedatedresource
And it worked just fine.
而且效果很好。