2012-05-07 7 views
5

Ho un BackgroundWorker che esegue un singolo processo. Voglio essere in grado di annullare l'elaborazione mentre sta andando, ma quando chiamo il metodo CancelAsync(), in realtà non annulla mai. Dove mi sbaglio?Backgroundworker.CancelAsync() non funziona

Ecco il metodo 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; 
      } 
     } 
    } 

Ecco il metodo che inizia il trattamento:

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

Ed ecco 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

hai solo una piccola finestra che controlla CancellationPending. Non lo riscalderai. –

+1

OT ma nel tuo Gestore completato il controllo per e.Error dovrebbe essere il primo. –

risposta

5

In realtà non dispone di un modo per annullare l'operazione. Il problema è che questo codice

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

non ha alcun modo di interrompere una volta avviato. Quindi, ciò che accade è che premi Annulla, ma la cancellazione non si registra mai se non hai annullato prima dell'inizio dell'azione. Una volta completata l'azione, il metodo DoWork restituisce correttamente e il backgroundworker non attiva mai la cancellazione.

EDIT: Se si dispone di un modo per spezzare il testo in blocchi più piccoli che possono poi essere "cercato e sostituito", si potrebbe scorrere quei segmenti ed eseguire un controllo di cancellazione su ogni circuito. Dovresti assicurarti di tenere conto del fatto che la stringa di ricerca si trova al di là di questi limiti di interruzione, quindi, in realtà, potrebbe richiedere PIÙ LUNGO per consentire la cancellazione.

+0

questo è quello di cui avevo paura. Il mio processo di "background" è un processo LUNGO, quindi non ero sicuro di come uccidere quel singolo processo. C'è un modo per essere in grado di farlo? – ganders

+3

Dato che il metodo DoWork fondamentalmente esegue solo un singolo comando, quindi no, non c'è davvero un modo per cancellare quell'unico comando. È possibile controllare solo gli annullamenti tra i comandi (specialmente se si sta eseguendo un ciclo). Potresti, forse, suddividere il testo in segmenti più piccoli, quindi eseguire un ciclo attraverso il FindReplace, controllando la cancellazione su ciascun ciclo. – saluce

+0

Così, suona come io non dovrebbero usare un BackgroundWorker, ma usare solo un thread separato (forse chiamato dal BackgroundWorker così posso ancora riferire il progresso?) Poi, quando la mia si sceglie Annulla, posso chiamare un Thread.Abort (), o qualcosa di simile? È così "intelligente" da fare? – ganders

7

CancelAsync non interrompe effettivamente il thread o altro cosa come che. Invia un messaggio al thread di lavoro che dovrebbe essere annullato tramite BackgroundWorker.CancellationPending. Il delegato DoWork che viene eseguito in background deve controllare periodicamente questa proprietà e gestire la cancellazione stessa.

Leggi tutto here

3

Il codice è giusto, ma se leggere attentamente di nuovo, si vedrà che una volta che il lavoratore sfondo si avvia, ben presto si va al di là del controllo di annullare. Dopodiché, anche se provi a cancellare, non funzionerà più.

si deve ridisegnare la ricerca e sostituzione algoritmo per includere il cancel controllare troppo, in modo da sostenere la cancellazione come si voleva.