如何在 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 框: