BackgroundWorker not starting again when once finished - c#

I have a WinForms application which uses a backgroundworker for downloading images from given urls. For the download I use a backgroundworker.
The application is running fine when started, and the download happens as planned, but when the worker is done and I click the downloadbutton again to start downloading from another url, the backgroundworker doesn't do anything.
I fixed that problem temporarily by calling application.restart() when the worker is done, which works but can't be here longer than it has to.
Worker-Code:
// initialization of worker is done in constructor of my class
downloadWorker.WorkerReportsProgress = true;
downloadWorker.WorkerSupportsCancellation = true;
downloadWorker.DoWork += new DoWorkEventHandler(worker_doWork);
downloadWorker.ProgressChanged += new ProgressChangedEventHandler(worker_progressChanged);
downloadWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_runWorkerCompleted);
// ...
private void worker_doWork(object sender, DoWorkEventArgs e)
{
WebClient downloadClient = new WebClient();
HttpWebRequest HttpReq = (HttpWebRequest)WebRequest.Create(url);
HttpWebResponse response;
try
{
response = (HttpWebResponse)HttpReq.GetResponse();
}
catch (WebException ex)
{
response = (HttpWebResponse)ex.Response;
}
if (response.StatusCode == HttpStatusCode.NotFound)
MessageBox.Show("Website not found");
if (response.StatusCode == HttpStatusCode.OK)
{
for(int i=0; i<3;i++)
{
string image = getImageUrl(url,i);
downloadWorker.ReportProgress(i);
image = WebUtility.HtmlDecode(image);
string saveName = "img_"+i+".png";
try
{
downloadClient.DownloadFile(image, saveName);
}
catch (Exception ex)
{
MessageBox.Show(ex.StackTrace);
}
}
}
}
private void worker_progressChanged(object sender, ProgressChangedEventArgs e)
{
rtxtStatus.AppendText("Downloade Image" + e.ProgressPercentage + " of 3");
}
private void worker_runWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("Download completed");
}
edit:
if (e.Error != null)
{
MessageBox.Show(e.Error.ToString());
}
To avoid any misunderstandings: The backgroundWorker is definetely running at the second time, and it is not an error of the reportProgress-method, since I get the same thing when I dont report anything.
edit no. 2:
I found out where the error came from: at the second run, the for-loop is completely skipped. But that doesn't make any sense for me either... There can't be any other value still be in because I have a completely new instance of the class, can it? But anyway, if it just skipped the method the worker still should exit which it doesn't do. For testing, I added a MessageBox after the for-loop, which is not executed after the second run.

Related

C# Background Worker Append TextBox

