I am calling managed code from a VB6 Application using COM Interop, which itself subsequently calls code from another managed assembly after creating a new AppDomain. This new AppDomain is what actually instantiates instances of the objects being used.
To try and make this simpler to understand, these are the players:
- VB6 Application: LegacyApp.exe
- Assembly containing Interface information used in all .NET Assemblies: MeatyInterfaces.dll
- COM Interop开发者_运维知识库-visible .NET Assembly: InteropAssembly.dll
- This Assembly has a direct reference to MeatyInterfaces.dll
- Assembly used by InteropAssembly.dll: MeatyImplementations.dll
- This Assembly has a direct reference to MeatyInterfaces.dll, and implements those interfaces
- implements the interface "MeatyInterfaces.IExampleInterface" with implementation "MeatyImplementations.ExampleImplementation"
To boil down the process, essentially we create an AppDomain and instantiate the ExampleImplementation from the new AppDomain as such in the InteropAssembly.dll code called by LegacyApp.exe:
AppDomainSetup domainSetup = GetExampleSetupInfo();
AppDomain domain = AppDomain.CreateDomain(domainSetup.ApplicationName, AppDomain.CurrentDomain.Evidence, domainSetup);
ObjectHandle handle = domain.CreateInstance("MeatyImplementations.dll", "MeatyImplementations.ExampleImplementation");
Once we have this handle, we attempt to unwrap it and cast it to IExampleInterface, which ExampleImplementation implements.
MeatyInterfaces.IExampleInterface initializer = (MeatyInterfaces.IExampleInterface) handle.Unwrap();
This throws the following exception:
Exception: "System.InvalidCastException"
Message: "Unable to cast transparent proxy to type 'MeatyInterfaces.IExampleInterface'"
The strange thing is, if we run through this exact code but starting with a Managed (.NET) Application, it works perfectly fine.
Here's what I know:
- I know that there are two contexts in which Assemblies can be loaded: Load and LoadFrom, and that the latter is sensitive to the location of DLLs.
- I know that casting an object from one Load Context into another appears to be the most frequent cause of this problem (in fact, the only one I've been able to find fairly exhaustive searching)
- I know that, if I override the AssemblyResolve event as recommended here (http://west-wind.com/weblog/posts/601200.aspx), it fixes the problem, which further suggests to me (though I have no material evidence) that InteropAssembly.dll must be loading MeatyInterfaces.dll in a different load context than MeatyImplementations.dll does when used in the manner shown above.
So here's what I don't understand... if anyone can provide clarification, I would be most grateful:
What I don't understand is why the load context would be different depending on if I'm using InteropAssembly.dll through a VB6 App (using COM Interop) vs. a .NET App, or whether the whole concept of Load Contexts has just been a red herring all along and his fix only resolved my problem by coincidence.
I found the answer:
I was looking at the Fusion Log incorrectly:
InteropAssembly.dll was loading in a LoadFrom context, but that wasn't the issue. The issue was that when InteropAssembly.dll tried to load MeatyInterfaces.dll, it couldn't find it: MeatyInterfaces.dll was in the bin folder where InteropAssembly.dll was located, but it wasn't looking there for it: it was looking in LegacyApp.exe's Application Path.
The .NET App was working basically by coincidence: these Assemblies were added to the .NET App's bin folder as part of the build process, so it had all of the dependencies it needed in the folder that it was checking.
The exception I was seeing that led me down the Load context path was basically a coincidence: the failure to load the dependency didn't trigger an exception until the first time it was used, which happened to be where that cast occurred: which threw an exception that appears to be caused by mismatched load contexts in a vast majority of circumstances.
F7U12
Yeah, so that was the cause: I've learned the hard way now that though an Assembly can be loaded via COM Interop from a location other than the calling application's path (or the GAC), the Assembly's dependencies will still need to be in the Application's path. D'oh.
精彩评论