.netwpffreezeshowdialogmessage-pump

ShowDialog method hangs without showing the window ¿Deadlock?


We have a WPF busy window indicator. It is shown on the main thread using a window.ShowDialog(). On response to the Loaded event an action is executed and the window is closed so the application continues its work.

The window.ShowDialog() seems to hang from time to time (very rarely) without showing the dialog and the Loaded event is not fired so the application hangs. The related code is the following one:

private void BusyIndicatorAsyncCall(string text, Action<DoWorkEventArgs> doWorkDinamicView = null, Action doWork = null, Action workCompleted = null, Action<Exception> exceptionReturn = null)
{
    Window window = this.CreateWindowOfBusyIndicator(text);
    Dispatcher dispatcher = window.Dispatcher;
    BackgroundWorker backgoundworker = new BackgroundWorker();
    IViewModel viewModel = (window.Content as UserControl).DataContext as IViewModel;

    this.Modals.Add(viewModel, window);
    if (doWorkDinamicView != null)
    {
        DoWorkEventArgs eventArgs = new DoWorkEventArgs(window.Content);
        backgoundworker.DoWork += (s, e) => doWorkDinamicView.Invoke(eventArgs);
    }
    else if (doWork != null)
    {
        backgoundworker.DoWork += (s, e) => { doWork.Invoke(); };
    }

    backgoundworker.RunWorkerCompleted += (s, e) =>
    {
        Exception exception = e.Error;
        if (exception == null)
        {
            if (workCompleted != null)
            {
                try
                {
                    this.StyleName = null;
                    workCompleted.Invoke();
                }
                catch (Exception ex)
                {
                    exception = ex;
                }
            }
        }
        this.Modals.Remove(viewModel);
        dispatcher.Invoke(new Action(window.Close));
        if (exception != null)
        {
            if (exceptionReturn == null)
                throw new Exception("Error en RunWorkerCompleted.", exception);
            else
                exceptionReturn(exception);
        }
    };

    RoutedEventHandler onLoaded = new RoutedEventHandler((sender, e) =>
                            {
                                try
                                {
                                    backgoundworker.RunWorkerAsync();
                                }
                                catch
                                {
                                }
                            });
    this.BusyIndicatorImpl(window, onLoaded);
}

private void BusyIndicatorImpl(Window window, RoutedEventHandler onLoaded)
{
    window.Loaded += onLoaded;
    window.ShowDialog();
    window.Loaded -= onLoaded;
}

When the application hangs I can see the instruction pointer on the window.ShowDialog() method but the window is not visible on the application and the backgroundWorker has not been started yet so my guess is that the OnLoaded event has not been raised.

The application is not really hung as it redraws correctly but you can't click anywhere on the screen. As a side weird effect when the app hangs it dissapears from the task bar on Windows 7.

The callstack I see when I break the execution is the following one:

user32.dll!_NtUserGetMessage@16()  + 0x15 bytes 
user32.dll!_NtUserGetMessage@16()  + 0x15 bytes 
[Managed to Native Transition]  
WindowsBase.dll!MS.Win32.UnsafeNativeMethods.GetMessageW(ref System.Windows.Interop.MSG msg, System.Runtime.InteropServices.HandleRef hWnd, int uMsgFilterMin, int uMsgFilterMax) + 0x14 bytes  
WindowsBase.dll!System.Windows.Threading.Dispatcher.GetMessage(ref System.Windows.Interop.MSG msg, System.IntPtr hwnd, int minMessage, int maxMessage) + 0x80 bytes 
WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame frame) + 0x75 bytes        WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrame(System.Windows.Threading.DispatcherFrame frame) + 0x49 bytes    
PresentationFramework.dll!System.Windows.Window.ShowHelper(object booleanBox) + 0x17d bytes 
PresentationFramework.dll!System.Windows.Window.Show() + 0x5c bytes 
PresentationFramework.dll!System.Windows.Window.ShowDialog() + 0x27d bytes  
>   xxx.dll!xxx.BusyIndicatorImpl(System.Windows.Window window, System.Windows.RoutedEventHandler onLoaded) Line 743 + 0xd bytes    C#

As others said before it looks like a deadlock so I got a dump with Visual Studio and ran some tools on it looking for the deadlock. This is the information of the threads the application has running:

