Sometimes we need to prepare a single file application, but what if we want to split a code into DLLs or use 3rd party libraries?
Fortunately .NET provides an event AssemblyResolve, which allows us to resolve them manually. In this article I will show how to prepare a Windows Forms Application with loading DLLs from resources.
1. Include DLLs
Add references
First we need to add references to our libraries, so that we would be able to use them in our project.
Disable “Copy Local”
As we want to embed those libraries, we don’t want them to be copied into our output directory. We need to right click on each reference to our DLL, go to Properties and set Copy Local to false.
Add to resources
Now we need to put our libraries into the project’s resources. Go to Solution Explorer, select Properties and double click on Resources.resx. In the designer add compiled DLL files.
When it’s done, there will be created a directory Resources with copied DLLs.
Change Build Action
There is one more thing we need to do – change build action. Go to Solution Explorer, right click on each resource (Library1.dll, Library2.dll), open Properties and set Build Action to Embedded Resource. Now our libraries are ready and will be included in the executable file.
2. Load libraries from resources
Ok, we’ve got included DLLs, but still we need to load them, otherwise there will be thrown an exception during the first call to them. Now it’s time to use AssemblyResolve event.
We are going to list all DLLs in resources and load them using Assembly.Load(). Fortunately we don’t have to resolve all assemblies, for unknown (not ours) we can just return null. The following code contains a complete Program.cs file.
using System; using System.Linq; using System.Reflection; using System.Windows.Forms; namespace EmbeddedLibraries { static class Program { private static Assembly ExecutingAssembly = Assembly.GetExecutingAssembly(); private static string[] EmbeddedLibraries = ExecutingAssembly.GetManifestResourceNames().Where(x => x.EndsWith(".dll")).ToArray(); [STAThread] static void Main() { // Attach custom event handler AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); } private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { // Get assembly name var assemblyName = new AssemblyName(args.Name).Name + ".dll"; // Get resource name var resourceName = EmbeddedLibraries.FirstOrDefault(x => x.EndsWith(assemblyName)); if (resourceName == null) { return null; } // Load assembly from resource using (var stream = ExecutingAssembly.GetManifestResourceStream(resourceName)) { var bytes = new byte[stream.Length]; stream.Read(bytes, 0, bytes.Length); return Assembly.Load(bytes); } } } }
AssemblyResolve event
If you write your own AssemblyResolve handler, be aware that some methods like for example Assembly.Load(name) can cause a stack overflow, because of recursion. For more information read this.