C# 使用 wpf 将 dll 合并为单个 .exe

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1025843/
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-08-06 05:49:07  来源:igfitidea点击:

Merging dlls into a single .exe with wpf

c#wpfdllilmerge

提问by Farawin

I'm currently working on a project where we have a lot of dependencies. I would like to compile all the referenced dll's into the .exe much like you would do with embedded resources. I have tried ILMergebut it can't handle .xaml resources.

我目前正在处理一个我们有很多依赖项的项目。我想将所有引用的 dll 编译到 .exe 中,就像您对嵌入式资源所做的一样。我尝试过ILMerge,但它无法处理 .xaml 资源。

So my question is: Is there a way to merge a WPF project with multiple dependencies into a single .exe?

所以我的问题是:有没有办法将具有多个依赖项的 WPF 项目合并到单个 .exe 中?

采纳答案by Hemant

.NET reactorhas the feature of merging the assemblies and its not very expensive.

.NET reactor具有合并程序集的功能,而且成本不是很高。

回答by Timotei

{smartassembly} is one such product. It can obsfucate or embedd your dlls.

{smartassembly} 就是这样一种产品。它可以混淆或嵌入您的 dll。

Try this: http://www.smartassembly.com/

试试这个:http: //www.smartassembly.com/

You can also do a lot of improvements on your application so it will run faster.

您还可以对应用程序进行大量改进,使其运行得更快。

And yes. You can use it for WPF.

是的。您可以将它用于 WPF。

Update 8/06/2015: ILRepack 2.0.0 (which is an open-source alternative to ILMerge) has now support for most of WPF cases merging: https://twitter.com/Gluckies/status/607680149157462016

2015 年 6 月 8 日更新:ILRepack 2.0.0(它是 ILMerge 的开源替代品)现在支持大多数 WPF 案例合并:https://twitter.com/Gluckies/status/607680149157462016

回答by Wegged

http://www.digitallycreated.net/Blog/61/combining-multiple-assemblies-into-a-single-exe-for-a-wpf-application

http://www.digitallycreated.net/Blog/61/combining-multiple-assemblies-into-a-single-exe-for-a-wpf-application

This worked like a charm for me :) and its completely free.

这对我来说就像一个魅力:)而且它完全免费。

Adding code in case the blog ever disappears.

添加代码以防博客消失。

1) Add this to your .csprojfile:

1) 将此添加到您的.csproj文件中:

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

2) Make your Main Program.cslook like this:

2)让你的主Program.cs看起来像这样:

[STAThreadAttribute]
public static void Main()
{
    AppDomain.CurrentDomain.AssemblyResolve += OnResolveAssembly;
    App.Main();
}

3) Add the OnResolveAssemblymethod:

3)添加OnResolveAssembly方法:

private static Assembly OnResolveAssembly(object sender, ResolveEventArgs args)
{
    Assembly executingAssembly = Assembly.GetExecutingAssembly();
    AssemblyName assemblyName = new AssemblyName(args.Name);

    var path = assemblyName.Name + ".dll";
    if (assemblyName.CultureInfo.Equals(CultureInfo.InvariantCulture) == false) path = String.Format(@"{0}\{1}", assemblyName.CultureInfo, path);

    using (Stream stream = executingAssembly.GetManifestResourceStream(path))
    {
        if (stream == null) return null;

        var assemblyRawBytes = new byte[stream.Length];
        stream.Read(assemblyRawBytes, 0, assemblyRawBytes.Length);
        return Assembly.Load(assemblyRawBytes);
    }
}

回答by Nils

