2012-05-07 10 views
5

Tengo un backgroundworker que ejecuta un solo proceso. Quiero poder cancelar el procesamiento mientras está en marcha, pero cuando llamo al método CancelAsync(), en realidad nunca cancela. ¿Dónde estoy equivocado?Backgroundworker.CancelAsync() no funciona

Aquí está el método DoWork():

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

Aquí es el método que se inicia el procesamiento:

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

y aquí está la WorkerCompleted() evento:

 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); 
    } 
+1

Solo tiene una ventana muy pequeña que comprueba CancelaciónPending. No lo calentarás. –

+1

OT pero en su manejador Completo el cheque para e.Error debe ir primero. –

Respuesta

5

En realidad, no tiene manera de cancelar la operación. El problema es que este código

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

no tiene ninguna forma de romperse una vez que comienza a funcionar. Entonces, lo que termina sucediendo es que pulses cancelar, pero la cancelación nunca se registra a menos que hayas cancelado antes de que comience la acción. Una vez que se completa esa acción, el método DoWork vuelve exitosamente y el backgroundworker nunca desencadena la cancelación.

EDIT: Si tiene una forma de dividir el texto en trozos más pequeños que luego pueden "buscarse y reemplazarse", puede recorrer esos segmentos y realizar una comprobación de cancelación en cada ciclo. Sin embargo, deberá asegurarse de que la cadena de búsqueda se encuentre dentro de los límites de ruptura, por lo que en realidad puede tomar más tiempo permitir la cancelación.

+0

eso es lo que temía. Mi proceso de "fondo" es un proceso LARGO, así que no estaba seguro de cómo matar ese único proceso. ¿Hay alguna manera de poder hacer eso? – ganders

+3

Dado que su método DoWork básicamente solo ejecuta un solo comando, entonces no, realmente no hay una forma de cancelar ese comando. Solo puede verificar cancelaciones entre comandos (especialmente si está bucleando). Podría, tal vez, dividir el texto en segmentos más pequeños, luego recorrer el 'FindReplace', verificando la cancelación en cada ciclo. – saluce

+0

Entonces, parece que no debería estar usando un backgroundworker, pero use solo un hilo separado (tal vez llamado desde el backgroundworker para que pueda informar el progreso?) Luego, cuando se hace clic en mi cancelación, puedo llamar un thread.Abort (), ¿o algo así? ¿Es eso "inteligente" de hacer? – ganders

7

CancelAsync en realidad no aborta su hilo o cualquier cosa como eso. Envía un mensaje al hilo de trabajo que debe ser cancelado a través de BackgroundWorker.CancellationPending. El delegado de DoWork que se está ejecutando en segundo plano debe comprobar periódicamente esta propiedad y manejar la cancelación en sí.

Leer más here

3

Su código es correcto, pero si lo vuelve a leer cuidadosamente, verá que una vez que se inicia el trabajador en segundo plano, pronto va más allá del control de cancelación. Después de eso, incluso si intentas cancelar, ya no funcionará.

Debe rediseñar su algoritmo de búsqueda y reemplazo para incluir también la verificación de cancelación, a fin de admitir la cancelación como desee.