title: How To Stop A BackgroundWorker With For Loop C# WinForms
body: <p>I'm writing an app that generates serial numbers. The user will choose a number of serials to generate, along with optional activation keys and hashcodes, and then click a 'Start' button. There is also a 'Stop' button that the user can click at any time to stop the backgroundworker, where the serials are generated. If the user clicks the 'Stop' button before the total number of serials is completed, serial generation stops and the user is prompted to save the serials generated so far or discard them. The user can then save their work and take up where they left off later on. The problem, which is a confusing one for a lot of us, is figuring out how to safely stop the backgroundworker in which the serial generation code is executing.</p>
  <p>Here's the code for my background worker...</p>
  <pre><code>    BwGetGUIDSerials.WorkerSupportsCancellation = true;
      BwGetGUIDSerials.WorkerReportsProgress = true;
      BwGetGUIDSerials.DoWork += BwGetGUIDSerials_DoWork;
      BwGetGUIDSerials.ProgressChanged += BwGetGUIDSerials_ProgressChanged;
      BwGetGUIDSerials.RunWorkerCompleted += BwGetGUIDSerials_RunWorkerCompleted;
  </code></pre>
  <p>And here's my code, listed in the order in which it is executed...</p>
  <pre><code>private void BtnGetMySerials_Click(object sender, EventArgs e)
  {
   
  // Let's create a list to send to the background worker, where our serials will be generated...
  List&lt;object&gt; Stuff = new List&lt;object&gt;
  {
  intQuantity,                          // The quantity of serials we want.
  OurProject.SerialInfo.SerialType,     // The type of serials we want.
  Prefix,                               // The serial prefix if there is one ( e.g. &quot;MyAppv10&quot;).
  OurSubGroups,                         // The list of subgroups we want in each serial (subgroups separted by hyphens).
  OurProject.HashCode,                  // Whether or not to generate a hash code for each serial.
  OurProject.Serials.Count,             // The serials list, in case we have already generated serials
  // and want to add more.
  OurProject.SerialInfo.IncludeActKey,  // Whether or not we want to generate an activation keys for each serial.
  OurProject.SerialInfo.ActKeyGroups    // The number of subgroups in the activation keys, also separated by hyphens. 
  };
  
  // Finally, we run the background worker, sending the above 'Stuff' list as a parameter...
  BwGetGUIDSerials.RunWorkerAsync(Stuff);
  }
  }
  
  private void BtnStopSerialGeneration_Click(object sender, EventArgs e)
  {
  // Stop serial generation immediately...
  if (BwGetGUIDSerials.IsBusy)
  BwGetGUIDSerials.CancelAsync();
  BtnStopSerialGen.Enabled = false;
  BtnExportSerials.Enabled = BtnCheck4Dupes.Enabled = BtnClearSerials.Enabled = true;
  
  // Notify the user that serial generation has been stopped as requested and
  // ask if user wants to keep the already generated serials...
  DialogResult Res = MessageBox.Show(&quot;Serial generation aborted.&quot; + &quot;\n\n&quot; + &quot;Do you want to keep the &quot; + OurProject.Serials.Count.ToString() + &quot; serials already generated?&quot;,
  &quot;Serial Generation Aborted&quot;, MessageBoxButtons.YesNo);
  if (Res == DialogResult.No)
  {
  BtnClearSerials.PerformClick();
  }
  }
  
  private void BwGetGUIDSerials_DoWork(object sender, DoWorkEventArgs e)
  {
  // This is the background worker where the serials are generated.
  // First, let's unpack the List&lt;object&gt; that got sent...
  List&lt;object&gt; SentStuff = e.Argument as List&lt;object&gt;;
  int quantity = Convert.ToInt32(SentStuff[0]);
  string serialType = SentStuff[1].ToString();
  string Prefix = SentStuff[2].ToString(); 
  List&lt;int&gt; OurSubGroups = SentStuff[3] as List&lt;int&gt;;
  bool HashCode = (bool)SentStuff[4];
  int CountSoFar = Convert.ToInt32(SentStuff[5]);  
  string ActKeyType = string.Empty;
  List&lt;int&gt; ActKeyGroups = new List&lt;int&gt;();
  bool IncludeActKey = (bool)SentStuff[6];
  
  // Create a serial list to store the serials...
  List&lt;SerialNumber&gt; OurSerials = new List&lt;SerialNumber&gt;();
  var watch = System.Diagnostics.Stopwatch.StartNew();
  for (int i = 0; i &lt; quantity; i++)
  {
  Thread.Sleep(5);
  if (!BwGetGUIDSerials.CancellationPending)
  {
  SerialNumber serial = new SerialNumber();
  do      // Generate a serial...
  {
  if (!string.IsNullOrEmpty(Prefix) &amp;&amp; Prefix != &quot;null&quot;)   
  serial.Serial = Prefix + &quot;-&quot;;
  for (int j = 0; j &lt; OurSubGroups.Count; j++)
  {
  serial.Serial += MakeGUIDSerial(OurSubGroups[j]);
  if (j &lt; OurSubGroups.Count - 1)
  serial.Serial += &quot;-&quot;;
  }
  if (HashCode)
  serial.HashCode = GetTheHashCode(serial.Serial);
  else
  serial.HashCode = 0;
  if (IncludeActKey)
  {
  Thread.Sleep(5);
  int Grp1 = -1;
  int Grp2 = -1;
  if (ActKeyGroups[0] &gt;= 1)
  Grp1 = ActKeyGroups[0];
  if (ActKeyGroups[1] &gt;= 1)
  Grp2 = ActKeyGroups[1];
  serial.ActKey = GetActKey(Grp1, Grp2);
  }
  if (IsThisADupe(serial))
  {
  dupes++;
  }
  }
  while (OurSerials.Contains(serial));        
  OurSerials.Add(serial);
  BwGetGUIDSerials.ReportProgress(q, serial);
  q++;
  }
  else
  {
  e.Cancel = true;
  }
  }  
  // Done! Now let's stop the watch and get the total time...
  watch.Stop();
  TimeSpan ts3 = watch.Elapsed;
  string elapsedTime3 = String.Format(&quot;{0:00}:{1:00}:{2:00}.{3:00}&quot;, ts3.Hours, ts3.Minutes, ts3.Seconds, ts3.Milliseconds / 10);
  
  // Finally, let's pack up the end results and send it all to the RunWorkerCompleted...
  List&lt;object&gt; OurResult = new List&lt;object&gt;
  {
  elapsedTime3,
  quantity,
  dupes
  };
  e.Result = OurResult;
  }
  
  private void BwGetGUIDSerials_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
  {
      // We're done generating GUID-based serials.
  
      // Reset the BtnGetMySerials button to 'Pause'.
      List&lt;object&gt; SentList = e.Result as List&lt;object&gt;;
      string Time = SentList[0].ToString();
      int TotalGenerated = Convert.ToInt32(SentList[1]);
      int Dupes = Convert.ToInt32(SentList[2]);
      List&lt;SerialNumber&gt; SentSerials = SentList[3] as List&lt;SerialNumber&gt;;
  
      BtnStopSerialGen.Enabled = false;
      BtnExportSerials.Enabled = BtnCheck4Dupes.Enabled = BtnClearSerials.Enabled = BtnGetMySerials.Enabled = true;
      StringBuilder sb = new StringBuilder();
      if (Dupes &gt; 0)
          CheckAgain4Dupes(Time);
  
      if (e.Cancelled)
      {
          // Notify the user that serial generation has been stopped as requested and
          // ask if user wants to keep the already generated serials...
          DialogResult Res = MessageBox.Show(&quot;Serial generation aborted.&quot; + &quot;\n\n&quot; + &quot;Do you want to keep the &quot; +
                                                  OurProject.Serials.Count.ToString() + &quot; serials already generated?&quot;,
                                                  &quot;Serial Generation Aborted&quot;, MessageBoxButtons.YesNo);
          if (Res == DialogResult.No)
          {
              BtnClearSerials.PerformClick();
          }
      }
      else
          // Notify user of the results...
          sb.Append(&quot;Total Serials Generated: &quot; + SentSerials.Count.ToString(&quot;N0&quot;) + 
                            &quot;\n\n&quot; + &quot;Time Elapsed: &quot; + Time);
  
      if (SentSerials.Count &gt; 0)
      {
          if (SentSerials.Count == OurProject.SerialInfo.Quantity)  
          {
              OurProject.Serials = SentSerials;
              LblTotalSerials.Text = &quot;Total Serials: &quot; + LvSerials.Items.Count.ToString(&quot;N0&quot;);
          }
      }
      PrgLongProgress.Value = PrgLongProgress.Maximum = 0;
      PrgLongProgress.Visible = false;
  }
  </code></pre>
  <p>The code above throws the following exception, though it doesn't say exactly where the error happens...</p>
  <pre><code>System.Reflection.TargetInvocationException
  HResult=0x80131604
  Message=Exception has been thrown by the target of an invocation.
  Source=mscorlib
  StackTrace:
  at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
  at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)
  at System.Delegate.DynamicInvokeImpl(Object[] args)
  at System.Windows.Forms.Control.InvokeMarshaledCallbackDo(ThreadMethodEntry tme)
  at System.Windows.Forms.Control.InvokeMarshaledCallbackHelper(Object obj)
  at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
  at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
  at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
  at System.Windows.Forms.Control.InvokeMarshaledCallback(ThreadMethodEntry tme)
  at System.Windows.Forms.Control.InvokeMarshaledCallbacks()
  at System.Windows.Forms.Control.WndProc(Message&amp; m)
  at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message&amp; m)
  at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message&amp; m)
  at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
  at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG&amp; msg)
  at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 reason, Int32 pvLoopData)
  at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
  at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
  at System.Windows.Forms.Application.Run(Form mainForm)
  at Serializerv10.Program.Main(String[] Args) in D:\Documents\VS 2022 Projects\AAAAAAA Serializer Projects\Serializer 1.0 Alt End\Serializer v10\Program.cs:line 24
  
  This exception was originally thrown at this call stack:
  System.ComponentModel.AsyncCompletedEventArgs.RaiseExceptionIfNecessary()
  System.ComponentModel.RunWorkerCompletedEventArgs.Result.get()
  Serializerv10.FrmMain.BwGetGUIDSerials_RunWorkerCompleted(object, System.ComponentModel.RunWorkerCompletedEventArgs) in FrmMain.cs
      System.ComponentModel.BackgroundWorker.OnRunWorkerCompleted(System.ComponentModel.RunWorkerCompletedEventArgs)
  System.ComponentModel.BackgroundWorker.AsyncOperationCompleted(object)
  
  Inner Exception 1:
  InvalidOperationException: Operation has been cancelled.
  </code></pre>
  <p>I've looked at lots of code examples for how to stop a background worker correctly, but in my case, I'm not quite sure where the problem is occuring. Obviously, I'm not stopping the background worker correctly. Is my &quot;e.Cancel = true;&quot; in the right place or what? Would it be better to create a for loop in which the background worker runs over and over again until it completes or the user kills it?</p>