Try .Netz ( http://madebits.com/netz/) - it's free (as in beer) and does some nice things if you're target is an exe.

试试 .Netz ( http://madebits.com/netz/) - 它是免费的(就像啤酒一样),如果你的目标是一个 exe,它会做一些不错的事情。

回答by Matthieu

As posted on ILMerge website, treat those dlls as ressources, from Jeffrey Richter here:

正如ILMerge 网站发布的那样,将这些 dll 视为资源,来自 Jeffrey Richter在这里

Many applications consist of an EXE file that depends on many DLL files. When deploying this application, all the files must be deployed. However, there is a technique that you can use to deploy just a single EXE file. First, identify all the DLL files that your EXE file depends on that do not ship as part of the Microsoft .NET Framework itself. Then add these DLLs to your Visual Studio project. For each DLL file you add, display its properties and change its “Build Action” to “Embedded Resource.” This causes the C# compiler to embed the DLL file(s) into your EXE file, and you can deploy this one EXE file. At runtime, the CLR won't be able to find the dependent DLL assemblies, which is a problem. To fix this, when your application initializes, register a callback method with the AppDomain's ResolveAssembly event. The code should look something like this:

许多应用程序由依赖于许多 DLL 文件的 EXE 文件组成。部署此应用程序时,必须部署所有文件。但是,有一种技术可用于仅部署单个 EXE 文件。首先,确定您的 EXE 文件所依赖的所有 DLL 文件,这些文件不作为 Microsoft .NET Framework 本身的一部分提供。然后将这些 DLL 添加到您的 Visual Studio 项目中。对于您添加的每个 DLL 文件,显示其属性并将其“构建操作”更改为“嵌入式资源”。这会导致 C# 编译器将 DLL 文件嵌入到您的 EXE 文件中,您可以部署这个 EXE 文件。在运行时,CLR 将无法找到依赖的 DLL 程序集,这是一个问题。要解决此问题,请在应用程序初始化时使用 AppDomain 的 ResolveAssembly 事件注册回调方法。

AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => {

   String resourceName = "AssemblyLoadingAndReflection." +

      new AssemblyName(args.Name).Name + ".dll";

   using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName)) {

      Byte[] assemblyData = new Byte[stream.Length];

      stream.Read(assemblyData, 0, assemblyData.Length);

      return Assembly.Load(assemblyData);

   }

}; 

Now, the first time a thread calls a method that references a type in a dependent DLL file, the AssemblyResolve event will be raised and the callback code shown above will find the embedded DLL resource desired and load it by calling an overload of Assembly's Load method that takes a Byte[] as an argument.

现在,线程第一次调用引用依赖 DLL 文件中的类型的方法时,将引发 AssemblyResolve 事件,上面显示的回调代码将找到所需的嵌入 DLL 资源并通过调用 Assembly 的 Load 方法的重载来加载它以 Byte[] 作为参数。

回答by j2associates

Here is a tweaked version of the quoted code from Matthieu that doesn't require knowing the namespace to extract the code. For WPF, put this in the application startup event code.

这是来自 Matthieu 的引用代码的调整版本,它不需要知道命名空间来提取代码。对于 WPF,请将其放在应用程序启动事件代码中。

AppDomain.CurrentDomain.AssemblyResolve += (s, args) =>
{
    // Note: Requires a using statement for System.Reflection and System.Diagnostics.
    Assembly assembly = Assembly.GetExecutingAssembly();
    List<string> embeddedResources = new List<string>(assembly.GetManifestResourceNames());
    string assemblyName = new AssemblyName(args.Name).Name;
    string fileName = string.Format("{0}.dll", assemblyName);
    string resourceName = embeddedResources.Where(ern => ern.EndsWith(fileName)).FirstOrDefault();
    if (!string.IsNullOrWhiteSpace(resourceName))
    {
        using (var stream = assembly.GetManifestResourceStream(resourceName))
        {
            Byte[] assemblyData = new Byte[stream.Length];
            stream.Read(assemblyData, 0, assemblyData.Length);
            var test = Assembly.Load(assemblyData);
            string namespace_ = test.GetTypes().Where(t => t.Name == assemblyName).Select(t => t.Namespace).FirstOrDefault();
#if DEBUG
            Debug.WriteLine(string.Format("\tNamespace for '{0}' is '{1}'", fileName, namespace_));
#endif
            return Assembly.Load(assemblyData);
        }
    }

    return null;
}; 

