C# 使用wpf将DLL合并到单个.exe中

C# 使用wpf将DLL合并到单个.exe中,c#,wpf,dll,ilmerge,C#,Wpf,Dll,Ilmerge,我目前正在从事一个我们有很多依赖关系的项目。我希望将所有引用的dll编译成.exe,就像您使用嵌入式资源所做的那样。我已经尝试过了,但它无法处理.xaml资源 因此,我的问题是:有没有一种方法可以将具有多个依赖项的WPF项目合并到一个.exe中?具有合并程序集的功能,而且成本不高。{smartassembly}就是这样一种产品。它可能会损坏或嵌入您的DLL 试试这个: 您还可以对应用程序进行大量改进,使其运行更快 是的。您可以将其用于WPF 更新日期:2015年6月8日: ILRepack2.0

我目前正在从事一个我们有很多依赖关系的项目。我希望将所有引用的dll编译成.exe,就像您使用嵌入式资源所做的那样。我已经尝试过了,但它无法处理.xaml资源


因此,我的问题是:有没有一种方法可以将具有多个依赖项的WPF项目合并到一个.exe中?

具有合并程序集的功能,而且成本不高。

{smartassembly}就是这样一种产品。它可能会损坏或嵌入您的DLL

试试这个:

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

是的。您可以将其用于WPF

更新日期:2015年6月8日: ILRepack2.0.0(它是ILMerge的开源替代品)现在支持大多数WPF案例合并:

这对我来说很有魅力:)而且完全免费

添加代码以防博客消失

1) 将此添加到您的
.csproj
文件: 3) 添加
OnResolveAssembly
方法: Try.Netz()-它是免费的(就像在啤酒中一样),如果您的目标是一个exe,它会做一些好事。

如上所述,将这些DLL视为资源,来自Jeffrey Richter:

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

现在,线程第一次调用引用中类型的方法 如果是依赖DLL文件,则将引发AssemblyResolve事件,并且 上面显示的回调代码将找到所需的嵌入式DLL资源 并通过调用程序集的load方法重载 将字节[]作为参数


这里是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;
}; 
AppDomain.CurrentDomain.AssemblyResolve+=(s,args)=>
{
//注意:需要System.Reflection和System.Diagnostics的using语句。
Assembly=Assembly.getExecutionGassembly();
List embeddedResources=新列表(assembly.GetManifestResourceNames());
字符串assemblyName=新的assemblyName(args.Name).Name;
字符串文件名=string.Format(“{0}.dll”,assemblyName);
字符串resourceName=embeddedResources.Where(ern=>ern.EndsWith(fileName)).FirstOrDefault();
如果(!string.IsNullOrWhiteSpace(resourceName))
{
使用(var stream=assembly.GetManifestResourceStream(resourceName))
{
Byte[]assemblyData=新字节[stream.Length];
读取(assemblyData,0,assemblyData.Length);
var测试=组装。负载(组装数据);
字符串名称空间=test.GetTypes()。其中(t=>t.Name==assemblyName)。选择(t=>t.namespace)。FirstOrDefault();
#如果调试
Debug.WriteLine(string.Format(“{0}”的名称空间为“{1}”,文件名,名称空间)));
#恩迪夫
返回Assembly.Load(assemblyData);
}
}
返回null;
}; 
为了使它们在编译时可用,我创建了一个名为ExternalDLLs的文件夹,并将dll复制到那里,并如上所述将它们设置为EmbeddedResource。要在代码中使用它们,仍然需要设置对它们的引用,但要将Copy local设置为False。为了使代码能够干净无误地编译,您还需要将代码中的using stations设置为DLL的名称空间

下面是一个小实用程序,它可以旋转嵌入的资源名称,并在输出窗口中显示它们的名称空间

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 
            {
            }
        }
    }
}
private void getEmbeddedResourceNamespaces()
{
//注意:需要System.Reflection和System.Diagnostics的using语句。
Assembly=Assembly.getExecutionGassembly();
List embeddedResourceNames=新列表(assembly.GetManifestResourceNames());
foreach(EmbeddedResourceName中的字符串resourceName)
{
使用(var stream=assembly.GetManifestResourceStream(resourceName))
{
Byte[]assemblyData=新字节[stream.Length];
读取(assemblyData,0,assemblyData.Length);
尝试
{
var测试=组装。负载(组装数据);
foreach(test.GetTypes()中的类型)
{
Debug.WriteLine(string.Format(“{0}”的名称空间为“{1}”,类型为.Name,类型为.Namespace));
}
}
抓住
{
}
}
}
}
  • 将此添加到.csprofj文件:
  • >


    来源:

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

    Install-Package Costura.Fody
    

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

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

    Install-Package Costura.Fody
    
    对于任何WPF应用程序,而不是每个项目,都需要添加以下内容以使代码编译为单个EXE。它仍将在输出文件夹中包含DLL,但
    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;
    }; 
    
    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 
                {
                }
            }
        }
    }
    
    <Target Name="AfterResolveReferences">
      <ItemGroup>
        <EmbeddedResource Include="@(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.Extension)' == '.dll'">
          <LogicalName>%(ReferenceCopyLocalPaths.DestinationSubDirectory)%(ReferenceCopyLocalPaths.Filename)%(ReferenceCopyLocalPaths.Extension)</LogicalName>
        </EmbeddedResource>
      </ItemGroup>
    </Target>
    
    [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);
        }
    }   
    
    Install-Package Costura.Fody
    
    <Import Project="$(MSBuildToolsPath)\Microsoft.VisualBasic.targets" />
    
    <Target Name="AfterResolveReferences">
       <ItemGroup>
          <EmbeddedResource Include="@(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.Extension)' == '.dll'">
             <LogicalName>%(ReferenceCopyLocalPaths.DestinationSubDirectory)%(ReferenceCopyLocalPaths.Filename)%(ReferenceCopyLocalPaths.Extension)
             </LogicalName>
          </EmbeddedResource>
       </ItemGroup>
    </Target>
    
    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
    
    dotnet publish -r win-x64 -p:PublishSingleFile=true
    
    <PropertyGroup>
      <RuntimeIdentifier>win10-x64</RuntimeIdentifier>
      <PublishSingleFile>true</PublishSingleFile>
    </PropertyGroup>