|
dev
newsgroups
|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
Complex Problem with AppDomains, Assembly Loading Contexts and COM Interopmicrosoft.public.dotnet.framework.clr, but got no reply so far, so I'm giving it a try here. My team has been struggling with an issue involving assemblies dynamically loaded into separate app domains in the context of an .Net extension running in a third party executable via COM Interop. After studying what's available online and reading thoroughly through most of Steven Pratschner's very good book "Customizing the Microsoft .Net Framework CLR", I believe we now understand what's going on, but we still don't know how to solve the problem. To describe what exactly the issue is I will need to give quite a bit of detail, so brace yourself for a lenghty post. I would certainly appreciate any suggestions. So here's what we set out to do. We have a third-party unmanaged application that allows us to create extensions. These extensions are easiest accessible via COM, but it would be possible to write simple C++ code (say to custom host a CLR). What we need to do is create a managed .Net extension that allows for a bidirectional communication between the underlying application and our extension. The easies solution appeared to be simply exposing our .Net extension as a COM server, and that's what we did, but we ran into an issue. To make things somewhat more complicated, our extension must itself support plug-ins that need to be dynamically loaded. These plugins would be .Net assemblies that expose a known set of predifined interfaces, and get dynamically loaded using Assembly.Load or similar. Moreover, since we want to support seamless updates to these plugins, we need to place them in separate app domains, so that they can be unloaded when a new version is available. This rather complex setup leads to an unexpected problem which has to do with assembly loading contexts. The CLRs default assembly loading mechanism (which also takes place when using Assembly.Load) searches for the references assemblies only in GAC (if the assembly is signed) and in the directory of the main executable (and potentially its subdirectories). Assemblies found there are loaded into the default loading context. Unfortunately, when running under COM Interop the hosting executable resides in an altogether different directory than our extension and all of its plugins. As a result, the CLR loads our extension's main assembly and all of its early-bound referenced assemblies into the other, "LoadFrom", context. If our extension then goes about creating app domains for its plugins (specifying the appropriate app base path) and loads the plugin assemblies into these app domains, the plugin assemblies get loaded using the usual loading mechanism and end up in the default loading context. The plugin assemblies in turn contain early-bound references to at least one shared assembly that defines the interfaces used to communicate between the extension and its plugins. These shared assemblies get loaded into the default context of the plugin app domain, and if our extension attempts to access any of the plugin via one of the shared interfaces, we get he ominous InvalidCastException, because the two types are incompatible since they are defined in different loading contexts. Interestingly enough, if our extension's assembly and all shared assemblies are copied into the hosting executable's directory, they get loaded into the default loading context and everything works just fine. Unfortunatly, since in our case the executable is a third party application, we do not have the option of sticking our dlls into the exeuctable's directory. I used the Fusion Log Viewer to examine exactly what happens, and I believe my description above is accurate, but please correct me if I'm wrong. I still have a few open questions, and naturally I do NOT have a good solution at this point, so I would appreciate any help. Here are the questions. 1. Nothing that I found online or in Steven's book explains what exactly the COM Interop layer does when creating the CLR to load a the first .Net assembly. Specifically, I have no clue how the CLR knows to look for the assembly in the specific directory that actually contains it. Naturally, such information would be available from Windows Registry, but I can't seem to find anything that would instruct the CLR to load such an assembly. Does the COM Interop layer actually explicitly load the assembly into the CLR by passing the full path and effectively executing LoadFrom? 2. If, in fact, a LoadFrom is executed, does the COM Interop attempt to load the assembly by the usual CLR means of looking under the main executable's directory FIRST? This would explain why copying the dlls into the executable's directory actually loads them into the default loading context. 3. What happens if an assembly loaded into the LoadFrom context early-bound references another assembly? Does the referenced assembly automatically land in the LoadFrom context? 4. Is there any way to intercept the process by which COM Interop creates the CLR and establishes the default app domain? If so, can one then change the app domain's base path? 5. Is there any other way to solve this problem by doing some magic of custom hosting the CLR? Thanks for any suggestions. Andrew |
|||||||||||||||||||||||