The ShowDialog method hangs without showing the window ¿Deadlock?

We have a WPF busy window indicator. It is displayed in the main thread using window.ShowDialog(). When you respond to the Loaded event, an action is taken and the window closes, so the application continues to work.

It seems to window.ShowDialog()freeze from time to time (very rarely) without showing a dialog, and the Loaded event does not fire, so the application freezes. The related code is as follows:

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 freezes, I see the instruction pointer by the method window.ShowDialog(), but the window does not appear in the application, and backgroundWorker is not running yet, so I assume that the OnLoaded event has not been generated.

, , . , , Windows 7.

, , :

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#

, , Visual Studio , . , :

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) 

(0, 1 ShowDialog). , 15 ( 10) .Net.

15 ( 10), , :

[[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]] 

, , , , , , , .

, . WinDbg, , .

:

:

, dispatcher.Invoke(new Action(window.Close)); , window.ShowDialog(); .

Spy ++ , , , , window.ShowDialog(); .

, , .

+4
3

, , :

(- ) 3 - (A → B → C), B. WPF ( C ), VSTO , B ShowDialog:

VSTO Office Word 2010 ( , , Office):

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
    }
}

1 , "Hello". , ShowDialog .

0

, , , (, Loaded ).

, :

  • , . , , .

  • , . Loaded ( , , Activated). , , .

WPF, boackground . , .

MainWindow.xaml

<Window x:Class="ProgressBarSample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        Title="MainWindow"
        Width="200"
        Height="100"
        mc:Ignorable="d">
    <Grid>
        <Button Click="OnButtonClick">Start</Button>
    </Grid>
</Window>

MainWindow.xaml.cs

using System.ComponentModel;
using System.Threading;
using System.Windows;

namespace ProgressBarSample
{
    public partial class MainWindow
    {
        private BackgroundWorker _backgroundWorker;
        private ProgressWindow _progressWindow;

        public MainWindow()
        {
            InitializeComponent();
        }

        private void OnButtonClick(object sender, RoutedEventArgs e)
        {
            _progressWindow = new ProgressWindow { Owner = this };
            _backgroundWorker = new BackgroundWorker { WorkerReportsProgress = true };
            _backgroundWorker.DoWork += OnWorkerDoWork;
            _backgroundWorker.RunWorkerCompleted += OnWorkerRunWorkerCompleted;
            _backgroundWorker.ProgressChanged += OnWorkerProgressChanged;
            _backgroundWorker.RunWorkerAsync();
            _progressWindow.ShowDialog();
        }

        private void OnWorkerProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            _progressWindow.ProgressValue = e.ProgressPercentage;
        }

        private void OnWorkerRunWorkerCompleted(
            object sender, 
            RunWorkerCompletedEventArgs e)
        {
            _progressWindow.Close();
            MessageBox.Show("Done");
        }

        private void OnWorkerDoWork(object sender, DoWorkEventArgs e)
        {
            int initialValue = 100;
            for (int i = 0; i < initialValue; i++)
            {
                Thread.Sleep(50);
                _backgroundWorker.ReportProgress(i);
            }
        }
    }
}

ProgressWindow.xaml

<Window x:Class="ProgressBarSample.ProgressWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        Title="Progress"
        Width="300"
        Height="100"
        mc:Ignorable="d">
    <Grid>
        <ProgressBar Maximum="100" Minimum="0"
                     Value="{Binding ProgressValue}" />
    </Grid>
</Window>

ProgressWindow.xaml.cs

using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace ProgressBarSample
{
    public partial class ProgressWindow : INotifyPropertyChanged
    {
        private double _progressValue;

        public ProgressWindow()
        {
            InitializeComponent();
            DataContext = this;
        }

        public double ProgressValue
        {
            get { return _progressValue; }
            set
            {
                _progressValue = value;
                OnPropertyChanged();
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(
            [CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
+1

, , . .

, Loaded . , .

onLoaded BusyIndicatorImpl.

private void BusyIndicatorImpl(Window window, RoutedEventHandler onLoaded)
{
    onLoaded();
    window.ShowDialog();
}

, .

private void BusyIndicatorImpl(Window window, RoutedEventHandler onLoaded)
{
    window.Dispatcher.InvokeAsync(() => onLoaded(window, null));
    window.ShowDialog();
}
0

Source: https://habr.com/ru/post/1659178/


All Articles