Merging dlls into a single .exe with wpf

C#WpfDllIlmerge

C# Problem Overview


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 ILMerge but it can't handle .xaml resources.

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

C# Solutions


Solution 1 - C#

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 .csproj file:

<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.cs look like this:

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

3) Add the OnResolveAssembly method:

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);
	}
}

Solution 2 - C#

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

Install-Package Costura.Fody

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

Solution 3 - C#

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

Try this: 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.

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

Solution 4 - C#

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

> 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:

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.

Solution 5 - C#

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

Solution 6 - C#

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.

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.

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 
            {
            }
        }
    }
}

Solution 7 - C#

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

Solution 8 - C#

As of.NET Core 3.0 this functionality is now part of .NET:

https://docs.microsoft.com/en-us/dotnet/core/whats-new/dotnet-core-3-0#single-file-executables

You can publish as a single executable using dotnet:

dotnet publish -r win-x64 -p:PublishSingleFile=true

Or doing the equivalent operation in Visual Studio: in your Publish Profile Settings, choose a target runtime (you must have one selected to publish as a single file), then expand the "File Select Options" section and select "Produce Single File". Exact steps may vary with Visual Studio versions.

You can also specify these defaults in your .csproj file:

<PropertyGroup>
  <RuntimeIdentifier>win10-x64</RuntimeIdentifier>
  <PublishSingleFile>true</PublishSingleFile>
</PropertyGroup>

However using this approach I've had issues with running my unit tests, so I personally just select the option when publishing.

Solution 9 - C#

  1. add this to .csprofj file:

>

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

2. right click project/ properties/application/starup object / select Sinhro.Program

  1. 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);
     	}
     }	
    

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

Solution 10 - C#

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.

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.

  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
<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:
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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionFarawinView Question on Stackoverflow
Solution 1 - C#WeggedView Answer on Stackoverflow
Solution 2 - C#Dark KnightView Answer on Stackoverflow
Solution 3 - C#TimoteiView Answer on Stackoverflow
Solution 4 - C#MatthieuView Answer on Stackoverflow
Solution 5 - C#HemantView Answer on Stackoverflow
Solution 6 - C#j2associatesView Answer on Stackoverflow
Solution 7 - C#NilsView Answer on Stackoverflow
Solution 8 - C#Alice HeatonView Answer on Stackoverflow
Solution 9 - C#Tone ŠkodaView Answer on Stackoverflow
Solution 10 - C#James IgoeView Answer on Stackoverflow