Loading “collocated” file dependencies at runtime

Best quote this year, from an internal email thread this morning:

‘Just Say No’ to collocated file dependencies at run time.

Loading file dependencies (like images, etc.) at runtime in a dynamic way can be tricky without resorting to a global information store like the registry. How do you know where your .NET application is “executing from”? You could be loaded via COM into another process, loaded into a .NET application via custom assembly resolution code, or shadow-copied in an ASP.NET application. If you’re depending on collocated files, where should you get them from? Of course this depends on your install strategy and any policy your architecture defines.

Here’s a summary of the different ways you can retrieve a path (whether “C:\foo” or “http://www.foo.com/”) representing an assembly’s “location”. All this information is available on MSDN, I’m just compiling it together here. Nothing original here! Move along…

Actually, the first question is “which assembly”?

1. Assembly.GetAssembly – Retrieves the loaded assembly that defines the specified type.
2. Assembly.GetCallingAssembly – Retrieves the assembly containing the first method up in the call stack. Big warning here: your current method can be expanded inline by the compiler and you might not get the results you expect. If you are going to call GetCallingAssembly and rely on particular results, mark your method with the [MethodImpl(MethodImplOptions.NoInlining)] attribute. Scott Hanselman has a good write-up of the scenario, and an important warning: don’t mess around with the MethodImplAttribute for no reason. Know what you’re doing.
3. Assembly.GetEntryAssembly – The first executed assembly in the AppDomain. If you have a standard WinForms application called “Foobar”, it’ll be the Foobar.exe. If you’re in code that was loaded into another AppDomain but wasn’t executed, it’ll be null.
4. Assembly.GetExecutingAssembly – Gets the assembly that contains the currently executing code. This is the safest way to say “my assembly”.

Once you’re sure you’ve got the correct assembly, you can get it’s location.

1. Assembly.Location – The location of the loaded file that contains the manifest (important note for the few of you who create multi-file assemblies–though here’s some reasons why you’d want to). If it was shadow-copied, it’s the post-copy location. If it was loaded from a byte array (like Reflector does – seriously, have you ever “reflector’d” over Reflector? :) ) then it’ll return an empty string.
2. Assembly.CodeBase – The originally specified location of the assembly. For example, if you load “C:\Foo.dll” this property will return file:///C:/Foo.dll.
3. AppDomain.BaseDirectory – “The base directory the assembly resolver uses to probe for assemblies.” In PowerShell, for example, this returns “C:\WINDOWS\system32\WindowsPowerShell\v1.0\”.
4. Module.FullyQualifiedName – The location of a particular module in an assembly. Using PowerShell again–the best .NET playground around:
PS C:\> [System.Reflection.Assembly]::GetExecutingAssembly().GetModules()[0].FullyQualifiedName
C:\Windows\assembly\GAC_MSIL\System.Management.Automation\1.0.0.0__31bf3856ad364e35\System.Management.Automation.dll
5. Update: Jeffrey Palermo has one more to add: AppDomain.CurrentDomain.SetupInformation.ApplicationBase.

Of course, if the “collocated file” you’re trying to load is an Assembly, the best choice is to use the built-in assembly resolution via Assembly.Load – it takes into account configuration file redirects, publisher policy, GAC vs. private, and well-defined probing rules. My non-official rule of thumb: always always always try to avoid loading assemblies manually.

This entry was posted on Wednesday, December 12th, 2007 at 12:42 am and is filed under .net. You can follow any responses to this entry through the RSS 2.0 feed. Both comments and pings are currently closed.

One Response to “Loading “collocated” file dependencies at runtime”

  1. 7 Links Today (2008-01-10) Says:

    [...] Loading “collocated” file dependencies at runtime [...]