first off I'd like to say I'm brand new to C# so I am not too aware with how the background worker is supposed to be implemented. I have a GUI program that basically pings a domain a returns the response to a textbox. I am able to get it to work normally, however, it freezes the code because it is running on the same thread which is why I am trying to implement a background worker.
Here is the basic setup
private void button1_Click(object sender, EventArgs e)
{
url = textBox1.Text;
button1.Enabled = false;
button2.Enabled = true;
bgWorker.DoWork += new DoWorkEventHandler(bgWorker_DoWork);
bgWorker.RunWorkerAsync();
}
private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
do
{
if (bgWorker.CancellationPending)
break;
Invoke((MethodInvoker)delegate { monitor(); });
} while (true);
}
public void monitor()
{
textBox2.AppendText("Status of: " + url + "\n");
Status(url);
System.Threading.Thread.Sleep(30000);
}
private void Status(string url)
{
// This method does all the ping work and also appends the status to the Text box as it goes through , as OK or down
}
I have not worked with bgworkers before and as you can imagine it's confusing. I've looked at tons of other articles and I can't seem to get it. Sorry if the code looks crazy, I'm trying to learn.
Use Microsoft's Reactive Framework (NuGet "System.Reactive.Windows.Forms" and add using System.Reactive.Linq;) and then you can do this:
private void button1_Click(object sender, EventArgs e)
{
var url = textBox1.Text;
Observable
.Interval(TimeSpan.FromMinutes(0.5))
.SelectMany(_ => Observable.Start(() => Status(url)))
.ObserveOn(this)
.Subscribe(status => textBox2.AppendText("Status of: " + status + "\n"));
}
You then just need to change Status to have this signature: string Status(string url).
That's it. No background worker. No invoking. And Status is nicely run on a background thread.
You've got several mistakes. First,
Invoke((MethodInvoker)delegate
{
monitor();
});
will call monitor() on your UI thread. In almost all cases you should not call methods on other threads. You especially should not call methods that block or do anything that takes more than a few milliseconds on your UI thread, and that is what this does:
System.Threading.Thread.Sleep(30000);
Instead of calling a method on another thread; submit immutable data to the other thread and let the thread decide when to handle it. There is an event already built in to BackgroundWorker which does that. Before you call bgWorker.RunWorkerAsync() do this:
url = new Uri(something);
bgWorker.WorkerReportsProgress = true;
bgWorker.WorkerSupportsCancellation = true;
bgWorker.ProgressChanged += Bgw_ProgressChanged;
private void Bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
textBox2.AppendText("Status of: " + url + ": " + e.UserState.ToString()
+ Environment.NewLine);
}
Your bgWorker_DoWork should look more like this:
void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
while (!bgw.CancellationPending)
{
System.Threading.Thread.Sleep(new TimeSpan(0, 0, 30));
var status = ResultOfPing(e.Argument as Uri);
bgw.ReportProgress(0, status);
}
e.Cancel = true;
}
and you should call it like this:
bgWorker.RunWorkerAsync(url);
You've got a second problem. BackgroundWorker creates a thread, and your thread is going to spend most of its time blocked on a timer or waiting for network responses. That is a poor use of a thread. You would be better off using completion callbacks or async/await.
The background worker is running on a thread pool thread, but your call to Status and Sleep is running on the UI thread. You need to move that stuff back into bgWorker_DoWork.
Try this code:
public partial class Form1 : Form
{
bool cancel;
public Form1()
{
InitializeComponent();
}
public void StartPinging()
{
this.cancel = false;
startButton.Enabled = false;
stopButton.Enabled = true;
responseBox.Clear();
responseBox.AppendText("Starting to ping server.");
responseBox.AppendText(Environment.NewLine);
var bw = new BackgroundWorker
{
WorkerReportsProgress = false,
WorkerSupportsCancellation = true
};
bw.DoWork += (obj, ev) =>
{
while (!cancel)
{
// Ping Server Here
string response = Server.PingServer();
this.Invoke(new UiMethod(() =>
{
responseBox.AppendText(response);
responseBox.AppendText(Environment.NewLine);
}));
}
};
bw.RunWorkerCompleted += (obj, ev) =>
{
this.Invoke(new UiMethod(() =>
{
responseBox.AppendText("Stopped pinging the server.");
responseBox.AppendText(Environment.NewLine);
startButton.Enabled = true;
stopButton.Enabled = false;
}));
};
bw.RunWorkerAsync();
}
delegate void UiMethod();
private void startButton_Click(object sender, EventArgs e)
{
StartPinging();
}
private void stopButton_Click(object sender, EventArgs e)
{
responseBox.AppendText("Cancelation Pressed.");
responseBox.AppendText(Environment.NewLine);
cancel = true;
}
}
public class Server
{
static Random rng = new Random();
public static string PingServer()
{
int time = 1200 + rng.Next(2400);
Thread.Sleep(time);
return $"{time} ms";
}
}
Erwin, when dealing with C# - threads and UI elements usually you will come across cross-thread operations i.e. Background thread with UI threads. This interaction needs to be done in thread safe way with the help of Invoke to avoid invalid operations.
Please look into below resource: InvokeRequired section.
https://learn.microsoft.com/en-us/dotnet/framework/winforms/controls/how-to-make-thread-safe-calls-to-windows-forms-controls

How to update a view without waiting for another operation to finish

