如何在 wpf/winforms 应用程序中将 DLL 与 .exe 结合(带图片)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/27891550/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 12:50:24  来源:igfitidea点击:

How to combine DLLs with .exe inside of a wpf / winforms application (with pictures)

c#wpfvb.netdll

提问by bigworld12

How to combine multiple dlls into the main .exe file? (without using third-party programs)

如何将多个 dll 合并到主 .exe 文件中?(不使用第三方程序)

回答by bigworld12

Update 2

更新 2

you don't even have to include the dlls as embedded resources at all, just use Fody.Costuraand it will resolve the referenced assemblies and auto-include them

您甚至根本不必将 dll 作为嵌入资源包含在内,只需使用Fody.Costura它将解析引用的程序集并自动包含它们

Also if you are using .net core 3+ you can use the Single file executable & Assembly linkingfeatures

此外,如果您使用 .net core 3+,您可以使用单文件可执行文件和程序集链接功能

Update

更新

if you want an easy tool to merge the assemblies without worrying about doing ANY work at all then Fody.Costurais the best choice you have, as all you need to do is just include the dlls and change their Build Action to Embedded Resource and it will work right away.

如果你想要一个简单的工具来合并程序集而不用担心做任何工作,那么Fody.Costura是你最好的选择,因为你需要做的只是包含 dll 并将它们的构建操作更改为嵌入式资源,它将立即工作。



1 - make a folder that contains all of the Dlls or place them separately as you like Adding Dlls

1 - 创建一个包含所有 Dll 的文件夹或根据需要单独放置它们 添加 Dll

2 - Click on each DLL from the "Solution Explorer" and make sure they have these properties

2 - 单击“解决方案资源管理器”中的每个 DLL 并确保它们具有这些属性

  • Build Action = Embedded Resources

  • Copy To Output Directory = Do not copy

  • 构建操作 = 嵌入式资源

  • 复制到输出目录 = 不要复制

change their Build Action

更改他们的构建操作

3 - Go to : Project > Properties > References , And make sure every Dll you add has the same name as the assembly like this :

3 - 转到:项目 > 属性 > 引用,并确保您添加的每个 Dll 与程序集具有相同的名称,如下所示:

In References :-

在参考文献中:-

Reference name = Dll name

引用名称 = Dll 名称

In Solution Explorer :-

在解决方案资源管理器中:-

Reference name = Dll name

引用名称 = Dll 名称

Note :-

笔记 :-

It's Better to Make copy local = True in references as it will give you an updated DLL each time you publish the project

最好在引用中复制 local = True 因为它会在您每次发布项目时为您提供更新的 DLL

Now at this point you have your DLLs added to the EXE , all is remaining now is to tell the program how to read these DLLs from the EXE (that's why we made build action = Embedded resources)

现在,您已将 DLL 添加到 EXE 中,现在剩下的就是告诉程序如何从 EXE 中读取这些 DLL(这就是我们进行构建操作 = 嵌入式资源的原因)

4 - In Solution Explorer Open your (Application.xaml.vb) file ([App.xaml.cs] in c#)
OR
Go To : Project > Properties > Application > View Application Events
Application events

4 - 在解决方案资源管理器中打开您的 (Application.xaml.vb) 文件(c# 中的 [App.xaml.cs])

转到:项目 > 属性 > 应用程序 > 查看应用程序事件
应用事件

Now in this page we are going to handle the very first event of the application (Construction event) to tell the program how to handle the assemblies we add as DLLs before loading/reading them by using the AssemblyResolveEvent

现在在这个页面中,我们将处理应用程序的第一个事件(构造事件),以告诉程序如何在使用AssemblyResolve事件 加载/读取它们之前处理我们添加为 DLL 的程序集

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

检查此 MSDN 页面以获取有关 AssemblyResolve 事件的更多信息 https://msdn.microsoft.com/en-us/library/system.appdomain.assemblyresolve(v=vs.110).aspx

5 - Now to The code part :
first of all Import this namespace

5 - 现在到代码部分:
首先导入这个命名空间

vb.net

网络

Imports System.Reflection  

c#

C#

using System.Reflection;

In the Constructor ([Sub New] in vb) add this code

在构造函数(vb 中的[Sub New])中添加此代码

Vb.net

网络

 Public Sub New()
        AddHandler AppDomain.CurrentDomain.AssemblyResolve, AddressOf OnResolveAssembly           
 End Sub

c#.net

c#.net

public App()
{
    AppDomain.CurrentDomain.AssemblyResolve += OnResolveAssembly;       
}

Then add the OnResolveAssemblyFunction
vb.net

然后添加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

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 - now publish the application or build it and all your dlls will be embedded in a single EXE file (with some extra milliseconds delay to load them)

6 - 现在发布应用程序或构建它,您的所有 dll 都将嵌入到单个 EXE 文件中(加载它们有一些额外的毫秒延迟)

To Update the DLLs

更新 DLL

1 - Simply drag and drop your new dll to the Solution Explorer as the same folder as the old dll then accept the override (make sure to check that [Build Action = Embedded Resources] AND [Copy To Output Directory = Do not copy])

1 - 只需将您的新 dll 拖放到与旧 dll 相同的文件夹中,然后接受覆盖(确保检查 [Build Action = Embedded Resources] AND [Copy To Output Directory = Do not copy])

change their Build Action

更改他们的构建操作

To Add New DLLs

添加新的 DLL

just repeat step 1 => 3

只需重复步骤 1 => 3

Credits :

学分:

http://richarddingwall.name/2009/05/14/wpf-how-to-combine-mutliple-assemblies-into-a-single-exe/

http://richarddingwall.name/2009/05/14/wpf-how-to-combine-mutliple-assemblies-into-a-single-exe/

*Feel free to ask if you had any problem

*如果您有任何问题,请随时询问

回答by noobCoder

Wasn't able to get bigworld12 answer to work for me, but I found this linkand it worked perfectly for me.

无法让 bigworld12 回答对我来说有效,但我找到了这个链接,它对我来说非常有效。

Basically the gist of the page is to unload your project and add the following code to your csprojfile

基本上页面的要点是卸载您的项目并将以下代码添加到您的csproj文件中

<Target Name="AfterResolveReferences">
    <ItemGroup>
        <EmbeddedResource Include="@(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.Extension)' == '.dll'">
            <LogicalName>%(ReferenceCopyLocalPaths.DestinationSubDirectory)%(ReferenceCopyLocalPaths.Filename)%(ReferenceCopyLocalPaths.Extension)   
            </LogicalName>
        </EmbeddedResource>
   </ItemGroup>
</Target>

Underneath this line of code.

在这行代码下面。

<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

Once that is done, reload the project and add a new class to it, and add the following code to your new 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;
}

Save your class, and open the properties menu of your project and select the startup object comboboxand select the name of the class you just created from the list.

保存您的类,然后打开项目的属性菜单并选择启动对象combobox并从列表中选择您刚刚创建的类的名称。

All that's left is to build your project.

剩下的就是构建您的项目。