A long time ago, I wrote a simple application using WPF (I will write about this utility in my next post) that I recently dug up from the lost archives and I wanted to use in my every day life (at work and at home).
I realized that this small application was made up of 3 projects, thus creating one EXE file and two extra DLL files. Since I want to pack it light, and I truly see no reason to carry around 3 files that are all under 1 MB, I looked for a way to compile all of these under one file.
After looking on the internet a little bit, I found a simple solution, which probably isn't the best way to do it, but it works good enough for me...
First, in my main project, I created a folder called Assemblies, and put the other compiled DLLs into this folder manually. Then, I defined these DLLs' Build Action to be 'Embedded Resource' (this is found in the Properties window of every file in your project - image attached). This will cause the DLLs to be compiled as resources embedded into the output EXE file.
And now that the DLLs are in the EXE file, we can delete the DLLs in our output folder and just run our EXE file, right? Wrong! The CLR will still look for these files in the folder (and also in the GAC) and when it doesn't find them, it won't know what to do.
So all we need to do actually is just tell the compiler where to look when it doesn't find the files in the original location. This is done by overriding the method AssemblyResolve
in our current AppDomain
object like this:
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
String resourceName = "MyProject.Assemblies." +
new AssemblyName(args.Name).Name + ".dll";
using (var stream =
Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
{
if (stream == null) ;
byte[] assemblyData = new byte[stream.Length];
stream.Read(assemblyData, 0, assemblyData.Length);
return Assembly.Load(assemblyData);
}
};
What we did here is just retrieve all the embedded resources and find the specific assembly the CLR was looking for.
Now, after compiling your project, you can delete the other DLL files in your output folder and the EXE file will work on its own.
While thinking of this solution, I didn't like the idea of having to copy the assemblies after every change into the 'Assemblies' folder I created, and then compiling again... but after thinking about it for a while, I think it's not that bad in my case if I consider the circumstances - I'm talking about a very small solution, only 3 projects, and all I need this packing into one file is to move it around. I also don't believe I will be working on it that much more, so this means I just need to do this once. If it was a current project at work or even for fun, I wouldn't go with this option, and I would've found a better way around this...
An extra note: While running the application for the first time, it immediately crashed. I opened it in debug mode (which actually meant I needed to put a Thread.Sleep()
in the initialization and then attach to the process through VS since I wanted to delete the DLL files before debugging). I realized that the application was looking for an assembly called XmlSerializers
that I never created. I don't know exactly why, but it turns out that this was automatically generated while compiling. No worries though, this can be taken care of. Just go to the project properties > Build > Output > Generate serialization assembly = Off.