lets say I have a GroupBox with several Labels. In these Labels, various IP-related information are displayed. One info is the external IP address of the machine.
string externalIP = "";
try
{
WebRequest request = WebRequest.Create("http://checkip.dyndns.org/");
request.Timeout = 3000;
System.Threading.Tasks.Task<System.Net.WebResponse> response = request.GetResponseAsync();
using (StreamReader stream = new StreamReader(response.Result.GetResponseStream()))
{
if (response.Result.ContentLength != -1)
{
externalIP = stream.ReadToEnd();
}
}
}
catch (Exception e)
{
externalIP = "Error.";
}
if (externalIP == "")
{
return "No service.";
}
else
{
return externalIP = (new Regex(#"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}")).Matches(externalIP)[0].ToString();
}
This method is called from following code:
private void updateNetworkIP()
{
string ip4e = "External IPv4: " + getExternalIPv4();
lblIP4external.Text = ip4e;
//Get some more info here.
}
How do I execute the code after getExternalIPv4() even when it's not finished yet? It works when setting a TimeOut like I did above but sometimes the request just takes a little longer but still completes successfully. So I want to still be able to display the external IP but continue to execute the other methods for refreshing the GroupBox.
The BackgroundWorker will deliver what you are after. Sample code:
BackgroundWorker bg = new BackgroundWorker();
bg.DoWork += new DoWorkEventHandler(getExternalIPv4Back);
bg.RunWorkerCompleted += new RunWorkerCompletedEventHandler(writeLabel);
bg.RunWorkerAsync();
//The code below this point will be executed while the BackgroundWorker does its work
You have to define getExternalIPv4Back as a DoWork Event Method and include inside it the code to be executed in parallel; also writeLabel as a RunWorkerCompleted Event(required to edit the label without provoking muti-threading-related errors). That is:
private void getExternalIPv4Back(object sender, DoWorkEventArgs e)
{
IP = "External IPv4: " + getExternalIPv4(); //IP -> Globally defined variable
}
private void writeLabel(object sender, RunWorkerCompletedEventArgs e)
{
  lblIP4external.Text = IP;
}

how to create loading window while web conncetion is checking in windows form app?

I have a test web connection form in c#. I want to show a loading window while my connection is being checked, and then show the result of checking.
This is my code for testing the web connection:
public bool ConnectionAvailable(string strServer)
{
try
{
HttpWebRequest reqFP = (HttpWebRequest)HttpWebRequest.Create(strServer);
HttpWebResponse rspFP = (HttpWebResponse)reqFP.GetResponse();
if (HttpStatusCode.OK == rspFP.StatusCode)
{
// HTTP = 200 - Internet connection available, server online
rspFP.Close();
return true;
}
else
{
// Other status - Server or connection not available
rspFP.Close();
return false;
}
}
catch (WebException)
{
// Exception - connection not available
return false;
}
}
And this:
private void button1_Click(object sender, EventArgs e)
{
string url = "Web-url";
label1.Text = "Checking ...";
button1.Enabled = false;
if (ConnectionAvailable(url))
{
WebClient w = new WebClient();
w.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
label1.Text = w.UploadString(url, "post", "SN=" + textBox1.Text);
button1.Enabled = true;
}
else
{
label1.Text = "Conntion fail";
button1.Enabled = true;
}
}
On a windows forms application the user interface runs on one thread, if you try to run a long running process, which checking the web connection might end up being this will cause the form to freeze until it completes the work.
So, I'd start a new thread that does the check. then raise an event to return the result. while all that's happening you can do what you like with the user interface, such as a loading graphic, or even allow the user to continue using features that don't require the internet connection.
Create EventArgs class of your own so you can pass back the result:
public class ConnectionResultEventArgs : EventArgs
{
public bool Available { get; set; }
}
Then in your form class, create your event, handlers and the method to action when the event arrives
//Create Event and Handler
public delegate void ConnectionResultEventHandler(object sender, ConnectionResultEventArgs e);
public event ConnectionResultEventHandler ConnectionResultEvent;
//Method to run when the event has been receieved, include a delegate in case you try to interact with the UI thread
delegate void ConnectionResultDelegate(object sender, ConnectionResultEventArgs e);
void ConnectionResultReceived(object sender, ConnectionResultEventArgs e)
{
//Check if the request has come from a seperate thread, if so this will raise an exception unless you invoke.
if (InvokeRequired)
{
BeginInvoke(new ConnectionResultDelegate(ConnectionResultReceived), new object[] { this, e });
return;
}
//Do Stuff
if (e.Available)
{
label1.Text = "Connection Good!";
return;
}
label1.Text = "Connection Bad";
}
Subscribe to the event when your form loads:
private void Form1_Load(object sender, EventArgs e)
{
//Subscribe to the the results event.
ConnectionResultEvent += ConnectionResultReceived;
}
and then setup the worker thread:
//Check the connection
void BeginCheck()
{
try
{
HttpWebRequest reqFP = (HttpWebRequest)HttpWebRequest.Create("http://google.co.uk");
HttpWebResponse rspFP = (HttpWebResponse)reqFP.GetResponse();
if (HttpStatusCode.OK == rspFP.StatusCode)
{
// HTTP = 200 - Internet connection available, server online
rspFP.Close();
ConnectionResultEvent(this, new ConnectionResultEventArgs {Available = true});
}
else
{
// Other status - Server or connection not available
rspFP.Close();
ConnectionResultEvent(this, new ConnectionResultEventArgs { Available = false });
}
}
catch (WebException)
{
// Exception - connection not available
//Raise the Event - Connection False
ConnectionResultEvent(this, new ConnectionResultEventArgs { Available = false });
}
}
private void button1_Click(object sender, EventArgs e)
{
//loading graphic, screen or whatever
label1.Text = "Checking Connection...";
//Begin the checks - Start this in a new thread
Thread t = new Thread(BeginCheck);
t.Start();
}
I am thinking of threading! One thread checks the connection while the other one is showing the loading window. If for example the connection has been established you can notify the other thread and show the result.

HttpWebRequest making duplicate request?

I'm running my program as a windows service and I'm trying to send a HTTP request everytime the time elapsed(i've set to 1 minute). What I'm trying to do at the server side is just writing a value that it gets from the query string. The writing to file works but i noticed there is some duplicate values being sent?
protected override void OnStart(string[] args)
{
try
{
eventLog1.WriteEntry("In OnStart, this is another new build 016");
timer1 = new System.Timers.Timer(5000D);
timer1.AutoReset = true;
timer1.Elapsed += new ElapsedEventHandler(timer_Elapsed);
timer1.Start();
eventLog1.WriteEntry("This is after calling start method");
}
catch (Exception exxx)
{
eventLog1.WriteEntry(exxx.Data.ToString());
}
}
protected override void OnStop()
{
eventLog1.WriteEntry("In onStop.");
}
private static void timer_Elapsed(object source, ElapsedEventArgs e)
{
timer1.Stop();
el.WriteEntry("The Elapsed event was raised at " + i);
i++;// i is initialized to 0
request = (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create("http://www.example.com/Test.php?test=" + i);
request.Method = "GET";
request.Timeout = 5000;
try
{
request.BeginGetResponse(new AsyncCallback(FinishWebRequest), request);
}
catch (System.Net.WebException e1)
{
el.WriteEntry("Exception 1:" + e1.Message);
}
catch (System.Net.ProtocolViolationException e2)
{
el.WriteEntry("Exception 2:" + e2.Message);
}
catch (System.InvalidOperationException e3)
{
el.WriteEntry("Exception 3:" + e3.Message);
}
timer1.Start();
}
private static void FinishWebRequest(IAsyncResult result)
{
request.GetResponse().Close();
}
What i noticed in my file is something like 1,2,1,1,3,2,2,1,1. I don't see anything wrong with my code. Is it possible that the HttpWebRequest is sending duplicate request?
In my opinion code is correct.I think you are using i variable somewhere else also.
Try changing i to something else which is not so common
HttpWebRequest never sends same request duplicate request.
For an instance let us assume that HttpRequest are duplicated then output should be something like 1,1,1,1,2,2,2,3.....

How to download files in a blocking/synchronous manner?

I am pretty new to silverlight and was very surprised to see that only asynchronous file downloading can be done. Well, I've attempted to counter act this by just setting a flag and waiting on it to change..
This is my simple code
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
WebClient webClient = new WebClient();
webClient.DownloadProgressChanged +=
new DownloadProgressChangedEventHandler(webClient_DownloadProgressChanged);
webClient.OpenReadCompleted += new OpenReadCompletedEventHandler(webClient_OpenReadCompleted);
webClient.OpenReadAsync(new Uri("/trunk/internal/SilverLightInterface.ashx?xxid=XXX", UriKind.Relative));
while (XmlStateStream == null) { }
lblProgress.Content = "Done Loading";
}
void webClient_DownloadProgressChanged(object sender,
DownloadProgressChangedEventArgs e) {
lblProgress.Content = "Downloading " + e.ProgressPercentage + "%";
}
volatile Stream XmlStateStream = null;
void webClient_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
if (e.Error != null)
{
lblProgress.Content = "Error: " + e.Error.Message;
return;
}
XmlStateStream = e.Result;
}
This is causing Firefox to actually freeze up(which is extremely annoying when I'm doing other things while developing) (btw, kudos to firefox cause I tested it and firefox froze, but I didn't lose what I was typing here after restoring)
I don't understand why the while(XmlStateStream==null){} is causing a freeze up. Is there some attribute for locks or volatile(other than what I already have) or am I in the wrong part of the Silverlight page lifecycle or something?
I'm really confused as to why this is not working.
Also, this is silverlight 3.0
Most likely, this code is running in the UI thread that handles all of the web browser's interaction with the user. This is why you won't find any blocking operations - because anything that blocks will freeze the UI in exactly the same way that you saw! What's more, if the UI thread also handles network IO (which is common), then you'll deadlock here because the asynchronous operation you're waiting on will never finish.
I'm afraid you'll just have to rewrite your code as a state machine driven by asynchronous operations.
Whilst you need to get with the asynchronous nature of things in Silverlight you can use C# 3 syntax to keep things a bit more together:-
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
DownloadXmlStateStream();
}
void DownloadXmlStateStream()
{
WebClient webClient = new WebClient();
webClient.DownloadProgressChanged += (s, e) => {
lblProgress.Content = "Downloading " + e.ProgressPercentage + "%";
}
webClient.OpenReadCompleted += (s, e) => {
if (e.Error != null)
{
lblProgress.Content = "Error: " + e.Error.Message;
}
else
{
XmlStateStream = e.Result;
lblProgress.Content = "Done Loading";
}
}
webClient.OpenReadAsync(new Uri("/trunk/internal/SilverLightInterface.ashx?xxid=XXX", UriKind.Relative));
}
You need to use the DownloadFileCompleted event.
delete this:
while (XmlStateStream == null) { }
lblProgress.Content = "Done Loading";
add this:
webClient.DownloadFileCompleted +=
new DownloadFileCompletedEventHandler(webClient_DownloadFileCompleted);
and this:
void webClient_DownloadFileCompleted(object sender, AsyncCompletedEventHandler) {
lblProgress.Content = "Done Loading";
}
If you really must have synchronous downloading, you need to "poll" for the download being complete less often. Try calling System.Threading.Thread.Sleep() with a delay of 50-250ms from within your busy-wait loop.
Although this will reduce the wasteful CPU utilization of your code, it's possible that it will not fix the UI responsiveness problem. It depends on whether the thread that's calling your MainPage_Loaded is the only one that can call UI update events. If that's the case, then the UI simply can not update until that handler returns.
By blocking until the file is downloaded, you're not only blocking the UI thread of your silverlight app - you're also blocking the UI thread of the browser, it would seem.
All you really want to do (I presume) is stop your app doing anything until the download completes. Try adding this line to MainPage_Loaded:
LayoutRoot.IsHitTestVisible = false;
Remove your blocking while loop and the completed message (the last two lines).
In webClient_OpenReadCompleted, add:
LayoutRoot.IsHitTestVisible = true;
lblProgress.Content = "Done Loading.";
And everything should work the way I think you want.
Here's the full code:
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
WebClient webClient = new WebClient();
webClient.DownloadProgressChanged +=
new DownloadProgressChangedEventHandler(webClient_DownloadProgressChanged);
webClient.OpenReadCompleted += new OpenReadCompletedEventHandler(webClient_OpenReadCompleted);
webClient.OpenReadAsync(new Uri("/trunk/internal/SilverLightInterface.ashx?xxid=XXX", UriKind.Relative));
LayoutRoot.IsHitTestVisible = false;
}
void webClient_DownloadProgressChanged(object sender,
DownloadProgressChangedEventArgs e) {
lblProgress.Content = "Downloading " + e.ProgressPercentage + "%";
}
void webClient_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
if (e.Error != null)
{
lblProgress.Content = "Error: " + e.Error.Message;
return;
}
LayoutRoot.IsHitTestVisible = true;
lblProgress.Content = "Done Loading.";
}

Categories

Resources