Sep 172014
 

Issue

You get CannotUnloadAppDomainException when trying to unload an AppDomain on which a WPF window has been displayed. This is how the exception looks like…

---------------------------
AppDomainUnload
---------------------------
Error while unloading appdomain. (Exception from HRESULT: 0x80131015)
---------------------------
OK
---------------------------

The callstack associated with this exception is not at all informative. This is all that I have for this exception…

System.CannotUnloadAppDomainException occurred
_HResult=-2146234347
_message=Error while unloading appdomain. (Exception from HRESULT: 0x80131015)
HResult=-2146234347
IsTransient=false
Message=Error while unloading appdomain. (Exception from HRESULT: 0x80131015)
Source=mscorlib
StackTrace:
at System.AppDomain.nUnload(Int32 domainInternal)
at System.AppDomain.Unload(AppDomain domain)
InnerException:

Repro

This was the code that was shared by the customer which demonstrates the issue. Nothing proprietary hence sharing this out…

Private Sub Button_Click(sender As Object, e As RoutedEventArgs) Handles btn1.Click
    Try
        btn1.IsEnabled = False

        ‘Create a new AppDomain
        Dim ad As AppDomain = AppDomain.CreateDomain("ad2")
        Dim exeAssembly As String = [Assembly].GetEntryAssembly().FullName

        Dim newDomain As NewAppDomain = DirectCast(ad.CreateInstanceAndUnwrap(Assembly.GetEntryAssembly.FullName, GetType(NewAppDomain).FullName), NewAppDomain)

        'Call a subroutine to show a WPF window
        newDomain.ShowWPFWindow()
        AppDomain.Unload(ad)

        MsgBox("New AppDomain unloaded successfully")

    Catch ex As Exception
        MsgBox(ex.Message)
    Finally
        btn1.IsEnabled = True
    End Try
End Sub

So in above code we’re creating an object in a new app domain (“ad2”) and we then call ShowWPFWindow on this new object which will show up the window in a new app domain. Once ShowWPFWindow returns we unload this AppDomain and this where CannotUnloadAppDomainException happens. We do see the application going unresponsive before this CannotUnloadAppDomainException.

Why does a CannotUnloadAppDomainException occur?

The million dollar question is why does this happen? Well from what we’ve found when a WPF Window is showed for the first time on a new app domain a Stylus input thread gets attached to this window for touch input events on touch enabled devices or at least on devices which such components installed.

When AppDomain.Unload call is made this thread fails to shutdown. We do see a thread abort request already sent to this stylus input thread but we don’t see a response yet.

Exception on top of the stack…

0:009> !pe
Exception object: 028280c4
Exception type:   System.CannotUnloadAppDomainException
Message:          Error while unloading appdomain. (Exception from HRESULT: 0x80131015)
InnerException:   <none>
StackTrace (generated):
SP       IP       Function
00000000 00000001 mscorlib_ni!System.AppDomain.nUnload(Int32)+0x2
07D3E320 793DCDE9 mscorlib_ni!System.AppDomain.Unload(System.AppDomain)+0x41

StackTraceString: <none>
HResult: 80131015