To make them available at compile time, I create a folder named ExternalDLLs and copy the dlls there and set them to EmbeddedResource as noted above. To use them in your code, you still need to set a reference to them, but set Copy local to False. To get the code to compile cleanly without errors you also need to set using statments in your code to the namespaces of the dlls.

为了使它们在编译时可用,我创建了一个名为 ExternalDLLs 的文件夹并将这些 dll 复制到那里并将它们设置为 EmbeddedResource,如上所述。要在您的代码中使用它们,您仍然需要设置对它们的引用,但将 Copy local 设置为 False。为了让代码干净利落地编译,您还需要在代码中将 using 语句设置为 dll 的命名空间。

Here is a little utility that spins through the embedded resource names and displays their namespaces in the output window.

这是一个小实用程序,它遍历嵌入的资源名称并在输出窗口中显示它们的命名空间。

private void getEmbeddedResourceNamespaces()
{
    // Note: Requires a using statement for System.Reflection and System.Diagnostics.
    Assembly assembly = Assembly.GetExecutingAssembly();
    List<string> embeddedResourceNames = new List<string>(assembly.GetManifestResourceNames());
    foreach (string resourceName in embeddedResourceNames)
    {
        using (var stream = assembly.GetManifestResourceStream(resourceName))
        {
            Byte[] assemblyData = new Byte[stream.Length];
            stream.Read(assemblyData, 0, assemblyData.Length);
            try
            {
                var test = Assembly.Load(assemblyData);
                foreach (Type type in test.GetTypes())
                {
                    Debug.WriteLine(string.Format("\tNamespace for '{0}' is '{1}'", type.Name, type.Namespace));
                }
            }
            catch 
            {
            }
        }
    }
}

回答by Tone ?koda

  1. add this to .csprofj file:
  1. 将此添加到 .csprofj 文件:

>

>

<Target Name="AfterResolveReferences">
  <ItemGroup>
    <EmbeddedResource Include="@(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.Extension)' == '.dll'">
      <LogicalName>%(ReferenceCopyLocalPaths.DestinationSubDirectory)%(ReferenceCopyLocalPaths.Filename)%(ReferenceCopyLocalPaths.Extension)</LogicalName>
    </EmbeddedResource>
  </ItemGroup>
