Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Backgroundworker.CancelAsync() not working

I have a backgroundworker that runs a single process. I want to be able to cancel the processing while it's going, but when I call the CancelAsync() method, it never actually cancels. Where am I wrong?

Here's the DoWork() method:

        private void bgw_DoWork(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker b = sender as BackgroundWorker;

        if (b != null)
        {
             if (!b.CancellationPending)
            {
                try
                {
                    // Let's run the process as a backgroundworker so we have the ability to cancel the search, and/or be able to view results while it's still searching
                    ProcessParameters pp = e.Argument as ProcessParameters;

                    if (pp.DoReplace)
                        results = FindReplace.FindReplace.FindAndReplace(pp.PathToSearch, pp.FindText, pp.ReplaceText, pp.UseRegularExpressions, pp.IncludeList, pp.ExcludeList, pp.RecurseSubdirectories, pp.IgnoreCase);
                    else
                        results = FindReplace.FindReplace.Find(pp.PathToSearch, pp.FindText, pp.UseRegularExpressions, pp.IncludeList, pp.ExcludeList, pp.RecurseSubdirectories, pp.IgnoreCase);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.ToString());
                }
            }
            else
            {
                // Cancel was clicked
                e.Cancel = true;
            }
        }
    }

Here's the method that starts the processing:

        private void btnGo_Click(object sender, EventArgs e)
    {
        if (btnGo.Text == "Cancel")
        {
            if (DialogResult.Yes == MessageBox.Show("Are you sure you wish to cancel?", "Cancel Requested", MessageBoxButtons.YesNo, MessageBoxIcon.Question))
                bgw.CancelAsync();

            return;
        }

        if (tbFind.Text.Length == 0)
        {
            MessageBox.Show("Find text is not valid.");
            return;
        }

        tbFound.Text = String.Empty;
        tbFoundInThisFile.Text = String.Empty;
        lvResults.Items.Clear();
        includeList = null;
        excludeList = null;
        results = null;

        if (radDirectory.Checked && !radFile.Checked)
        {
            includeList = BuildIncludeExcludeList(tbIncludeFiles.Text);
            excludeList = BuildIncludeExcludeList(tbExcludeFiles.Text);
        }

        ProcessParameters pp = null;

        if (chkReplace.Checked)
            pp = new ProcessParameters(tbPath.Text, tbFind.Text, tbReplace.Text, chkUseRegEx.Checked, includeList, excludeList, chkRecursion.Checked, chkIgnoreCase.Checked, true);
        else
            pp = new ProcessParameters(tbPath.Text, tbFind.Text, chkUseRegEx.Checked, includeList, excludeList, chkRecursion.Checked, chkIgnoreCase.Checked, false);

        bgw.RunWorkerAsync(pp);

        // Toggle fields to locked while it's running
        btnGo.Text = "Cancel";
    }

And here's the WorkerCompleted() event:

        private void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        btnGo.Text = "Go";

        string message = String.Empty;
        const string caption = "FindAndReplace is Complete";

        if (!e.Cancelled)
        {
            if (results != null)
            {
                tbFound.Text = results.Found.ToString();
                tbSearched.Text = results.FilesSearched.ToString();
                tbSkipped.Text = results.FilesSkipped.ToString();

                message = String.Format("Search finished resulting in {0} match(es).", results.Found);
            }
            else
                message = "The FindAndReplace results were empty. The process was cancelled or there was an error during operation.";
        }
        else
            message = "The FindAndReplace process was cancelled.";

        if (e.Error != null)
            message += String.Format("{0}{0}There was an error during processing: {1}", Environment.NewLine, e.Error);

        MessageBox.Show(message, caption);
    }
like image 279
ganders Avatar asked Dec 04 '22 16:12

ganders


2 Answers

CancelAsync doesn't actually abort your thread or anything like that. It sends a message to the worker thread that work should be cancelled via BackgroundWorker.CancellationPending. Your DoWork delegate that is being ran in the background must periodically check this property and handle the cancellation itself.

Read more here

like image 128
zimdanen Avatar answered Dec 08 '22 01:12

zimdanen


You don't really have a way to cancel the operation. The problem is that this code

               if (pp.DoReplace)
                    results = FindReplace.FindReplace.FindAndReplace(pp.PathToSearch, pp.FindText, pp.ReplaceText, pp.UseRegularExpressions, pp.IncludeList, pp.ExcludeList, pp.RecurseSubdirectories, pp.IgnoreCase);
                else
                    results = FindReplace.FindReplace.Find(pp.PathToSearch, pp.FindText, pp.UseRegularExpressions, pp.IncludeList, pp.ExcludeList, pp.RecurseSubdirectories, pp.IgnoreCase);

doesn't have any way to break once it starts running. So, what winds up happening is that you hit cancel, but the cancel never registers unless you've canceled before the action begins. Once that action is complete, the DoWork method returns successfully and the backgroundworker never triggers the cancellation.

EDIT: If you have a way to break the text up into smaller chunks that can then be "searched and replaced", you could loop through those segments and perform a cancellation check on each loop. You'd need to make sure that you account for the search string being across those break boundaries, though, so it may actually take LONGER to allow for cancellation.

like image 40
saluce Avatar answered Dec 08 '22 00:12

saluce