Does COM interop respect .NET AppDomain boundaries for assembly loading?
Asked Answered
F

3

9

Here's the core problem: I have a .NET application that is using COM interop in a separate AppDomain. The COM stuff seems to be loading assemblies back into the default domain, rather than the AppDomain from which the COM stuff is being called.

What I want to know is: is this expected behaviour, or am I doing something wrong to cause these COM related assemblies to be loaded in the wrong AppDomain? Please see a more detailed description of the situation below...

The application consists of 3 assemblies: - the main EXE, the entry point of the application. - common.dll, containing just an interface IController (in the IPlugin style) - controller.dll, containing a Controller class that implements IController and MarshalByRefObject. This class does all the work and uses COM interop to interact with another application.

The relevant part of the main EXE looks like this:

AppDomain controller_domain = AppDomain.CreateDomain("Controller Domain");
IController c = (IController)controller_domain.CreateInstanceFromAndUnwrap("controller.dll", "MyNamespace.Controller");
result = c.Run();
AppDomain.Unload(controller_domain);

The common.dll only contains these 2 things:

public enum ControllerRunResult{FatalError, Finished, NonFatalError, NotRun}
public interface IController
{
    ControllerRunResult Run();
}

And the controller.dll contains this class (which also calls the COM interop stuff):

public class Controller: IController, MarshalByRefObject

When first running the application, Assembly.GetAssemblies() looks as expected, with common.dll being loaded in both AppDomains, and controller.dll only being loaded into the controller domain. After calling c.Run() however I see that assemblies related to the COM interop stuff have been loaded into the default AppDomain, and NOT in the AppDomain from which the COM interop is taking place.

Why might this be occurring?

And if you're interested, here's a bit of background:

Originally this was a 1 AppDomain application. The COM stuff it interfaces with is a server API which is not stable over long periods of use. When a COMException (with no useful diagnostic information as to its cause) occurs from the COM stuff, the entire application has to restarted before the COM connection will work again. Simply reconnecting to the COM app server results in immediate COM exceptions again. To cope with this I have tried to move the COM interop stuff into a seperate AppDomain so that when the mystery COMExceptions occur I can unload the AppDomain in which it occurs, create a new one and start again, all without having to manually restart the application. That was the theory anyway...

Faldstool answered 28/10, 2008 at 6:25 Comment(1)
fyi, if anyone else takes a look at this - it's the HP Quality Center API DLLs giving me this issue. I worked around it by making the app restart itself, but I'd still be really interested into why this was happening.Faldstool
C
17

Unfortunately, A COM component is loaded within Process Space and not within the context of an AppDomain. Thus, you will need to manually tear-down (Release and Unload) your Native DLLs (applies to both COM and P/Invoke). Simply destroying an appdomain will do you no good, but respawning the whole process shouldn't be necessary to reset COM state (simply recreating the COM object(s) should also normally work, this sounds like a bug within the component providers code, perhaps they can address it?)

References

(TechNet) Process Address Space

(MSDN) Application Domains

(MSDN) Boundaries: Processes and AppDomains

Cosimo answered 6/11, 2009 at 4:43 Comment(5)
Thanks for your response, Shaun. It's been so long now that I can't remember if I tried just recreating the COM objects, but I suspect I did. When I have time I will give your suggestions a try. In the meantime, +1 for mentioning the process space.Faldstool
So I still haven't had a chance to try this, but you did actually answer the main question so I'll mark it as answered. Many thanks for everyone's input.Faldstool
And yes, I do suspect it's a bug in the provider's code, especially as the cause of the error leading me to try and unload/reload the COM component is a real mystery. I should re-examine my own code again someday before blaming someone else though.Faldstool
Thanks, this is helpful. Can anyone provide links or other references so I can better understand Process Space vs. App Domain, and how Process Space is used by .NET-as-COM?Heterosporous
Links added. "Process Space" is short for "Process Address Space" and refers to the entire memory footprint of a process. "Application Domain" is a small partition of memory within Process Address Space reserved for use by a .NET Application. A .NET Application may allocate more than one AppDomain, but typically only has one (referred to as the Primary AppDomain.) COM and WinAPI DLLs are loaded into Process Address Space outside of the allocated memory of an AppDomain, the CLR then maps them into each loaded AppDomain so they are visible to the .NET Code within. HTHCosimo
B
3

Here's the proof that Shaun Wilson's answer is correct

Com App Domain

Beard answered 11/1, 2013 at 17:26 Comment(0)
L
1

Don't make your Controller MBR. Create a small proxy, which loads the Controller in the second domain and starts it. That way Controller dll will not be loaded in the first domain.

Langobardic answered 28/10, 2008 at 15:52 Comment(5)
Hi Sunny, That was the idea behind using the IController interface in a common dll. The Controller.dll doesn't get loaded into the default domain. The COM interop activity by the Controller class in the controller domain seems to cause COM related DLLs to be loaded in the default domain.Faldstool
UPDATE: using a proxy to create and run the Controller in the other domain has the same effect. The Controller.dll is NOT getting loaded into the default AppDomain (that wasn't the problem), but the COM interop stuff seems to still be loading COM assemblies back in the default domain.Faldstool
Did you try to load the interop assemblies manually in the second domain, and not relaying on the autoloader?Langobardic
Are you suggesting loading the .NET interop assembly manually, or the COM assemblies that the interop assembly then causes to be loaded? I will check into it and see what happens as soon as I have time.Faldstool
I had to load the interop assemblies.Langobardic

© 2022 - 2024 — McMap. All rights reserved.