I was playing with .NET 2.0 unmanaged hosting API. I am trying to customize assembly loading and “fix” some assemblies on the fly. I found an interesting gotcha, that I want to write down.
In one line: if you return a non-null value from
ICLRRuntimeHost::ExecuteInDefaultAppDomain returns error code 0x80131051 or
FUSION_E_LOADFROM_BLOCKED, without ever consulting your assembly store, or the non-host store assemblies list.
The CLR hosting API is very well structured. You start with
CorBindToRuntimeEx call, followed by
ICLRRuntimeHost::SetHostControl, and then CLR will ask your host to provide a number of “managers”, such as memory manager, assembly manager, etc. All managers are optional: you may return E_NOINTERFACE to let CLR do the default thing.
Naturally, I was interested in the assembly manager that implements
IHostAssemblyManager interface. This interface returns two objects to the CLR: the assembly store, that knows how to get assemblies, and the list of “non host store assemblies” that the CLR should be able to load by itself. The documentation says that the CLR behaves differently depending on whether non host store assemblies list is NULL or not, but it describes only the differences in assembly loading order.
What it does not describe, is that if you return a non-NULL for “non host store assemblies list”, an internal variable
g_bFusionHosted is set to
true, and the method
true. This means that whenever you try to load an assembly from a particular path, you will get return code
FUSION_E_LOADFROM_BLOCKED, or 0x80131051.
ExecuteInDefaultAppDomain will fail with this error code, because it loads assembly from a path. Even if you don’t specify the path, and don’t specify the extension, it still fails.
Note, that all that matters is a non-null value of the “non host store assemblies list”. If you call
ExecuteInDefaultAppDomain the CLR never actually consults this list. You may even pass any garbage value instead of the real list, the result will still be the same.