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.
ShowDialog shows the window, disables all other windows in the application, and returns only when the window is closed. This type of window is known as a modal window. Modal windows are primarily used as dialog boxes.
Show() method shows a windows form in a non-modal state. ShowDialog() method shows a window in a modal state and stops execution of the calling context until a result is returned from the windows form open by the method.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With