0:009> !threads
ThreadCount:      14
UnstartedThread:  0
BackgroundThread: 9
PendingThread:    0
DeadThread:       3
Hosted Runtime:   no
Lock
ID OSID ThreadOBJ    State GC Mode     GC Alloc Context  Domain   Count Apt Exception
0    1  e10 00b63f40   203a228 Preemptive  025EA018:00000000 00b573b8 0     MTA
2    2  a44 00b65bf0     2b228 Preemptive  0282A108:00000000 00b573b8 0     MTA (Finalizer)
XXXX    3    0 00bf2b18     30820 Preemptive  00000000:00000000 00b573b8 0     Ukn
XXXX    4    0 00c00a20     39820 Preemptive  00000000:00000000 00b573b8 0     MTA
XXXX    5    0 00c09b10     39820 Preemptive  00000000:00000000 00b573b8 0     MTA
5    6 1ec8 00c06d08   10b9228 Preemptive  0280C960:00000000 00b573b8 0     Ukn (Threadpool Worker)
7    7 2388 00c22128     ab028 Preemptive  02600420:00000000 00b573b8 0     MTA
8    8 20e4 00c2b2a8     ab228 Preemptive  025FA3DC:00000000 00b573b8 0     MTA
9    9 22b0 06ff0a58     a7028 Preemptive  0282CAF4:00000000 00b573b8 0     STA System.CannotUnloadAppDomainException 028280c4
11   10 1848 07098f78     2b228 Preemptive  026CE7D8:00000000 00b573b8 0     MTA
16   11 1e74 09e984c8     21228 Preemptive  00000000:00000000 00b573b8 0     Ukn
17   12 22b8 07087b90   102a228 Preemptive  00000000:00000000 00b573b8 0     MTA (Threadpool Worker)
  18   13 14f4 0702d878     2b229 Preemptive  027827CC:00000000 09e047a0 0     MTA <<<<--- Stylus input thread
19   14  a6c 07090058   10b9228 Preemptive  00000000:00000000 00b573b8 0     Ukn (Threadpool Worker)

The only thread in that domain is #18, a stylus input thread. A thread abort has already been posted and a debug suspend is pending as well…

0:018> !ThreadState 2b229
    Thread Abort Requested  <<<- Thread abort requested but yet to shutdown
Debug Suspend Pending
Legal to Join
Background
CLR Owns
CoInitialized
In Multi Threaded Apartment
Fully initialized

Please note this is the only thread on this AppDomain now that’s left to exit. Since it fails to exit we get a CannotUnloadAppDomainException.

Workaround for this CannotUnloadAppDomainException?

Only workaround is to move AppDomain+Window creation to a separate thread and then forcefully shutdown the Dispatcher thread (via calls to Dispatcher.CurrentDispatcher.InvokeShutdown()). InvokeShutdown alone can fail unless a GC is forced. Please see below code. I’ve added a new thread worker function called ‘DoWork’ which does the work of creating and displaying the AppDomain window…

‘Thread proc
Public Shared Sub DoWork()
    'Create a new AppDomain
    Dim ad As AppDomain = AppDomain.CreateDomain("ad2")
    Dim exeAssembly As String = [Assembly].GetEntryAssembly().FullName

    Dim newDomain As NewAppDomain = DirectCast(ad.CreateInstanceAndUnwrap(Assembly.GetEntryAssembly.FullName, GetType(NewAppDomain).FullName), NewAppDomain)

    'Call a subroutine to show a WPF window
    newDomain.ShowWPFWindow()
    Dispatcher.CurrentDispatcher.InvokeShutdown()  <<<--- Workaround
    GC.Collect() <<<—Workaround
    AppDomain.Unload(ad)

    MsgBox("New AppDomain unloaded successfully")
End Sub

Private Sub Button_Click(sender As Object, e As RoutedEventArgs) Handles btn1.Click
    '
    Try
        btn1.IsEnabled = False

        Dim ts As New ThreadStart(AddressOf DoWork)
        Dim WorkerThread As New System.Threading.Thread(ts)
        WorkerThread.SetApartmentState(ApartmentState.STA)
        WorkerThread.Start()
        WorkerThread.Join()

        MsgBox("New AppDomain unloaded successfully")

    Catch ex As Exception
        MsgBox(ex.Message)
    Finally
        btn1.IsEnabled = True
    End Try
End Sub

Please note where we’re calling (workaround) InvokeShutdown.

Dispatcher.CurrentDispatcher.InvokeShutdown()
GC.Collect()

GC.Collect is important just to make sure the Pen/Stylus objects are cleared out from memory. Alternatively you can also write above code in a Window’s closing event so that the stylus input thread is detached from this window and the objects associated with it are cleared from memory.

  4 Responses to “CannotUnloadAppDomainException on AppDomain unload that has displayed a WPF Window/Dialog”

  1. Thanks a lot for this solution!

  2. Many thanks, you solved my problem.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.