Debugger Thread ID  Managed Thread ID   OS Thread ID    Thread Object   GC Mode     Domain  Lock Count  Apt Exception
0                   1                   5684            2b2390          Preemptive  9176600 0           STA  
6                   2                   5572            2c7a80          Preemptive  2a82f8  0           MTA (Finalizer) 
7                   3                   3676            2cb828          Preemptive  2a82f8  0           Unknown  
11                  4                   864             7f7d5c0         Preemptive  2a82f8  0           MTA (Threadpool Worker) 
15                  10                  4340            921cdc8         Preemptive  9176600 1           MTA  
16                  12                  1648            9438560         Preemptive  2a82f8  0           MTA (Threadpool Completion Port) 
17                  14                  3380            9001038         Preemptive  2a82f8  0           Unknown (Threadpool Worker) 
21                  7                   5336            9002fe8         Preemptive  2a82f8  0           MTA (Threadpool Worker) 
20                  5                   4120            9003fc0         Preemptive  2a82f8  0           MTA (Threadpool Worker) 
25                  18                  5172            9004508         Preemptive  2a82f8  0           MTA (Threadpool Worker) 
27                  11                  5772            9003a78         Preemptive  2a82f8  0           MTA (Threadpool Worker) 

There is only one thread with app code (the 0, managed 1 with the ShowDialog call). The other threads do not have application code and the thread 15 (managed 10) is the only one with some .Net code.

Looking at the thread 15 (managed 10) as it is the one with a lock I see the following callstack:

[[HelperMethodFrame_1OBJ] (System.Threading.WaitHandle.WaitMultiple)] System.Threading.WaitHandle.WaitMultiple(System.Threading.WaitHandle[], Int32, Boolean, Boolean) 
mscorlib_ni!System.Threading.WaitHandle.WaitAny(System.Threading.WaitHandle[], Int32, Boolean)+92 
System_ni!System.Net.TimerThread.ThreadProc()+28f 
mscorlib_ni!System.Threading.ThreadHelper.ThreadStart_Context(System.Object)+6f 
mscorlib_ni!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)+a7 
mscorlib_ni!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)+16 
mscorlib_ni!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)+41 
mscorlib_ni!System.Threading.ThreadHelper.ThreadStart()+44 
[[GCFrame]] 
[[DebuggerU2MCatchHandlerFrame]] 
[[ContextTransitionFrame]] 
[[DebuggerU2MCatchHandlerFrame]] 

This callstack looks like a timer waiting to be fired to me but maybe I'm wrong about this interpretation so I don't know how to continue with this.

I can provide any information that you need by request. I'm not an expert on WinDbg but I'm starting to be able to handle it so you can ask me to get information with it as well.

UPDATE :

After adding some logs to the application we have the following extra information:

The problem is that the method dispatcher.Invoke(new Action(window.Close)); is called and it executes without throwing an exception but the method window.ShowDialog(); does not return.

We have tried to find the window with Spy++ and similar tools and as far as I can say the window is not there but the window.ShowDialog(); keeps executing.

I hope this gives you some insight about what is going on.


Solution

  • I found the cause of the issue one year latter, the following code shows a proof of concept of what is happening:

    Summarizing (due to a race condition in the real code) there are 3 windows with a parent - child relation (A-->B-->C) and the code is closing B. This works on a WPF application (and C gets closed too) but in a VSTO add-in does not work and hangs, B never leaves the ShowDialog method:

    Create a new VSTO project for Office Word 2010 and paste the following code (not sure about what happens if you target a different Office version):

    using System.Diagnostics;
    using System.Windows;
    using System.Windows.Interop;
    using Action = System.Action;
    
    namespace WordAddIn1HangTest
    {
        public partial class ThisAddIn
        {
            private void ThisAddIn_Startup(object sender, System.EventArgs e)
            {
                Window window1 = new Window();
                window1.Content = "1";
                Window window2 = new Window();
                window2.Content = "2";
                WindowInteropHelper windowInteropHelper1 = new WindowInteropHelper(window1);
                WindowInteropHelper windowInteropHelper2 = new WindowInteropHelper(window2);
    
                System.Windows.Threading.Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() =>
                {
                    windowInteropHelper1.Owner = Process.GetCurrentProcess().MainWindowHandle;
                    window1.ShowDialog();
                    MessageBox.Show("Hello");
                }));
    
                System.Windows.Threading.Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() =>
                {
                    windowInteropHelper2.Owner = windowInteropHelper1.Handle;
                    window2.ShowDialog();
                }));
    
                System.Windows.Threading.Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() =>
                {
                    window1.Close();
                }));
            }
    
            private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
            {
            }
    
            #region VSTO generated code
    
            /// <summary>
            /// Required method for Designer support - do not modify
            /// the contents of this method with the code editor.
            /// </summary>
            private void InternalStartup()
            {
                this.Startup += new System.EventHandler(ThisAddIn_Startup);
                this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
            }
    
            #endregion
        }
    }
    

    As the window1 gets closed you should expect to see the message "Hello". Instead of that you don't see any window open and the ShowDialog call gets hang.