Saad
Saad

Reputation: 21

C# Before closing application how to let background worker finish saving application state

In a C# desktop application, a backgroundworker responsible for saving application state is being called in 2 situations. Once while the application is running. That works fine. Other when application is being closed, backgroundworker is called to save the application state but before it starts saving, the application is closed and nothing gets saved.

I tried to solve it by using the AutoReset event class in DoWork and RunWorkerCompleted but didnt work because application closed before backgroundworker could save any thing.

Question is - how can I make the main thread wait until backgroundworker finishes saving?

private void frmMain_FormClosing(object sender, FormClosingEventArgs e)
{
    this.saveAHToolStripMenuItem_Click(this, e);
}

private void saveAHAsToolStripMenuItem_Click(object sender, EventArgs e)
{
    this.backgroundWorkerMain1.RunWorkerAsync(args);
}

private void backgroundWorkerMain1_DoWork(object sender, DoWorkEventArgs e)
{
    saveMethod();
}

private void backgroundWorkerMain1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    showResultOfSaving();
}

Upvotes: 1

Views: 1209

Answers (4)

KarmaEDV
KarmaEDV

Reputation: 1691

Is it WinForms?

Maybe you could register to the OnClosing event. Within it, set a private property IsClosing to true. Mark the eventhandler e as e.Handled = true.

Register to the BackgroundWorker event RunWorkerCompleted. Within it, check if the IsClosing property is set and in that case MainForm.Close() the application.

Edit:

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

namespace BgWorker
{
    public partial class Form1 : Form
    {
        BackgroundWorker _bgWorker;
        bool _iNeedToCloseAfterBgWorker;

        public Form1()
        {
            InitializeComponent();
        }

        void Form1_Load(object sender, EventArgs e)
        {
            _bgWorker = new BackgroundWorker();
            _bgWorker.DoWork += _bgWorker_DoWork;
            _bgWorker.RunWorkerCompleted += _bgWorker_RunWorkerCompleted;
        }

        void _bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            MessageBox.Show("Done!");

            if (_iNeedToCloseAfterBgWorker)
                Close();
        }

        void _bgWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            // Do long lasting work
            Thread.Sleep(2000);
        }

        void btnWorkIt_Click(object sender, EventArgs e)
        {
            // Note how the Form remains accessible
            _bgWorker.RunWorkerAsync();
        }

        void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (_iNeedToCloseAfterBgWorker || _bgWorker.IsBusy) return;
            e.Cancel = true;
            _iNeedToCloseAfterBgWorker = true;
            _bgWorker.RunWorkerAsync();
        }
    }
}

Upvotes: 1

Saad
Saad

Reputation: 21

My solution to this problem is to wait untill backgroundworker is fnished by adding following after backgroundworker async call.

while (this.backgroundWorkerMain1.IsBusy)
{
    Application.DoEvents();
}

Upvotes: 0

Baaleos
Baaleos

Reputation: 1825

Just throwing an idea out there. Perhaps a volatile DateTime property on the MainForm. (Note - DateTimes cannot be volatile it appears, so you could use the string representation instead)

public volatile string _lastUpdated;

This property gets updated everytime the save event is carried out on the background worker thread.

OnClose will check the Time Difference between DateTime.Now and the stored date time.

Eg:

var timeDiff = DateTime.Now - _lastSavedTime;

If the OnClose detects that the timeDiff.TotalSeconds is < 30 (less than 30 seconds) You can then trigger the save event manually from the main thread, prior to the close event being completed.

This however won't protect against Process.Kill - very little can protect against that. All I can suggest is that you manage your saves in a smart way. Eg: Save to a new File each time, keeping the last 5 saves. When the 6th save is made, delete the oldest save etc

This is to account for the potential corruption that may happen from a Process.Kill scenario. Means you will still have at least a 60 second backup, in case the 30 second backup fails.

Upvotes: 0

EventHorizon
EventHorizon

Reputation: 2986

It would be cleaner to place the SaveState code in a separate method, which is called from the background worker and (synchronously) when you close the application.

Either that, or you can block the application from closing based on a manual reset event (ManualResetEvent-class).

Upvotes: 0

Related Questions