如何在 wpf / winforms 应用程序中将 DLL 与 .exe 组合(含图片)
How to combine DLLs with .exe inside of a wpf / winforms application (with pictures)
如何将多个dll合并到主.exe文件中? (不使用第三方程序)
更新 3
.Net 内置了这个
https://docs.microsoft.com/en-us/dotnet/core/deploying/single-file
更新 2
您甚至根本不必将 dll 作为嵌入式资源包含在内,只需使用 Fody.Costura 它将解析引用的程序集和 auto-include 它们
此外,如果您使用的是 .net core 3+,则可以使用 Single file executable & Assembly linking 功能
更新
如果您想要一个简单的工具来合并程序集而不用担心做任何工作,那么 Fody.Costura 是您的最佳选择,因为您需要做的只是包含 dll 并更改它们为嵌入式资源构建操作,它会立即运行。
1 - 创建一个包含所有 Dll 的文件夹或根据需要将它们单独放置
2 - 单击“解决方案资源管理器”中的每个 DLL 并确保它们具有这些属性
构建操作 = 嵌入式资源
复制到输出目录 = 不复制
3 - 转到:项目 > 属性 > 引用,并确保您添加的每个 Dll 都与程序集同名,如下所示:
在参考文献中:-
在解决方案资源管理器中:-
注:-
It's Better to Make copy local = True in references as it will give
you an updated DLL each time you publish the project
此时您已将 DLL 添加到 EXE 中,剩下的就是告诉程序如何从 EXE 中读取这些 DLL(这就是我们制作构建操作 = 嵌入式资源的原因)
4 - 在解决方案资源管理器中打开您的 (Application.xaml.vb) 文件([App.xaml.cs] 在 c# 中)
或者
转到:项目 > 属性 > 应用程序 > 查看应用程序事件
现在,在此页面中,我们将处理应用程序的第一个事件(构建事件),以告诉程序如何处理我们在 loading/reading 它们之前添加为 DLL 的程序集,方法是使用 AssemblyResolve
事件
Check this MSDN page for more Info about the AssemblyResolve Event
https://msdn.microsoft.com/en-us/library/system.appdomain.assemblyresolve(v=vs.110).aspx
5 - 现在到代码部分:
首先导入这个命名空间
vb.net
Imports System.Reflection
c#
using System.Reflection;
在构造函数中(vb 中的 [Sub New])添加此代码
Vb.net
Public Sub New()
AddHandler AppDomain.CurrentDomain.AssemblyResolve, AddressOf OnResolveAssembly
End Sub
c#.net
public App()
{
AppDomain.CurrentDomain.AssemblyResolve += OnResolveAssembly;
}
然后添加OnResolveAssembly
函数
vb.net
''' <summary>
''' Tells the program that the Assembly it's Seeking is located in the Embedded resources By using the
''' <see cref="Assembly.GetManifestResourceNames"/> Function To get All the Resources
''' </summary>
''' <param name="sender"></param>
''' <param name="args"></param>
''' <returns></returns>
''' <remarks>Note that this event won't fire if the dll is in the same folder as the application (sometimes)</remarks>
Private Shared Function OnResolveAssembly(sender As Object, args As ResolveEventArgs) As Assembly
Try
'gets the main Assembly
Dim parentAssembly = Assembly.GetExecutingAssembly()
'args.Name will be something like this
'[ MahApps.Metro, Version=1.1.3.81, Culture=en-US, PublicKeyToken=null ]
'so we take the name of the Assembly (MahApps.Metro) then add (.dll) to it
Dim finalname = args.Name.Substring(0, args.Name.IndexOf(","c)) & ".dll"
'here we search the resources for our dll and get the first match
Dim ResourcesList = parentAssembly.GetManifestResourceNames()
Dim OurResourceName As String = Nothing
'(you can replace this with a LINQ extension like [Find] or [First])
For i As Integer = 0 To ResourcesList.Count - 1
Dim name = ResourcesList(i)
If name.EndsWith(finalname) Then
'Get the name then close the loop to get the first occuring value
OurResourceName = name
Exit For
End If
Next
If Not String.IsNullOrWhiteSpace(OurResourceName) Then
'get a stream representing our resource then load it as bytes
Using stream As Stream = parentAssembly.GetManifestResourceStream(OurResourceName)
'in vb.net use [ New Byte(stream.Length - 1) ]
'in c#.net use [ new byte[stream.Length]; ]
Dim block As Byte() = New Byte(stream.Length - 1) {}
stream.Read(block, 0, block.Length)
Return Assembly.Load(block)
End Using
Else
Return Nothing
End If
Catch ex As Exception
Return Nothing
End Try
End Function
c#.net
/// <summary>
/// Tells the program that the Assembly its Seeking is located in the Embedded resources By using the
/// <see cref="Assembly.GetManifestResourceNames"/> Function To get All the Resources
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
/// <returns></returns>
/// <remarks>Note that this event won't fire if the dll is in the same folder as the application (sometimes)</remarks>
private static Assembly OnResolveAssembly(object sender, ResolveEventArgs args)
{
try {
//gets the main Assembly
var parentAssembly = Assembly.GetExecutingAssembly();
//args.Name will be something like this
//[ MahApps.Metro, Version=1.1.3.81, Culture=en-US, PublicKeyToken=null ]
//so we take the name of the Assembly (MahApps.Metro) then add (.dll) to it
var finalname = args.Name.Substring(0, args.Name.IndexOf(',')) + ".dll";
//here we search the resources for our dll and get the first match
var ResourcesList = parentAssembly.GetManifestResourceNames();
string OurResourceName = null;
//(you can replace this with a LINQ extension like [Find] or [First])
for (int i = 0; i <= ResourcesList.Count - 1; i++) {
var name = ResourcesList(i);
if (name.EndsWith(finalname)) {
//Get the name then close the loop to get the first occuring value
OurResourceName = name;
break;
}
}
if (!string.IsNullOrWhiteSpace(OurResourceName)) {
//get a stream representing our resource then load it as bytes
using (Stream stream = parentAssembly.GetManifestResourceStream(OurResourceName)) {
//in vb.net use [ New Byte(stream.Length - 1) ]
//in c#.net use [ new byte[stream.Length]; ]
byte[] block = new byte[stream.Length];
stream.Read(block, 0, block.Length);
return Assembly.Load(block);
}
} else {
return null;
}
} catch (Exception ex) {
return null;
}
}
6 - 现在发布或构建应用程序,您的所有 dll 都将嵌入到单个 EXE 文件中(加载它们会有一些额外的毫秒延迟)
更新 DLL
1 - 只需将新的 dll 拖放到与旧 dll 相同的文件夹的解决方案资源管理器中,然后接受覆盖(确保检查 [Build Action = Embedded Resources] 和 [Copy To Output Directory =请勿复制])
添加新的 DLL
只需重复步骤 1 => 3
学分:
http://richarddingwall.name/2009/05/14/wpf-how-to-combine-mutliple-assemblies-into-a-single-exe/
*如有问题欢迎随时咨询
无法获得适合我的 bigworld12 答案,但我找到了这个 link,它对我来说非常有用。
基本上页面的要点是卸载您的项目并将以下代码添加到您的 csproj
文件
<Target Name="AfterResolveReferences">
<ItemGroup>
<EmbeddedResource Include="@(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.Extension)' == '.dll'">
<LogicalName>%(ReferenceCopyLocalPaths.DestinationSubDirectory)%(ReferenceCopyLocalPaths.Filename)%(ReferenceCopyLocalPaths.Extension)
</LogicalName>
</EmbeddedResource>
</ItemGroup>
</Target>
在这行代码下面。
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
完成后,重新加载项目并向其添加新的 class,然后将以下代码添加到新的 class。
[STAThread]
public static void Main()
{
AppDomain.CurrentDomain.AssemblyResolve += OnResolveAssembly;
App.Main(); // Run WPF startup code.
}
private static Assembly OnResolveAssembly(object sender, ResolveEventArgs e)
{
var thisAssembly = Assembly.GetExecutingAssembly();
// Get the Name of the AssemblyFile
var assemblyName = new AssemblyName(e.Name);
var dllName = assemblyName.Name + ".dll";
// Load from Embedded Resources - This function is not called if the Assembly is already
// in the same folder as the app.
var resources = thisAssembly.GetManifestResourceNames().Where(s => s.EndsWith(dllName));
if (resources.Any())
{
// 99% of cases will only have one matching item, but if you don't,
// you will have to change the logic to handle those cases.
var resourceName = resources.First();
using (var stream = thisAssembly.GetManifestResourceStream(resourceName))
{
if (stream == null) return null;
var block = new byte[stream.Length];
// Safely try to load the assembly.
try
{
stream.Read(block, 0, block.Length);
return Assembly.Load(block);
}
catch (IOException)
{
return null;
}
catch(BadImageFormatException)
{
return null;
}
}
}
// in the case the resource doesn't exist, return null.
return null;
}
保存您的 class,然后打开项目的属性菜单和 select 启动对象 combobox
和 select class 的名称您刚刚从列表中创建。
剩下的就是构建您的项目了。
如果您使用任何最新版本的 .NET Core 和 Visual Studio,您可以使用 Publish...
选项将所有依赖项包含在可执行文件中。
默认情况下,这仅包括来自您的解决方案的 DLL,而不包括本机的;要包含 运行 项目所需的 所有 DLL,您必须在 csproj
文件中添加 1 行:
<PropertyGroup>
<!--This is the section that contains <OutputType>, <TargetFramework>, etc.-->
<IncludeAllContentForSelfExtract>true</IncludeAllContentForSelfExtract>
</PropertyGroup>
如果您还没有为您的项目创建发布配置文件,请在解决方案资源管理器中 R+单击您的项目,然后单击 Publish...
:
(您可以使用发布中的文件夹选项window在没有Azure的情况下发布)
创建发布配置文件后,单击 Edit
并确保将部署模式设置为 Self-Contained
并选中 Produce Single File
框:
如何将多个dll合并到主.exe文件中? (不使用第三方程序)
更新 3
.Net 内置了这个 https://docs.microsoft.com/en-us/dotnet/core/deploying/single-file
更新 2
您甚至根本不必将 dll 作为嵌入式资源包含在内,只需使用 Fody.Costura 它将解析引用的程序集和 auto-include 它们
此外,如果您使用的是 .net core 3+,则可以使用 Single file executable & Assembly linking 功能
更新
如果您想要一个简单的工具来合并程序集而不用担心做任何工作,那么 Fody.Costura 是您的最佳选择,因为您需要做的只是包含 dll 并更改它们为嵌入式资源构建操作,它会立即运行。
1 - 创建一个包含所有 Dll 的文件夹或根据需要将它们单独放置
2 - 单击“解决方案资源管理器”中的每个 DLL 并确保它们具有这些属性
构建操作 = 嵌入式资源
复制到输出目录 = 不复制
3 - 转到:项目 > 属性 > 引用,并确保您添加的每个 Dll 都与程序集同名,如下所示:
在参考文献中:-
在解决方案资源管理器中:-
注:-
It's Better to Make copy local = True in references as it will give you an updated DLL each time you publish the project
此时您已将 DLL 添加到 EXE 中,剩下的就是告诉程序如何从 EXE 中读取这些 DLL(这就是我们制作构建操作 = 嵌入式资源的原因)
4 - 在解决方案资源管理器中打开您的 (Application.xaml.vb) 文件([App.xaml.cs] 在 c# 中)
或者
转到:项目 > 属性 > 应用程序 > 查看应用程序事件
现在,在此页面中,我们将处理应用程序的第一个事件(构建事件),以告诉程序如何处理我们在 loading/reading 它们之前添加为 DLL 的程序集,方法是使用 AssemblyResolve
事件
Check this MSDN page for more Info about the AssemblyResolve Event https://msdn.microsoft.com/en-us/library/system.appdomain.assemblyresolve(v=vs.110).aspx
5 - 现在到代码部分:
首先导入这个命名空间
vb.net
Imports System.Reflection
c#
using System.Reflection;
在构造函数中(vb 中的 [Sub New])添加此代码
Vb.net
Public Sub New()
AddHandler AppDomain.CurrentDomain.AssemblyResolve, AddressOf OnResolveAssembly
End Sub
c#.net
public App()
{
AppDomain.CurrentDomain.AssemblyResolve += OnResolveAssembly;
}
然后添加OnResolveAssembly
函数
vb.net
''' <summary>
''' Tells the program that the Assembly it's Seeking is located in the Embedded resources By using the
''' <see cref="Assembly.GetManifestResourceNames"/> Function To get All the Resources
''' </summary>
''' <param name="sender"></param>
''' <param name="args"></param>
''' <returns></returns>
''' <remarks>Note that this event won't fire if the dll is in the same folder as the application (sometimes)</remarks>
Private Shared Function OnResolveAssembly(sender As Object, args As ResolveEventArgs) As Assembly
Try
'gets the main Assembly
Dim parentAssembly = Assembly.GetExecutingAssembly()
'args.Name will be something like this
'[ MahApps.Metro, Version=1.1.3.81, Culture=en-US, PublicKeyToken=null ]
'so we take the name of the Assembly (MahApps.Metro) then add (.dll) to it
Dim finalname = args.Name.Substring(0, args.Name.IndexOf(","c)) & ".dll"
'here we search the resources for our dll and get the first match
Dim ResourcesList = parentAssembly.GetManifestResourceNames()
Dim OurResourceName As String = Nothing
'(you can replace this with a LINQ extension like [Find] or [First])
For i As Integer = 0 To ResourcesList.Count - 1
Dim name = ResourcesList(i)
If name.EndsWith(finalname) Then
'Get the name then close the loop to get the first occuring value
OurResourceName = name
Exit For
End If
Next
If Not String.IsNullOrWhiteSpace(OurResourceName) Then
'get a stream representing our resource then load it as bytes
Using stream As Stream = parentAssembly.GetManifestResourceStream(OurResourceName)
'in vb.net use [ New Byte(stream.Length - 1) ]
'in c#.net use [ new byte[stream.Length]; ]
Dim block As Byte() = New Byte(stream.Length - 1) {}
stream.Read(block, 0, block.Length)
Return Assembly.Load(block)
End Using
Else
Return Nothing
End If
Catch ex As Exception
Return Nothing
End Try
End Function
c#.net
/// <summary>
/// Tells the program that the Assembly its Seeking is located in the Embedded resources By using the
/// <see cref="Assembly.GetManifestResourceNames"/> Function To get All the Resources
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
/// <returns></returns>
/// <remarks>Note that this event won't fire if the dll is in the same folder as the application (sometimes)</remarks>
private static Assembly OnResolveAssembly(object sender, ResolveEventArgs args)
{
try {
//gets the main Assembly
var parentAssembly = Assembly.GetExecutingAssembly();
//args.Name will be something like this
//[ MahApps.Metro, Version=1.1.3.81, Culture=en-US, PublicKeyToken=null ]
//so we take the name of the Assembly (MahApps.Metro) then add (.dll) to it
var finalname = args.Name.Substring(0, args.Name.IndexOf(',')) + ".dll";
//here we search the resources for our dll and get the first match
var ResourcesList = parentAssembly.GetManifestResourceNames();
string OurResourceName = null;
//(you can replace this with a LINQ extension like [Find] or [First])
for (int i = 0; i <= ResourcesList.Count - 1; i++) {
var name = ResourcesList(i);
if (name.EndsWith(finalname)) {
//Get the name then close the loop to get the first occuring value
OurResourceName = name;
break;
}
}
if (!string.IsNullOrWhiteSpace(OurResourceName)) {
//get a stream representing our resource then load it as bytes
using (Stream stream = parentAssembly.GetManifestResourceStream(OurResourceName)) {
//in vb.net use [ New Byte(stream.Length - 1) ]
//in c#.net use [ new byte[stream.Length]; ]
byte[] block = new byte[stream.Length];
stream.Read(block, 0, block.Length);
return Assembly.Load(block);
}
} else {
return null;
}
} catch (Exception ex) {
return null;
}
}
6 - 现在发布或构建应用程序,您的所有 dll 都将嵌入到单个 EXE 文件中(加载它们会有一些额外的毫秒延迟)
更新 DLL
1 - 只需将新的 dll 拖放到与旧 dll 相同的文件夹的解决方案资源管理器中,然后接受覆盖(确保检查 [Build Action = Embedded Resources] 和 [Copy To Output Directory =请勿复制])
添加新的 DLL
只需重复步骤 1 => 3
学分:
http://richarddingwall.name/2009/05/14/wpf-how-to-combine-mutliple-assemblies-into-a-single-exe/
*如有问题欢迎随时咨询
无法获得适合我的 bigworld12 答案,但我找到了这个 link,它对我来说非常有用。
基本上页面的要点是卸载您的项目并将以下代码添加到您的 csproj
文件
<Target Name="AfterResolveReferences">
<ItemGroup>
<EmbeddedResource Include="@(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.Extension)' == '.dll'">
<LogicalName>%(ReferenceCopyLocalPaths.DestinationSubDirectory)%(ReferenceCopyLocalPaths.Filename)%(ReferenceCopyLocalPaths.Extension)
</LogicalName>
</EmbeddedResource>
</ItemGroup>
</Target>
在这行代码下面。
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
完成后,重新加载项目并向其添加新的 class,然后将以下代码添加到新的 class。
[STAThread]
public static void Main()
{
AppDomain.CurrentDomain.AssemblyResolve += OnResolveAssembly;
App.Main(); // Run WPF startup code.
}
private static Assembly OnResolveAssembly(object sender, ResolveEventArgs e)
{
var thisAssembly = Assembly.GetExecutingAssembly();
// Get the Name of the AssemblyFile
var assemblyName = new AssemblyName(e.Name);
var dllName = assemblyName.Name + ".dll";
// Load from Embedded Resources - This function is not called if the Assembly is already
// in the same folder as the app.
var resources = thisAssembly.GetManifestResourceNames().Where(s => s.EndsWith(dllName));
if (resources.Any())
{
// 99% of cases will only have one matching item, but if you don't,
// you will have to change the logic to handle those cases.
var resourceName = resources.First();
using (var stream = thisAssembly.GetManifestResourceStream(resourceName))
{
if (stream == null) return null;
var block = new byte[stream.Length];
// Safely try to load the assembly.
try
{
stream.Read(block, 0, block.Length);
return Assembly.Load(block);
}
catch (IOException)
{
return null;
}
catch(BadImageFormatException)
{
return null;
}
}
}
// in the case the resource doesn't exist, return null.
return null;
}
保存您的 class,然后打开项目的属性菜单和 select 启动对象 combobox
和 select class 的名称您刚刚从列表中创建。
剩下的就是构建您的项目了。
如果您使用任何最新版本的 .NET Core 和 Visual Studio,您可以使用 Publish...
选项将所有依赖项包含在可执行文件中。
默认情况下,这仅包括来自您的解决方案的 DLL,而不包括本机的;要包含 运行 项目所需的 所有 DLL,您必须在 csproj
文件中添加 1 行:
<PropertyGroup>
<!--This is the section that contains <OutputType>, <TargetFramework>, etc.-->
<IncludeAllContentForSelfExtract>true</IncludeAllContentForSelfExtract>
</PropertyGroup>
如果您还没有为您的项目创建发布配置文件,请在解决方案资源管理器中 R+单击您的项目,然后单击 Publish...
:
(您可以使用发布中的文件夹选项window在没有Azure的情况下发布)
创建发布配置文件后,单击 Edit
并确保将部署模式设置为 Self-Contained
并选中 Produce Single File
框: