是否可以将一个预先存在的DLL嵌入到一个编译好的c#可执行文件中(这样你就只有一个文件可以分发)?如果这是可能的,人们会怎么去做呢?
通常情况下,我不介意把dll放在外面,让安装程序处理所有事情,但工作中有几个人问过我这个问题,老实说,我不知道。
是否可以将一个预先存在的DLL嵌入到一个编译好的c#可执行文件中(这样你就只有一个文件可以分发)?如果这是可能的,人们会怎么去做呢?
通常情况下,我不介意把dll放在外面,让安装程序处理所有事情,但工作中有几个人问过我这个问题,老实说,我不知道。
当前回答
在Visual Studio中右键单击项目,选择项目属性->资源->添加资源->添加现有文件… 并将下面的代码包含到你的App.xaml.cs或等效程序中。
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);
}
以下是我的博客原文: http://codeblog.larsholm.net/2011/06/embed-dlls-easily-in-a-net-assembly/
其他回答
杰弗里·里希特的节选非常好。简而言之,将库作为嵌入式资源添加,并在其他任何事情之前添加回调。下面是我放在控制台应用程序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);
}
};
如果它们实际上是托管程序集,您可以使用ILMerge。对于本地dll,您将有更多的工作要做。
请参见:如何将c++ windows dll合并到c#应用程序exe中?
通常,您需要某种形式的后构建工具来执行您所描述的程序集合并。有一个免费的工具叫做Eazfuscator (eazfuscator.blogspot.com/),它是为字节码分解设计的,同时也处理程序集合并。您可以将此添加到Visual Studio的构建后命令行中以合并程序集,但由于在任何重要的程序集合并场景中会出现的问题,您的里程将有所不同。
您还可以检查构建make untitil NANT是否具有在构建后合并程序集的功能,但我自己对NANT不够熟悉,无法确定是否内置了该功能。
还有很多很多Visual Studio插件会在构建应用程序时执行程序集合并。
或者,如果你不需要自动完成,有很多工具,比如ILMerge,可以将。net程序集合并到一个文件中。
我在合并程序集时遇到的最大问题是它们是否使用类似的名称空间。或者更糟的是,引用同一个dll的不同版本(我的问题通常是NUnit dll文件)。
以下方法不使用外部工具,自动包含所有需要的DLL(不需要手动操作,一切都在编译时完成)
我在这里读了很多回答说使用ILMerge, ILRepack或Jeffrey Ritcher方法,但这些方法都不适合WPF应用程序,也不容易使用。
当你有很多DLL时,很难手动将你需要的DLL包含在你的exe中。我发现的最好的方法是由wegger在StackOverflow上解释的
为了清晰起见,Copy将他的答案粘贴在这里(全部归功于weged)
1)添加到你的。csproj文件:
<Target Name="AfterResolveReferences">
<ItemGroup>
<EmbeddedResource Include="@(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.Extension)' == '.dll'">
<LogicalName>%(ReferenceCopyLocalPaths.DestinationSubDirectory)%(ReferenceCopyLocalPaths.Filename)%(ReferenceCopyLocalPaths.Extension)</LogicalName>
</EmbeddedResource>
</ItemGroup>
</Target>
2)让你的Main Program.cs看起来像这样:
[STAThreadAttribute]
public static void Main()
{
AppDomain.CurrentDomain.AssemblyResolve += OnResolveAssembly;
App.Main();
}
3)添加OnResolveAssembly方法:
private static Assembly OnResolveAssembly(object sender, ResolveEventArgs args)
{
Assembly executingAssembly = Assembly.GetExecutingAssembly();
AssemblyName assemblyName = new AssemblyName(args.Name);
var path = assemblyName.Name + ".dll";
if (assemblyName.CultureInfo.Equals(CultureInfo.InvariantCulture) == false) path = String.Format(@"{0}\{1}", assemblyName.CultureInfo, path);
using (Stream stream = executingAssembly.GetManifestResourceStream(path))
{
if (stream == null) return null;
var assemblyRawBytes = new byte[stream.Length];
stream.Read(assemblyRawBytes, 0, assemblyRawBytes.Length);
return Assembly.Load(assemblyRawBytes);
}
}
.NET Core 3.0原生支持编译为单个.exe
该特性是通过在项目文件(.csproj)中使用以下属性启用的:
<PropertyGroup>
<PublishSingleFile>true</PublishSingleFile>
</PropertyGroup>
这无需任何外部工具即可完成。
详见我对这个问题的回答。