All Code Dies and Burns in Time

Delay Loading .Net Assemblies

I had to figure out how to make an application run as its native type while loading only the proper dlls for that platform – when the application runs on x64, load only x64 assemblies so that the application will run fully as x64 and the same for x86. The answer is counterintuitive and not straightforward.

Below is an example application that exhibits this behavior. The Amd64Library.Foo is defined in an assembly compiled for x64; the Win32Library.Foo is similarly defined in an x86 assemly. The Loader application is compiled as ‘Any CPU’ so it will load natively on either platform as long as we load the correct dependency assemblies. Remember that the using statement, is just a compiler hint and doesn’t effect the created MSIL application.

#region Using DirectivesusingSystem;usingWin32Library;#endregionnamespaceLoaderApplication{internalclassProgram{privatestaticvoidMain(string[]args){//RunLoadingBothAssemblies(); // will crash.RunLoadingOneAssembly();Console.ReadLine();}privatestaticvoidRunLoadingBothAssemblies(){if(IntPtr.Size==4){varfoo=newFoo();foo.Bar();}else{varfoo=newAmd64Library.Foo();foo.Bar();}}privatestaticvoidRunLoadingOneAssembly(){if(IntPtr.Size==4){LoadWin32Foo();}else{LoadAmd64Foo();}}privatestaticvoidLoadAmd64Foo(){varfoo=newAmd64Library.Foo();foo.Bar();}privatestaticvoidLoadWin32Foo(){varfoo=newFoo();foo.Bar();}}}

The LoaderApplication references both x64 and x86 assemblies – yes, at the same time. The key is that the assemblies aren’t loaded into the process until the last possible moment. If you look at the RunLoadingBothAssemblies method, the moment you enter the method, the .NET runtime will load the assemblies for the types in that method. This means it will try to load the x64 library into a 32bit process and vise versa resulting in errors. The RunLoadingOneAssembly determines which platform is being used and then makes another call so that only the library needed is loaded.

There is a caveat to this method. The assemblies need to use separate namespaces or different type names. If both assemblies use the same namespace, you will run into type resolution issues as the compiler won’t know which class you are trying to load while referencing both assemblies. If you have source access to the assembly that has to be compiled for each platform, #if statements will allow you to easily adjust the namespaces or type names.

You can watch this application run on x64 and the process name will not have *32 as it will be running natively. So, if you want to have a single build of your application, but run on x64 and x86, this is one way to go.