When I was writing application that required to parse HTML pages, I selected HtmlAgilityPack to do this. It is excellent library but it's sometimes inconvenient to distribute program with multiple DLLs. So, I decided to link required DLL statically.
The first solving that I found was to merge required assemblies into my own using ILMerge. Spent a little time to find required options I merged DLL into the program:
ILMerge.exe /targetplatform:v4,"C:\Windows\Microsoft.NET\Framework4.0.30319" /out:appout.exe app.exe HtmlAgilityPack.dll
It works ok, but while searching command line options I found another decision by Jeffrey Richter how to get one file for application. Read the comments I took a notice on this one by Jeff:
ILMerge produces a new assembly file from a set of existing assemblies. This means that the original assemblies lose their identity (name, version, culture, and public key).
What I am showing here is creating an assembly that embeds the EXISTING assemblies into it so that they do not lose their identity at all.
So, I decided to use his method. But there was one problem - it didn't work for me :). His code couldn't find the resource. So, I started to search further for its another variations. But I couldn't find what's was wrong.
Nevertheless, I found two ways how to implement Jeff's idea. The first resolution is direct:
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
if (new AssemblyName(args.Name).Name == "HtmlAgilityPack")
return Assembly.Load(Properties.Resources.HtmlAgilityPack);
return null;
}
And the second is more universal and doesn't require to write "if" for every embedded assembly:
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
var requestedAssemblyName = new AssemblyName(args.Name).Name;
var manifestResourceName = new AssemblyName(Assembly.GetExecutingAssembly().FullName).Name + ".Properties.Resources.resources";
using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(manifestResourceName))
{
using (var reader = new ResourceReader(stream))
{
var resource = reader.GetEnumerator();
while (resource.MoveNext())
{
if ((string)resource.Key == requestedAssemblyName)
return Assembly.Load((byte[])resource.Value);
}
}
}
return null;
}