BUG: AppDomainUnloadedException is thrown when you call a virtual destructor on a __nogc class during an AppDomain unload (837317)



The information in this article applies to:

  • Microsoft Visual C++ .NET (2003)
  • Microsoft Visual C++ .NET (2002)
  • Microsoft .NET Framework 1.1
  • Microsoft .NET Framework 1.0
  • Microsoft Common Language Runtime (included with the .NET Framework 1.1)
  • Microsoft Common Language Runtime (included with the .NET Framework) 1.0

SUMMARY

When you try to unload an application domain (AppDomain) after you create an instance of an unmanaged C++ class, you may receive an AppDomainUnloadedException exception if the class has a virtual destructor declared in it.

SYMPTOMS

When you try to unload an application domain, you may receive an AppDomainUnloadedException exception or your program may behave unpredictably. You may notice this behavior when an instance of an unmanaged C++ class has a virtual destructor declared in it, and the class is destroyed while the class unloads ts application domain.

CAUSE

As part of the implementation of the It Just Works (IJW) mechanism, when a managed DLL that you created by using the Visual C++ .NET compiler loads, the runtime creates thunks for transitions from unmanaged code to managed code. These thunks contain a reference to the AppDomain where the DLL loads. The runtime does not re-create these thunks if the DLL loads again. Also, the runtime does not update the reference when the original AppDomain unloads, and the DLL loads in another AppDomain.

When the unloading of an AppDomain starts, any transitions to that AppDomain from native code are not permitted and the AppDomainUnloadedException exception is thrown. This behavior occurs for the following reasons:
  • To make sure that incoming calls to the AppDomain are correctly executed because the finalization of types in the AppDomain is started.
  • To make sure that forward progress is made in the unload process.
You receive an AppDomainUnloadedException exception because of a "double thunking" issue that causes the common language runtime to move to native code and then back to managed code to call the virtual destructor function.

RESOLUTION

To work around this problem, use one of the following methods:
  • Change the C++ class definition and its usage from unmanaged code (__nogc) to managed code (__gc).
  • Do not use a virtual destructor in the __nogc class.
  • Use the #pragma unmanaged pragma directive around the __nogc class to override the /clr setting to force the class to be compiled in native code.

STATUS

Microsoft has confirmed that this is a bug in the Microsoft products that are listed at the beginning of this article.

MORE INFORMATION

Steps to reproduce the behavior

To reproduce the bug by using a Microsoft Visual C++ .NET Class Library project and a Microsoft Visual C# .NET Console Application project in Microsoft Visual Studio .NET 2003, follow these steps:
  1. Start Visual Studio .NET 2003.
  2. On the File menu, point to New, and then click Project.
  3. Under Project Types, click Visual C++ Projects, and then click Class Library (.NET) under Templates.
  4. In the Name box, type Server, and then click OK.
  5. In Solution Explorer, expand Header Files under Server.
  6. Double-click Server.h under Header Files.
  7. In the Code window, replace the code in the Server namespace with the following code:
    //workaround 3: Use the #pragma directive by uncommenting the following line.
    //#pragma unmanaged	
    	//workaround 1: Make __nogc as __gc in the following line 
    	__nogc class CauseAppDomainUnloadedException
    	{
    	public:
    		//workaround 2: Remove the virtual keyword
    		virtual ~CauseAppDomainUnloadedException()
    		{
    		}
    	};
    
    //workaround 3: Use the #pragma directive by uncommenting the following line.
    //#pragma managed
    	public __gc class ManagedClass : public System::MarshalByRefObject
    	{
    public:
    		ManagedClass()
    		{
    			//workaround 1: Make __nogc as __gc in the following line
    			m_pInternal = __nogc new CauseAppDomainUnloadedException();
    		}
    		~ManagedClass()
    		{
    			// The destructor is called when ~ManagedClass is called by
    			// the GC when the AppDomain is being unloaded.
    			// If ~CauseAppDomainUnloadedException() is virtual
    			// then an AppDomainUnloadedException occurs in the delete
    			// statement. If ~CauseAppDomainUnloadedException() is
    			// NOT virtual, then it does not occur.			 
    			delete m_pInternal; 
    			m_pInternal = 0;			
    			
    		}
            
    
    	private:
    		//workaround 1: Make __nogc as __gc in the following line
    		CauseAppDomainUnloadedException __nogc* m_pInternal;
    	};
  8. In Solution Explorer, double-click Stdafx.h under Header Files.
  9. Add the following code in the Code window:
    #include <windows.h>
    #include <vcclr.h>
  10. In Solution Explorer, right-click the Server project node, and then click Properties.
  11. In the Server Property Pages dialog box, expand Linker under Configuration Properties, and then click Input.
  12. Type msvcrt.lib in the Additional Dependencies box, and the click OK.
  13. Press CTRL+SHIFT+S to save the project, and then press CTRL+SHIFT+B to build the solution.

    This builds the solution, and the Server.dll file is created in the Debug folder of the project.
  14. In Solution Explorer, right-click the solution node, point to Add, and then click New Project.
  15. Under Project Types click Visual C# Projects, and then click Console Application under Templates.
  16. In the Name box, type Client, and then click OK.
  17. In Solution Explorer, right-click the Client project node, and then click Add Reference.
  18. In the Add Reference dialog box, click Browse, and then select the Server.dll file that you created in Step 14.
  19. Click OK to add the reference.
  20. In Solution Explorer, double-click Class1.cs under the Client project folder.
  21. Add the following code to the top of the Code window after the using statement:
    using System.Reflection;
    using System.Runtime.InteropServices;
  22. Replace the code in the Main function with the following code:
    AppDomain domain = AppDomain.CreateDomain("A new domain");
    //domain.InitializeLifetimeService();
    
    Server.ManagedClass mc = (Server.ManagedClass) domain.CreateInstanceAndUnwrap(
    	typeof(Server.ManagedClass).Assembly.FullName, 
    	typeof(Server.ManagedClass).FullName,
    	false, BindingFlags.Default,null,null,null,null,null);
    
    //ServerCSharp.ManagedClass mc = new ServerCSharp.ManagedClass();
    
    AppDomain.Unload(domain);
  23. In Solution Explorer, right-click the Client project node, and then click Set as StartUp Project.
  24. Press CTRL+SHIFT+S to save the project, and then press CTRL+SHIFT+B to build the solution.
  25. Press F5 to run the application in Debug mode.

    You notice the exception in the Console window.
Note This bug typically reproduces in an environment where Managed Extensions for C++ class libraries are used in multiple AppDomains such as Microsoft ASP.NET. However, under similar situations this bug may be reproduced in Microsoft Visual Studio .NET 2002.

REFERENCES

For additional information about AppDomainUnloadedException exception in managed classes, click the following article number to view the article in the Microsoft Knowledge Base:

309694 BUG: AppDomainUnloaded exception when you use Managed Extensions for C++ components


Modification Type:MajorLast Reviewed:4/1/2004
Keywords:kbManaged kbdomain kbDLL kbThunks kbinterop kbGarbageCollect kbConsole kberrmsg kbcode kbbug KB837317 kbAudDeveloper