user3256047
user3256047

Reputation: 1

C# Application.DoEvents cannot work in my application

As my English is not well, I explain my question simple and paste a code snippet here to describe the problem.

The problem is a multiple threading issue in our winForm application. I simple the logic as following code sample.

In the test code, there are 1 mainForm Form1 and a button named "Start" in the mainForm. When user click the button, two forms form2 and form3 will be shown from 2 background threads. After form2 was closed, the Form1 will be triggered to close. But form3 is shown here, so I need user to close form3 by himself. So I handled form.Closing event and use Application.DoEvents() to let user close form3. It looks work in mind. But actually, the form3 can accept user's actions but form3 will not be closed as expected.

Please explain why form3 cannot be closed here and how to modify the code to make user's close operation work.

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

namespace CloseFormFromMainThread
{
public partial class Form1 : Form
{
    private Form2 _form2;
    private Form2 _form3;
    private SynchronizationContext _synchronizationContext;

    public Form1()
    {
        InitializeComponent();
        Closing += Form1Closing;
    }

    void Form1Closing(object sender, CancelEventArgs e)
    {
        while (_form3 != null)
        {
            Application.DoEvents();
            Thread.Sleep(100);
        }
    }

    private void ButtonStartClick(object sender, EventArgs e)
    {
        var thread = new Thread(StartForm3);
        thread.Start();

        var thread2 = new Thread(StartForm2);
        thread2.Start();
    }

    private void StartForm3()
    {
        Thread.Sleep(200);
        var action = new Action(() =>
                                    {
                                        _form3 = new Form2();
                                        _form3.Text = "form 3";
                                        _form3.ShowDialog();
                                        _form3 = null;
                                    });
        ExecuteActionInUiThread(action);
    }

    private void Form1Load(object sender, EventArgs e)
    {
        _synchronizationContext = SynchronizationContext.Current;
    }

    private void StartForm2()
    {
        Thread.Sleep(500);
        var action = new Action(() =>
        {
            _form2 = new Form2();
            _form2.Text = "form 2";
            _form2.ShowDialog();

            Close();
        });
        ExecuteActionInUiThread(action);
    }

    private void ExecuteActionInUiThread(Action action)
    {
        var sendOrPostCallback = new SendOrPostCallback(o => action());
        _synchronizationContext.Send(sendOrPostCallback, null);
    }
}
}

Upvotes: 0

Views: 2029

Answers (2)

user3256047
user3256047

Reputation: 1

As I checked the source code of Form.Close(), for modal dialog, there only raised FormClosing event, no Closed event raised. No signal was sent to let form3.ShowDialog to go ahead. So I think only use main thread and Application.DoEvents cannot make code go ahead. Then it is a soft deadlock in the main thread.

Currently, I use a another thread to do check (_form3 != null) and let main thread to execute the _form3.ShowDialog() logic. Following is my code about Form1Closing.

    private bool _isFormClosing;
    void Form1Closing(object sender, CancelEventArgs e)
    {
        if (_form3 == null) return;

        e.Cancel = true;
        if (!_isFormClosing)
        {
            _isFormClosing = true;
            Task.Factory.StartNew((() =>
            {
                while (_form3 != null)
                {
                    Thread.Sleep(50);
                }

                ExecuteActionInUiThread(Close);
            }));
        }
    }

Upvotes: 0

PMF
PMF

Reputation: 17298

First suggestion: Do not use Application.DoEvents(). Ever. Whenever you think you need it, you have a conceptual problem in your code flow that you should fix first. I guess your code is just creating a deadlock because it waits for the OnClosing callback to return before it can process more events (like the closing of another form).

Upvotes: 2

Related Questions