A super-simple way to combine C # programs (with multiple DLLs) into an EXE
Developers often refer to some third-party DLLs, and then compile the generated EXE files can not be separated from these DLLs run independently.
But most of the time we wanted to develop a gadget that would work perfectly with just one exe. What should we do then?
Here's a super-easy way to do it without writing a single line of code.
Here we need to use a tool called Costura.fody. The costura.fody is a plug-in in a fody framework that can be installed into the VS project through NuGet. Once installed, you can package the DLLs (or even PDB) files that your project relies on in an EXE file.
How to use
- In VS, Costura.fody is installed through NuGet for the target EXE project.
- Re-build the project.
After the build is completed, the newly generated EXE file is found in the output directory of the project, and you will also find those DLLs still exist under the output directory. But don't worry, this exe has been able to run independently. You can delete all these DLLs and then run EXE to try again.
In addition, Fody.costura supports some advanced features, such as:
- Temporary assembly file: Automatically extracts the DLL from the EXE to the folder system before running the EXE, and then loads the DLL in a regular manner.
- Merging unmanaged DLL:Fody.Costura can merge unmanaged DLLs, but they do not automatically fit. If your program involves an unmanaged DLL, you need to show it by modifying the Fody.costura configuration file to tell it which unmanaged DLLs you want to merge.
- Preloaded DLL:Fody.Costura can help you preload some DLLs when the program starts, and you can even specify the order in which these DLLs are loaded.
These advanced features require you to modify the Fody.costura configuration file to achieve, the specific steps can refer to its official documentation.
Well, the way Fody.costura is used has been introduced. If you're curious about how the Fody.costura works, you can look down.
Introduction to the principle of implementation
When the CLR tries to load an assembly but fails to load, it raises the Appdomain.assemblyresolve event. Our program can listen to this event and return the assembly that the CLR is trying to load in the handler of the event, thus allowing the program to continue to function properly.
Fody.costura will embed the DLLs referenced to the EXE file when building the project. When a program uses one of the DLLs while it is running (because the CLR cannot find the DLL file, causing the Appdomain.assemblyresolve event to be triggered), it extracts the required DLLs from the embedded resources of the EXE file.
The following two functions are the code that Fody.costura implements this part of the logic.
public static void Attach()
{ var currentDomain = AppDomain.CurrentDomain;
currentDomain.AssemblyResolve += (s, e) => ResolveAssembly(e.Name);
}
public static Assembly ResolveAssembly(string assemblyName)
{ if (nullCache.ContainsKey(assemblyName))
{ return null;
} var requestedAssemblyName = new AssemblyName(assemblyName); var assembly = Common.ReadExistingAssembly(requestedAssemblyName); if (assembly != null)
{ return assembly;
}
Common.Log("Loading assembly ‘{0}‘ into the AppDomain", requestedAssemblyName);
assembly = Common.ReadFromEmbeddedResources(assemblyNames, symbolNames, requestedAssemblyName); if (assembly == null)
{
nullCache.Add(assemblyName, true); // Handles retargeted assemblies like PCL if (requestedAssemblyName.Flags == AssemblyNameFlags.Retargetable)
{
assembly = Assembly.Load(requestedAssemblyName);
}
} return assembly;
}
As you can see, the Attach method listens to the Appdomain.assemblyresolve event. The Assemblyresolve event handler is executed when the CLR cannot successfully load an assembly. Assemblyresolve tries to get the target assembly from the embedded resource of the loaded assembly and returns it to the CLR through the Common.readfromembeddedresources method.
See here, you may ask, when is the Attach method executed?
In fact, for the C # language, the CLR hides a big trick--clr can execute some initialization code before each module (each assembly contains one or more modules) is loaded. Unfortunately, the C # language has no control over this part of the code. Fody.costura is to inject IL code internally into the initialization function of the internal module of the EXE assembly, and this part of the IL code actually executes the attach method. In this way, the EXE assembly is loaded, and the Attach method can be called immediately.
The above is a brief introduction to the principle of fody.costura implementation.
A super-simple way to combine C # programs (with multiple DLLs) into an EXE