簡體   English   中英

如何在 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 ,它就會解析引用的程序集並自動包含它們

此外,如果您使用的是 .net core 3+,您可以使用單文件可執行文件和程序集鏈接功能

更新

如果您想要一個簡單的工具來合並程序集而不必擔心做任何工作,那么Fody.Costura是您擁有的最佳選擇,因為您只需包含 dll 並將其構建操作更改為嵌入式資源即可將立即工作。


1 - 創建一個包含所有 Dll 的文件夾或根據需要單獨放置它們添加 Dll

2 - 單擊“解決方案資源管理器”中的每個 DLL,並確保它們具有這些屬性

  • 構建操作 = 嵌入式資源

  • 復制到輸出目錄 = 不復制

改變他們的建造行動

3-轉到:項目>屬性>參考,並確保您添加的每個Dll都與程序集具有相同的名稱,如下所示:

在參考文獻中:-

參考名稱 = Dll 名稱

在解決方案資源管理器中:-

參考名稱 = 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 文件中(加載它們有一些額外的毫秒延遲)

更新 DLL

1 - 只需將新 dll 拖放到與舊 dll 相同的文件夾的解決方案資源管理器中,然后接受覆蓋(確保檢查 [Build Action = Embedded Resources] 和 [Copy To Output Directory = Do not copy])

改變他們的建造行動

添加新的 DLL

只需重復步驟 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框:

3

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM