Assembly Loading (Example Application)
The real value to merging or relocation of assemblies is generally to consolidate the number of assemblies, ease deployment/updates, and address the issue of file size of the deployable. Tools like SmartAssembly (RedGate, http://www.red-gate.com/products/dotnet-development/smartassembly/) offers a turn-key solution for different ways of consolidation of assemblies. Some of the core features of their application include assembly merging, dynamic assembly loading, compression, obfuscation, etc. While SmartAssembly is very full featured (and offers assembly injection, linkers, etc. – outside the scope of this series), it doesn't match the flexibility of writing your own assembly loading method. Additionally, much of what they do is very much a 'black box' which can have developers scratching their heads (on their forums) for hours trying to debug without knowledge of the underlying mechanisms at play.
The tools:
1.) ilSpy (http://ilspy.net/) allows clear demonstration of the output of your assembly using reflection. Its a free and handy .NET disassembler. Whether you decide to hand write your assembly loading method or use a tool like SmartAssembly, ilSpy will help a great deal to validate your compiled output. Other options are ildasm or reflector, this is to check the binding *before* it happens. (Protip: associate *.dll with ilSpy for easy reflection).
2.) Fuslogvw.exe (Assembly Binding Log Viewer), this allows you to watch the binding as it happens at runtime. Fusion (the Assembly Binding Log Viewer) will show you all assy binds when you set the HKLM\Software\Microsoft\Fusion\ForceLog registry value to 1.
3.) Process Explorer will tell you what DLL (assembly) is loaded by the process. Its an indispensable tool for many other reasons, but this allows you to check after the bind what was loaded (and where it came from).
For starters, the example solution contains three projects: Public.Process (console application [exe]), which depends on Public.Dependency (class library), which depends on Public.SubDependency (class library).
The example application environment with dependencies (project references):
The build output folder (by default) from the example application:
We have our executable, two libraries, symbol files (pdb's, for debugging), *.vshost.exe (visual studio debugger helper, not needed), and the manifest (which is xml file which loosely describes the application & assemblies) inside our release configuration (currently I have the two default configurations, debug/release; in this case I'm working in release — only the release configuration usually requires this type of packaging).
Launch fuslogvw.exe from the Visual Studio Command Prompt (don't forget to first enable HKLM\Software\Microsoft\Fusion\ForceLog registry value to 1). Once logging is enabled, execute Public.Process.exe, then refresh fuslogvw.exe's output window.
With logging is enabled and fuslogvw.exe running, executing Public.Process.exe, fuslogvw.exe's output:
Fuslogvw neatly outlines the assembly binds. First the OS loaded the Public.Process.exe, followed by Public.Dependency.dll, Public.SubDependency.dll, and mscorlib (the .NET runtime). Double clicking any one of the entries gives you all the information regarding the bind actions, probing paths, assembly versions and more. Its a very helpful tool if you have trouble with the binding itself. Most common issues with the assembly load is probing paths and version mismatches (like trying to load a .NET 4.0 assembly using the 2.0 runtime). All of this and more is outlined with fuslogvw.exe.
Lastly, once the application is loaded, we can inspect the process by using ProcessExplorer.exe. With ProcessExplorer, selecting the process give you insights into the process information, threads, file handles, assembly locations, etc. We can easily observe the libraries that were loaded by the process.
Public.Process.exe inside ProcessExplorer.exe:
The last tool worth mentioning is the Visual Studio output window. While its easy to overlook the flurry of verbose text that occurs in the output window when debugging the application, it does offer a quick means to see which assemblies were loaded, from where, and if symbols were loaded.
Next… Assembly Loading: Combine Assemblies & Executables Using ilMerge