Re: Complex Problem with AppDomains, Assembly Loading Contexts and COM Interop
- From: "ObsessivelyCurious" <andrew.miadowicz@xxxxxxxxx>
- Date: 12 Sep 2006 15:43:52 -0700
David,
Thanks for your reply. We have since found a solution that works for
us and requires a minimal GAC installation. Since our "hosting COM
executable" happens to be iexplore.exe, it is impractical to supply a
config file in the executable's directory. And, since some number of
..Net assemblies get loaded by the CLR as the result of interop, we need
to rely on the default base path in the default app domain, as we don't
have a chance to set anything up ahead of time. However, all
subsequent assemblies get loaded into a separate app domain, for which
we can supply our own base path as well as config file, which we do,
and this allows us to load the assemblies into the Load context.
Incidentally, I find it very interesting that you can force fusion to
load an assembly from any location whatsoever by using codebase hints.
This would seem to violate its default police, and I wonder if it might
pose some security risk.
Andrew
David Levine wrote:
I don't believe the basic problem is related to COM interop, that's just
what is making the problem surface. I believe the root problem is that you
have assemblies located in a directory that in one case is reachable via the
default load context and in another is reachable only via the LoadFrom
context.
One workaround is to install all assemblies in the GAC but I personally
don't like doing that - it pollutes the GAC and creates other versioning and
installation issues.
The mechanism I used to avoid the Load versus LoadFrom problem was whenever
I create a secondary appdomain to run a application in I would first create
an config file on the fly for that appdomain. That file is specified as the
appdomain's configuration file as part of the appdomain setup information. I
have a list of all assemblies that must be shared and I create binding
redirects and codebase hints for all such assemblies. When the fusion layer
loads the assemblies the codebase hints redirect it to the assembly I
specified in the codebase hint. This allows me to load all assemblies into
the Load context and avoid the InvalidCastException. AFAIK when using
codebase hints there are no restrictions on where the assembly must be
located other then that it must be a valid path.
Can you supply an app.config file that would be used by your COM hosting
executable to point to your assemblies? This should have the effect of
loading them into the default load context.
It might also help to use Trace to print out some information from within
the ctor of a class that gets loaded early about the appdomain; the base
directory, the private bin paths, codebases etc.
<andrew.miadowicz@xxxxxxxxx> wrote in message
news:1156544208.532468.270100@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
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
.
- Prev by Date: Reflection in 1.1 and 2.0
- Next by Date: Re: CLR Events and Handlers?
- Previous by thread: Reflection in 1.1 and 2.0
- Next by thread: Error in event log about no support for profilers written for .NET 1.x
- Index(es):
Relevant Pages
|
Loading