</Target>
  1. right click project/ properties/application/starup object / select Sinhro.Program

  2. add this to your program.cs file:

    using System.Reflection; using System.IO; using System.Globalization;

    [STAThreadAttribute]
    static void Main()
    {
        AppDomain.CurrentDomain.AssemblyResolve += OnResolveAssembly;
        ...
    
    
    private static Assembly OnResolveAssembly(object sender, ResolveEventArgs args)
    {
        Assembly executingAssembly = Assembly.GetExecutingAssembly();
        AssemblyName assemblyName = new AssemblyName(args.Name);
        string path = assemblyName.Name + ".dll";
        if (assemblyName.CultureInfo.Equals(CultureInfo.InvariantCulture) == false)
        {
            path = String.Format(@"{0}\{1}", assemblyName.CultureInfo, path);
        }
        using (Stream stream = executingAssembly.GetManifestResourceStream(path))
        {
            if (stream == null)
                return null;
            byte[] assemblyRawBytes = new byte[stream.Length];
            stream.Read(assemblyRawBytes, 0, assemblyRawBytes.Length);
            return Assembly.Load(assemblyRawBytes);
        }
    }   
    
  1. 右键单击项目/属性/应用程序/starup 对象/选择 Sinhro.Program

  2. 将此添加到您的 program.cs 文件中:

    使用 System.Reflection; 使用 System.IO;使用 System.Globalization;

    [STAThreadAttribute]
    static void Main()
    {
        AppDomain.CurrentDomain.AssemblyResolve += OnResolveAssembly;
        ...
    
    
    private static Assembly OnResolveAssembly(object sender, ResolveEventArgs args)
    {
        Assembly executingAssembly = Assembly.GetExecutingAssembly();
        AssemblyName assemblyName = new AssemblyName(args.Name);
        string path = assemblyName.Name + ".dll";
        if (assemblyName.CultureInfo.Equals(CultureInfo.InvariantCulture) == false)
        {
            path = String.Format(@"{0}\{1}", assemblyName.CultureInfo, path);
        }
        using (Stream stream = executingAssembly.GetManifestResourceStream(path))
        {
            if (stream == null)
                return null;
            byte[] assemblyRawBytes = new byte[stream.Length];
            stream.Read(assemblyRawBytes, 0, assemblyRawBytes.Length);
            return Assembly.Load(assemblyRawBytes);
        }
    }   
    

source: http://www.digitallycreated.net/Blog/61/combining-multiple-assemblies-into-a-single-exe-for-a-wpf-application

来源:http: //www.digitallycreated.net/Blog/61/combining-multiple-assemblies-into-a-single-exe-for-a-wpf-application

回答by Dark Knight

Use Costura.Fody- It is available as Nuget Pkg for best and easiest way to embed resources in your assembly.

使用Costura.Fody- 它作为 Nuget Pkg 提供,是在程序集中嵌入资源的最佳和最简单的方法。

Install-Package Costura.Fody

After adding it to the project, it will automatically embed all added references to your main assembly.

将它添加到项目后,它会自动将所有添加的引用嵌入到您的主程序集。

回答by James Igoe

Since all the other solutions are in C#, and I needed this for VB.NET, this includes clarification about where to insert the configuration change, the necessary imports, and the way to add a handler, instead of C#'s += syntax.

由于所有其他解决方案都在 C# 中,而我在 VB.NET 中需要它,这包括说明在何处插入配置更改、必要的导入以及添加处理程序的方式,而不是 C# 的 += 语法。

For any WPF application, not each project, the following needs to be added to make the code compile to a single EXE. It will still include the DLL's in the output folder, but the EXE will contain all of them.

对于任何 WPF 应用程序,而不是每个项目,都需要添加以下内容以使代码编译为单个 EXE。它仍将在输出文件夹中包含 DLL,但 EXE 将包含所有这些。

  1. Unload the WPF project (usually the view)
  2. Right-click the project and edit it
  3. In the document, paste the following code after this line
  1. 卸载 WPF 项目(通常是视图)
  2. 右键单击项目并进行编辑
  3. 在文档中,将以下代码粘贴到这一行之后
<Import Project="$(MSBuildToolsPath)\Microsoft.VisualBasic.targets" />

Code to paste

要粘贴的代码

<Target Name="AfterResolveReferences">
   <ItemGroup>
      <EmbeddedResource Include="@(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.Extension)' == '.dll'">
         <LogicalName>%(ReferenceCopyLocalPaths.DestinationSubDirectory)%(ReferenceCopyLocalPaths.Filename)%(ReferenceCopyLocalPaths.Extension)
         </LogicalName>
      </EmbeddedResource>
   </ItemGroup>
</Target>
  1. Close it, save it, and then reload the project
  2. In the Application.xaml.vb file add the following code, or if something already exists in the file, add this to it:
  1. 关闭它,保存它,然后重新加载项目
  2. 在 Application.xaml.vb 文件中添加以下代码,或者如果文件中已存在某些内容,请将其添加到其中:
Imports System.Reflection
Imports System.Globalization
Imports System.IO

Class Application

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

    Private Shared Function OnResolveAssembly(ByVal sender As Object, ByVal args As ResolveEventArgs) As Assembly

        Dim executingAssembly As Assembly = Assembly.GetExecutingAssembly()
        Dim assemblyName As AssemblyName = New AssemblyName(args.Name)
        Dim path = assemblyName.Name & ".dll"
        If assemblyName.CultureInfo.Equals(CultureInfo.InvariantCulture) = False Then path = String.Format("{0}\{1}", assemblyName.CultureInfo, path)

        Using stream As Stream = executingAssembly.GetManifestResourceStream(path)
            If stream Is Nothing Then Return Nothing
            Dim assemblyRawBytes = New Byte(stream.Length - 1) {}
            stream.Read(assemblyRawBytes, 0, assemblyRawBytes.Length)
            Return Assembly.Load(assemblyRawBytes)
        End Using

    End Function

End Class