Sending Arguments to Background Worker

Sending Arguments To Background Worker?

You start it like this:

int value = 123;
bgw1.RunWorkerAsync(argument: value); // the int will be boxed

and then

private void worker_DoWork(object sender, DoWorkEventArgs e) 
{
int value = (int) e.Argument; // the 'argument' parameter resurfaces here

...

// and to transport a result back to the main thread
double result = 0.1 * value;
e.Result = result;
}

// the Completed handler should follow this pattern
// for Error and (optionally) Cancellation handling
private void worker_Completed(object sender, RunWorkerCompletedEventArgs e)
{
// check error, check cancel, then use result
if (e.Error != null)
{
// handle the error
}
else if (e.Cancelled)
{
// handle cancellation
}
else
{
double result = (double) e.Result;
// use it on the UI thread
}
// general cleanup code, runs when there was an error or not.
}

How can i pass parameters to a background worker?

It has to be passed as a parameter (object) through RunWorkAsync. Be sure to cast it to whatever you passed in.

Private Sub frm_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
bgwThread.RunWorkerAsync('your parameters here')

'e.g.:
Dim sTemp As String = "Hello"
bgwThread.RunWorkerAsync(sTemp)
End Sub

Private Sub bgWorker_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs) Handles bgwThread.DoWork
Dim sThisIsYourParameter As String = CStr(e.Argument)

'...
DoStuff()
End Sub

Although as Hans said above, you can't populate the ListView in another thread. This is just "how to pass a parameter to a backgroundworker."

Passing argument to Backgroundworker error handler

There are lots of different ways to get data back to the RunWorkerCompleted event handler in the case of an exception.

IMHO, the most natural from a semantic point of view is to put the data in the exception itself. For example:

class BackgroundWorkerException : Exception
{
public string Sernum { get; }

public BackgroundWorkerException(string sernum, Exception inner)
: base("DoWork event handler threw an exception", inner)
{
Sernum = sernum;
}
}

Then in your DoWork handler:

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;

try
{
int b = 0; //simulate error
for (int i = 1; i <= 10; i++)
{
if (worker.CancellationPending == true)
{
string[] array2 = { "1", "cancelled" };
e.Result = array2; //passing values when user cancel through e.Result object
e.Cancel = true;
break;
}
else
{
// Perform a time consuming operation and report progress.
worker.ReportProgress(i * 10, "Test a");
int a = 1 / b; //simulate error
System.Threading.Thread.Sleep(1000);

}
string[] array1 = {"1","done"};
e.Result = array1; //passing values when complete through e.Result object
}
}
catch (Exception e)
{
throw new BackgroundWorkerException("1", e);
}
}

Finally, in the RunWorkerCompleted event handler:

private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled == true)
{
string[] someArray2 = e.Result as string[];
string sernum = someArray2[0];
string status = someArray2[1];
resultLabel.Text = sernum + " " + status;
}
else if (e.Error != null)
{
string sernum = ((BackgroundWorkerException)e.Error).Sernum;

resultLabel.Text = "Error: " + e.Error.Message;
}
else
{
string[] someArray = e.Result as string[];
string sernum = someArray[0];
string status = someArray[1];
resultLabel.Text = sernum + " " + status;

}
}

Your question isn't clear about what sernum actually represents, and in particular whether it's a single value for a given background task, or a single task could have more than one value for sernum. If it's the former, i.e. you know when you start the task what the value is, then you could pass it directly to the event handlers by capturing it in an anonymous method used for each actual event handler.

That approach won't work though in your specific scenario without some changes. You appear to have added a single BackgroundWorker object to your form as a component and are reusing it. Using an anonymous method works better/more easily if you are creating a new BackgroundWorker each time, so that you can subscribe your anonymous method delegate to DoWork and RunWorkerCompleted. (You have to subscribe it just before each invocation because, presumably, the sernum value is different each time.)

