Wednesday, June 02, 2004

I have been facing a silly bug related to AppDomain, so stupid that it is worth mentionned in a blog.

The symptoms

Load an assembly in a separate AppDomain, create an instance of a type. Things work correctly. Then unload the domain, and recreate the type. Somehow, strange bugs (Custom Attributes not detected, the debugger messed up) arise.

How-to recreate it:

Assume that we have the Assembly MbUnit.Core.dll, MbUnit.Framework.dll (that references Core) and Test.dll that references both. Core defines an abstract Custom Attribute TestPatternFixtureAttribute,

public abstract TestFixturePatternAttribute :Attribute{...}
that is inherited in Framework as TestFixtureAttribute
public class TestFixtureAttribute : TestFixturePatternAttribute{...}
and TestFixtureAttribute is used in Test to tag fixture classes
[TestFixture]
public class MyTest{...}
At last, Core contains RemoteTestTree a serializable class that takes care of loading assemblies, extracting fixtures (class taged with TestPatternFixtureAttribute) and launching tests.

  1. Create a separate AppDomain using AppDomain.CreateDomain,
  2. Create an instance of RemoteTestTree using AppDomain.CreateInstanceFromAndUnWrap
  3. Load Test.dll in the domain and find fixtures. At this point, everything works and we found the fixtures.
  4. Unload the separate AppDomain using AppDomain.UnLoad,
  5. Re-Create an instance of RemoteTestTree (at this point, weird things happen because the debugger seems to tell: RemoteTestTree is not initialized)
  6. Load Test.dll in the domain and find fixtures. This time, no fixture is found because the framework seems to tell that TestFixtureAttribute does not inherit from TestPatternAttribute.

The fix

In point 2,5, I have been using AppDomain.CreateInstanceFromAndUnWrap, this is the error. I should have been using AppDomain.CreateInstanceAndUnWrap. The difference is subtle, 4 letters.

The explanation

I'm still unsure of the explanation.... so I'm waiting for someone to come up with a relevant explanation :)

posted on Wednesday, June 02, 2004 8:37:00 PM UTC  #    Comments [9]
Monday, June 06, 2005 6:00:04 PM UTC
I experienced the same behavior trying to debug and test from within the IDE. It seems to have to do with versioning - your have .* in your AssemblyInfo.cs and it seems like, once a new version is compiled, the host process is "confused" about that inheritance relationship. I think I solved it by explicitly specifying full version numbers in AssemblyInfo for all your projects (which, "they" say, is a good practice anyway).
Sanin Saracevic
Monday, June 06, 2005 6:00:04 PM UTC
Jamie Cansdale had told me the same thing (removing .*) but it had no effect. The problem was not related to version number changing. I'm wondering if I found a way to get dll hell back into business :)
Jonathan de Halleux
Monday, June 06, 2005 6:00:05 PM UTC
Hopefully not!! ;-)
<br>Well, I did not investigate the root cause of the issue - I settled for the quick fix. I'll try to dig around a little deeper to try to see what's going on. Keep you posted.
Sanin Saracevic
Monday, June 06, 2005 6:00:05 PM UTC
I don't knof if this is related, but worth a read anyway....
<br>
<br>Debugging an InvalidCastException
<br><a target="_new" href="http://blogs.msdn.com/suzcook/archive/2004/06/02/147195.aspx">http://blogs.msdn.com/suzcook/archive/2004/06/02/147195.aspx</a>
Jamie Cansdale
Monday, June 06, 2005 6:00:06 PM UTC
Hi Jamie,
<br>
<br>The blog you pointed describes exactly the bug I had: loading multiple times the same assembly and not catching the InvalidCastException, in other words: dll hell.
<br>
<br>Thanks for the link.
Jonathan de Halleux
Monday, June 06, 2005 6:00:07 PM UTC
I found the following FAQ very helpfull
<br>
<br><a target="_new" href="http://www.gotdotnet.com/team/clr/AppdomainFAQ.aspx">http://www.gotdotnet.com/team/clr/AppdomainFAQ.aspx</a>
<br>
<br>I was running into strang issues myself. Some facts I found out:
<br>- Somewhere somebody wrote that when you invoke the Unload, the assembly will not be unload right away.
<br>- Statics are static only within an AppDomain, they are not cross app domain shared.
<br>- Whatch your MarshallByRef, and Serialization.
<br>
<br>L.A.Marco
<br>
<br>
<br>
<br>
<br>
<br>
l.a.marco
Monday, June 06, 2005 6:00:08 PM UTC
I have another strange problem, AppDomain.Unload(&lt;domain&gt;) hangs and never returns, any suggestions.
Sanjay
Monday, June 06, 2005 6:00:08 PM UTC
You must be keeping reference to some instance that belong to that AppDomain. However, in that case, it show throw an exception.
<br>
<br>What .Net version are you using ? Is there an repro that I could try ?
Jonathan de Halleux
Monday, June 06, 2005 6:00:09 PM UTC
Wow, superb!
<br>Thanks.
Diana
Comments are closed.