Wednesday, 29 January 2014

How to merge multiple .NET assemblies into one

Sometimes we need to deliver our code as a single assembly for different reasons. There are a few techniques for doing that.

The first one and maybe the most obvious is ILMerge. A command line tool that disassembles multiple assemblies into IL code and then reassembles them to a single one.

When to use ILMerge:
 - When all the assemblies are from the same provider. If the assemblies have strong name, they will be re-signed with the snk file of the first of them (or executable if exists).
- ILMerge works well on Microsoft.NET framework only. Supported version is only 2.0 and higher

When we cannot use ILMerge:
- When merging assemblies from different providers. For example, one assembly written by our company which references a few 3rd party assemblies.
- When merging WPF assemblies, because they use embedded resources, that needs to be serialized
- ILMerge won't work on other .NET frameworks, such as Mono or Rotor.
- ILMerge won't merge assemblies from .NET Framework 1.1


Another technique makes use of embedding assemblies as resources within the main assembly. So here are the steps:
1. Add referenced assembly as embedded resource to the main assembly.
2. Don't forget to select "Embedded Resource" in Build Action on File Properties
3. Add a code that loads assembly from the resources (see below)

When to use loading assemblies from resources:
- When we need to load multiple strong named assemblies from different providers.
- Any case that is not supported by ILMerge

Here is a sample code that loads assemblies from resources. It started from Jeffrey Richter's blog post and enhanced to support loading multiple assemblies. When I used the code as is in his post I got an exception saying "Method not found". So here is the modified piece of code. It needs to be initialized in a static constructor of your "main" class, i.e. the first class user would access before accessing other classes.

 

public class AssemblyResolver
{
    private readonly Dictionary<string,Assembly> m_LoadedAssemblies = new Dictionary<string,Assembly>();

    public Assembly CurrentDomainAssemblyResolve(object sender, ResolveEventArgs args)
    {
        var assemblyResource = BuildAssemblyResourceName(args.Name);

        return m_LoadedAssemblies.ContainsKey(assemblyResource)
            ? m_LoadedAssemblies[assemblyResource]
            : LoadAssembly(assemblyResource);
    }

    private static string BuildAssemblyResourceName(string requestedAssemblyFullName)
    {
        var containingAssemblyName = Assembly.GetExecutingAssembly().GetName().Name;
        var indexOfComma = requestedAssemblyFullName.IndexOf(",", StringComparison.Ordinal);
        var requestedAssemblyName = indexOfComma > 0
            ? requestedAssemblyFullName.Substring(0, indexOfComma)
            : requestedAssemblyFullName;
        var assemblyFullName = String.Format("{0}.Resources.{1}.dll", containingAssemblyName, requestedAssemblyName);
        return assemblyFullName;
    }

    private Assembly LoadAssembly(string assemblyName)
    {
        using (var assemblyStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(assemblyName))
        {
            if (assemblyStream == null) return null;

            using (var reader = new BinaryReader(assemblyStream))
            {
                var readBytes = reader.ReadBytes((int) assemblyStream.Length);
                var assembly = Assembly.Load(readBytes);
                m_LoadedAssemblies.Add(assemblyName, assembly);
                return assembly;
            }
        }
    }

    public AssemblyResolver Attach()
    {
        AppDomain.CurrentDomain.AssemblyResolve += CurrentDomainAssemblyResolve;
        return this;
    }

    public void Dettach()
    {
        AppDomain.CurrentDomain.AssemblyResolve -= CurrentDomainAssemblyResolve;
    }
}
Yes, I know there is a lot of code, but let's try to understand it one by one.

AssemblyResolver class holds list of already loaded assemblies. If an AssemblyResolve event occurs, the resolver looks up in the m_LoadedAssemblies and return the already preloaded instance from it. If the requested assembly is not loaded yet, LoadAssembly method will load it from the embedded resources and add it to m_LoadedAssemblies. BuildAssemblyResourceName method concatenates resource name assuming that dependend assemblies reside in Resources folder in the main assembly.

Please note that it ignores the version of the requested assembly and its public key. It assumes that the correct assembly was added to the embedded resources.

Here is the usage from another class:

public class MainClass : IDisposable
{
    static readonly AssemblyResolver Resolver = new AssemblyResolver();

    static MainClass()
    {
        Resolver.Attach();
    }

    public void Dispose()
    {
        Resolver.Dettach();
    }
}
That's all! Hope it helps

2 comments:

wallcottmabbitt said...

Baltimore Fishbowl reports the enjoyable, factual and sometimes 코인 카지노 controversial scoop on local schools, actual property, cash and energy, culture, way of life, and neighborhood. Find every day posts Monday through Friday, longer unique weekly tales, assorted columns and curated information from around the region, all accompanied by photos and video. After making a win, you need to withdraw it as rapidly as attainable. This is feasible when you make the right selections, however due to of} legal restrictions, there are some issues you must to} consideration to|take observe of}, particularly at on-line on line casino South Korea sites. Incredibly sufficient, many Korea players take their chances with Korea gambling sites despite the strict South Korean gambling legal guidelines and menace of|the specter of} extreme penalties. As technologically advanced because the nation is, it still cannot get full control of internet actions.

Anonymous said...

Streamed from dedicated studios and actual land-based casinos Live casino Roulette presents all the excitement of actual casino roulette. With so many 1xbet korea on-line casinos out there, it may be} tough to know the place begin out|to begin}. Stakes can vary from low to high levels and the famed Texas Hold’em is a favourite because of its superstar gamers and television increase in the noughties. Here have the ability to|you probably can} fold them, increase them and wait for the flop and the river playing cards as you wager incrementally by way of the rounds of sport play. This displays the straightforward lay out of the betting area on the roulette table.