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);
}
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
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.
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