You could get it to work with the single component added to the form in the Designer as you're doing here, but it's a lot more complicated because you have to dynamically add a handler to the RunWorkerCompleted event which unsubscribes both itself and the delegates you subscribed to the DoWork and RunWorkerCompleted events (you wouldn't subscribe any methods directly to the component in the Designer, in this scheme).

Another alternative is to create a custom data structure passed as the argument for RunWorkerAsync(), which can contain a property for the sernum value. You can set this value in the method that starts the worker, or in the DoWork event handler.

This approach fits only a little better with the component-in-Designer scenario you have, because you still need a way to get the reference to that custom data structure back to the RunWorkerCompleted event handler, which you can do only by storing it in e.g. an instance field that can be shared between the Click event handler that starts the worker and the RunWorkerCompleted event (and frankly, if you do that, at that point it's debatable whether it's even worth it to pass that reference to the RunWorkerAsync() method, since the DoWork event handler could get at the same instance field just as well.)

Another alternative is to catch the exception as I've done in my code example above, but then instead of rethrowing the exception, treat it as if the work was cancelled (i.e. set the Result and Cancel properties).

Yet another approach is to abandon BackgroundWorker altogether and switch to the TPL Task-based idiom. That doesn't solve the problem implicitly, but it allows any of the above options, as well as the option of just defining your own mode for passing errors back.

If you need more specific help than that, you'll need to post a new question, with a good Minimal, Complete, and Verifiable code example that shows which of the above approaches you've attempted to try, or some other alternative not listed here, and what specifically you're unable to figure out.

Passing argument into backgroundWorker (for use as a Cancel button)

First of all, the problem to avoid "cross-thread operation was not valid" is use Invoke on controls. You cannot use a control from a different thread.

About the second issue, I would implement it in the following way. This is a minimum background worker implementation with cancel support.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplication5
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();

// Set the background worker to allow the user to stop the process.
backgroundWorkerStopCheck.WorkerSupportsCancellation = true;
backgroundWorkerStopCheck.DoWork += new DoWorkEventHandler(backgroundWorkerStopCheck_DoWork);
}

private void backgroundWorkerStopCheck_DoWork(object sender, DoWorkEventArgs e)
{
try
{
for (int i = 0; i < 50; i++)
{
if (backgroundWorkerStopCheck.CancellationPending)
{
// user cancel request
e.Cancel = true;
return;
}

System.Threading.Thread.Sleep(100);
}
}
finally
{
InvokeEnableStartButton();
}
}

private void buttonStart_Click(object sender, EventArgs e)
{
//disable start button before launch work
buttonStart.Enabled = false;

// start worker
backgroundWorkerStopCheck.RunWorkerAsync();
}

private void buttonStop_Click(object sender, EventArgs e)
{
// Tell the backgroundWorker to stop process.
backgroundWorkerStopCheck.CancelAsync();
}

private void InvokeEnableStartButton()
{
// this method is called from a thread,
// we need to Invoke to avoid "cross thread exception"
if (this.InvokeRequired)
{
this.Invoke(new EnableStartButtonDelegate(EnableStartButton));
}
else
{
EnableStartButton();
}
}

private void EnableStartButton()
{
buttonStart.Enabled = true;
}
}

internal delegate void EnableStartButtonDelegate();
}

About passing arguments to the worker, you can pass any object in the RunWorkerAsync() method, and its reveived in the backgroundWorkerStopCheck_DoWork method:

  ...
backgroundWorkerStopCheck.RunWorkerAsync("hello");
...

private void backgroundWorkerStopCheck_DoWork(object sender, DoWorkEventArgs e)
{
string argument = e.Argument as string;
// argument value is "hello"
...
}

Hope it helps.

Pass arguments by reference to BackgroundWorker

You can use a lambda for the DoWork event handler to close over variables (note that closures close over variables, not values, so this will not just copy the value). From there you can either do the work right in the lambda, or pass the variable, by reference, to other methods.

int someValue = 5;

BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += (s, args) =>
{
ProcessValue(ref someValue);
};
worker.RunWorkerAsync();

VB.NET: Sending Multiple Arguments To a Background Worker

You can wrap your arguments in an object and then pass that object to the worker.

To retrieve it, you can just cast e in the DoWork to your custom type.

here's an example:

' Define a class named WorkerArgs with all the values you want to pass to the worker.
Public Class WorkerArgs
Public Something As String
Public SomethingElse As String
End Class

Dim myWrapper As WorkerArgs = New WorkerArgs()
' Fill myWrapper with the values you want to pass

BW1.RunWorkerAsync(myWrapper)

' Retrieve the values
Private Sub bgw1_DoWork(sender As Object, e As DoWorkEventArgs)
' Access variables through e
Dim args As WorkerArgs = e.Argument
' Do something with args
End Sub

How can I send arguments(string) to the UI thread with the Background Worker?

The problem is that the ReportProgress method only takes an integer as parameter

Actually there is another ReportProgress method overload that allows you to pass additional arbitrary object which then is accessible via ProgressChangedEventArgs.UserState property.

For instance:

backgroundWorker.ReportProgress(barProgress, "Passed Argument");

and then inside the ProgressChanged event:

progressLabel.Text = e.UserState as string;        
progressLabel.Refresh();


Related Topics



Leave a reply



Submit