How to make a single file application in WPF C# – Merging DLLs

H

Sometimes there is a necessity to deliver your applications in a simple and fast way.  However, creating a whole installer project for just a few assemblies is not the optimal solution, and packaging up a zip file must be accompanied by “Unzip this into a folder in your programs directory and create a shortcut…” which brings us back to the whole installer business we started with. Having those reasons in mind (and several other of course), the best case would be to deliver a single EXE of your application.

Below you will find the steps to convert your application into a single-exe bundle without having to pay anything for third party programs.

Step 1: Open your Solution Project with a text editor and paste the following part in the end of your file
PS: Exactly after  <Import Project=”$(MSBuildToolsPath)\Microsoft.CSharp.targets” />

<Target Name="EmbedReferencedAssemblies" AfterTargets="ResolveAssemblyReferences"> 
   <ItemGroup> 
      <AssembliesToEmbed Include="@(ReferenceCopyLocalPaths)" Condition="'%(Extension)' == '.dll'" /> 
      <EmbeddedResource Include="@(AssembliesToEmbed)"> 
       <LogicalName>
%(AssembliesToEmbed.DestinationSubDirectory)%(AssembliesToEmbed.Filename)%(AssembliesToEmbed.Extension)
         </LogicalName> 
      </EmbeddedResource> 
   </ItemGroup> 
   <Message Importance="high" Text="Embedding: @(AssembliesToEmbed->'%(DestinationSubDirectory)%(Filename)%(Extension)', ', ')" /> 
</Target>

Step 2: Reload your project in VS and create a new class named Program.cs

public class Program
{
   [STAThread]
   public static void Main()
   {
      var assemblies = new Dictionary<string, Assembly>();
      var executingAssembly = Assembly.GetExecutingAssembly();
      var resources = executingAssembly.GetManifestResourceNames().Where(n => n.EndsWith(".dll"));

      foreach (string resource in resources)
      {
         using (var stream = executingAssembly.GetManifestResourceStream(resource))
         {
            if (stream == null)
               continue;

            var bytes = new byte[stream.Length];
            stream.Read(bytes, 0, bytes.Length);
            try
            {
               assemblies.Add(resource, Assembly.Load(bytes));
            }
            catch (Exception)
            { }
         }
      }

      AppDomain.CurrentDomain.AssemblyResolve += (s, e) =>
      {
         var assemblyName = new AssemblyName(e.Name);
         var path = string.Format("{0}.dll", assemblyName.Name);
         return assemblies.ContainsKey(path) == true ? assemblies[path] : null;
      };

      App.Main();
   }
}

Step 3: Go to your project’s properties (right click -> Properties) and change the startup object field in the Application tab to <ProjectName>.Program

Extra TIPS: After those steps everything should work almost correctly. However in some cases like Mahapps.Metro you may face some issues. In order to avoid them you have to move all your styling resources reside in Apps.xaml in a separate resources dictionary.

Disclaimer: The present content may not be used for training artificial intelligence or machine learning algorithms. All other uses, including search, entertainment, and commercial use, are permitted.

Categories

Tags