[英]How to combine DLLs with .exe inside of a wpf / winforms application (with pictures)
如何將多個 dll 組合到主 .exe 文件中? (不使用第三方程序)
.Net 內置了https://docs.microsoft.com/en-us/dotnet/core/deploying/single-file
您甚至根本不必將 dll 作為嵌入式資源包含在內,只需使用Fody.Costura ,它就會解析引用的程序集並自動包含它們
此外,如果您使用的是 .net core 3+,您可以使用單文件可執行文件和程序集鏈接功能
如果您想要一個簡單的工具來合並程序集而不必擔心做任何工作,那么Fody.Costura是您擁有的最佳選擇,因為您只需包含 dll 並將其構建操作更改為嵌入式資源即可將立即工作。
1 - 創建一個包含所有 Dll 的文件夾或根據需要單獨放置它們
2 - 單擊“解決方案資源管理器”中的每個 DLL,並確保它們具有這些屬性
構建操作 = 嵌入式資源
復制到輸出目錄 = 不復制
3-轉到:項目>屬性>參考,並確保您添加的每個Dll都與程序集具有相同的名稱,如下所示:
在參考文獻中:-
在解決方案資源管理器中:-
最好在引用中復制 local = True,因為每次發布項目時它都會為您提供更新的 DLL
現在,您已將 DLL 添加到 EXE 中,現在剩下的就是告訴程序如何從 EXE 中讀取這些 DLL(這就是我們制作構建操作 = 嵌入式資源的原因)
4 - 在解決方案資源管理器中打開您的 (Application.xaml.vb) 文件(C# 中的 [App.xaml.cs])
或者
轉到:項目>屬性>應用程序>查看應用程序事件
現在在這個頁面中,我們將處理應用程序的第一個事件(構造事件),以告訴程序如何在使用AssemblyResolve
事件加載/讀取它們之前處理我們作為 DLL 添加的程序集
檢查此 MSDN 頁面以獲取有關 AssemblyResolve 事件的更多信息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 文件中(加載它們有一些額外的毫秒延遲)
1 - 只需將新 dll 拖放到與舊 dll 相同的文件夾的解決方案資源管理器中,然后接受覆蓋(確保檢查 [Build Action = Embedded Resources] 和 [Copy To Output Directory = Do not copy])
只需重復步驟 1 => 3
學分:
http://richarddingwall.name/2009/05/14/wpf-how-to-combine-mutliple-assemblies-into-a-single-exe/
*如有任何問題,請隨時詢問
無法讓 bigworld12 回答為我工作,但我找到了這個鏈接,它對我來說非常有效。
基本上該頁面的要點是卸載您的項目並將以下代碼添加到您的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" />
完成后,重新加載項目並為其添加一個新類,並將以下代碼添加到您的新類中。
[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;
}
保存您的類,然后打開項目的屬性菜單並選擇啟動對象combobox
,然后從列表中選擇您剛剛創建的類的名稱。
剩下的就是構建您的項目。
如果您使用的是任何最新版本的 .NET Core 和 Visual Studio,則可以使用Publish...
選項將所有依賴項包含在可執行文件中。
默認情況下,這僅包括您的解決方案中的 DLL,而不包括本機的; 要包含運行項目所需的所有DLL,您必須在您的csproj
文件中添加 1 行:
<PropertyGroup>
<!--This is the section that contains <OutputType>, <TargetFramework>, etc.-->
<IncludeAllContentForSelfExtract>true</IncludeAllContentForSelfExtract>
</PropertyGroup>
如果您還沒有為您的項目創建發布配置文件,請在解決方案資源管理器中單擊您的項目,然后單擊Publish...
:
(您可以使用發布窗口中的文件夾選項在沒有 Azure 的情況下發布)
創建發布配置文件后,單擊Edit
並確保將部署模式設置為Self-Contained
並選中Produce Single File
框:
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.