Accessing controls through threads - c#

I use WebBrowser Control in c# to check a couple search engine results but sometime for no reason it gets stuck. My first thought was to make each searching function as a thread and use thread.abort() from a timer but I just couldn't handle with the UI Controls (including the WebBrowser) no matter what I've tried.
Anyone has a solution for me? an example would be great cause I already tried so many things and I keep getting all these exceptions.

I believe you can use WebRequest in a Background worker & avoid the difficulties of dealing with the COM threading model. You can use WebRequest.Timeout to handle anything you think is taking too long.
Try something like this:
static void Main(string[] args)
{
BackgroundWorker bg = new BackgroundWorker();
bg.DoWork += backgroundWorker_DoWork;
bg.RunWorkerAsync(new List<object> { "http://download.thinkbroadband.com/512MB.zip" });
while (true) {}
}
private static void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
Console.WriteLine("Starting operation.");
BackgroundWorker bw = sender as BackgroundWorker;
List<object> args = (List<object>)e.Argument;
var url = (string)args[0];
WebRequest request = WebRequest.Create(url);
request.Timeout = 300;
try
{
WebResponse response = request.GetResponse();
Console.WriteLine("Request successful.");
}
catch (Exception ex)
{
Console.WriteLine("Request timed out.");
}
}
WebBrowser is a component you should use when you want to embed an instance of IE into your presentation layer. Unless you need this, you can use something more lightwweight.

Use the control's .Invoke() method: http://msdn.microsoft.com/en-us/library/zyzhdc6b.aspx
There are other methods you can use too: .BeginInvoke and .EndInvoke for asynchronous invokes.
if(control.InvokeRequired)
{
control.Invoke(control.delegate);
}

Related

Which to use, WebClient.OpenRead, OpenReadAsync or OpenReadTaskAsync

I'm trying to understand the differences among WebClient.OpenRead, WebClient.OpenReadAsync and WebClient.OpenReadTaskAsync.
It looks like these have differences regarding blocking a thread, but I don't understand it well.
Could you please explain the differences? It would be great if you could give me some example (examples don't have to be sample code, but would be great if you could provide)
As you said, the difference is in thread blocking behavior. First one (OpenRead()) is thread blocking operation, other two - not. For example, let assume, that your network latency to reach google.com is 300ms. When you do var stream = webClient.OpenRead(#"https://www.google.com"); your application is "paused" for this 300ms, so code next to this line is not executed until your webClient return a stream to stream variable. This is calling Thread blocking.
When you do this in UI-thread (in example: in a button click handler) - your application become freezing and not responding to user actions. This is bad user experience, so never-ever call a thread blocking stuff in your UI. Here is example for console application:
var address = #"https://www.google.com/";
Console.WriteLine($"Opening stream from {address}");
using (var stream = webClient.OpenRead(address)) // this will block for 300ms
{
Console.WriteLine("Stream is open!");
// some other code
}
Second method (OpenReadAsync()) is asynchronous and return nothing immediately after a call, so your thread is not blocked. After awhile (300ms) OpenReadCompleted event will be raised by your webClient and all attached listeners will handle opened stream one-by-one. Here is an example:
public partial class MainForm : Form
{
private WebClient _client = new WebClient();
public MainForm()
{
InitializeComponents();
_client.OpenReadCompleted += OpenReadCompletedHandler;
}
private void ButtonClickHandler(object sender, EventArgs e)
{
_client.OpenReadAsync(#"https://www.google.com/");
}
private void OpenReadCompletedHandler(object sender, OpenReadCompletedEventArgs e)
{
// this event will be raiesed 300ms after 'Button' click
var stream = e.Result; // <- here is your stream
// some other code
}
}
The last one (OpenReadTaskAsync()) is all about TPL (Task Parallel Library) and async/await keywords. It runs all stuff in a Task which is returned by this method. Here is an example:
public partial class MainForm : Form
{
private WebClient _client = new WebClient();
public MainForm()
{
InitializeComponents();
}
private async void ButtonClickHandler(object sender, EventArgs e)
{
// after 'await' keyword, execution will be returned from this method immediately
// meanwhile, actual acquiring of 'stream' is running in a background thread
using (var stream = await _client.OpenReadTaskAsync(#"https://www.google.com/"))
{
// after 300ms, this code will be continued in UI thread
// result will be automaticly unpacked to 'stream' variable
// some other code
}
}
}
Hope this helps.
I'd suggest WebClient is more or less obsolete now, HttpClient is more appropriate for anything targeting .NET framework 4.5+ or .NET core. Just watch out that the latter does not automatically throw exceptions on HTTP error codes (400+).

AutoResetEvent use issue

I'm trying to use an AutoResetEvent object to block the thread until the async. download of a WebClient is done.
My problem is that once I call WaitOne(), the thread just locks there and VS never reaches the breakpoint in the DownloadComplete event handler method.
Here's my code
//Class used to pass arguments to WebClient's events...
public class RunArgs
{
public JobInfo jobInfo;
public int jobTotal;
public int jobIndex;
public AutoResetEvent AutoResetEventObject;
}
List<JobInfo> jl = ConfigSectionWrapper.GetAllJobs();
int jobAmount = jl.Count;
int jobIndex = 0;
RunArgs args = new RunArgs();
args.jobTotal = jl.Count;
foreach (JobInfo ji in jl)
{
if (ji.enabled == "0")
{
args.jobIndex++;
continue;
}
try
{
args.jobIndex++;
args.jobInfo = ji;
appLog.Source = ji.eventSource;
appLog.WriteEntry(string.Format("Started job {0}...", ji.jobName), EventLogEntryType.Information);
ji.fullFileName = string.Format(ji.reportFileName, string.Format("{0}-{1}-{2}", DateTime.Now.Year.ToString(), DateTime.Now.Month.ToString().PadLeft(2, '0'), DateTime.Now.Day.ToString().PadLeft(2, '0')));
ji.fullFileName = string.Format("{0}{1}", ji.downloadDirectory, ji.fullFileName);
using (WebClient wc = new WebClient())
{
AutoResetEvent notifier = new AutoResetEvent(false);
args.AutoResetEventObject = notifier;
wc.Credentials = CredentialCache.DefaultNetworkCredentials;
wc.DownloadFileCompleted += new AsyncCompletedEventHandler(DownloadCompleted);
wc.DownloadFileAsync(new Uri(args.jobInfo.reportURL), args.jobInfo.fullFileName, args); //Pass the args params to event handler...
notifier.WaitOne();
}
}
catch (Exception ex)
{
appLog.WriteEntry(string.Format("Error starting report execution: {0}", ex.Message), EventLogEntryType.Error);
DeleteFile(ji.fullFileName);
}
}
private void DownloadCompleted(object sender, AsyncCompletedEventArgs e)
{
RunArgs args = (RunArgs)e.UserState;
//Do things....
args.AutoResetEventObject.Set();
}
So I instantiate notifier with false in the constructor, because I don't want its status to be signaled already. Unless I'm reading MSDN wrong ?
Anything obviously wrong ?
WebClient uses the AsyncOperationManager to manage async operations. So, make sure you are aware of how these async operations get called under different scenarios.
Under WinForms
Under a WinForm application AsyncOperationManager uses the WindowsFormsSynchronizationContext. As #Michael says, the WaitOne() call blocks the main form thread from firing the DownloadCompleted event. In WinForms the DownloadCompleted is executed on the main WinForm thread.
So, remove the notifier.WaitOne() and it should work. DownloadCompleted needs to be called by the main window thread (presumably, the one which you're blocking with WaitOne()).
Under Console applications
Under a Console type application the AsyncOperationManager uses System.Threading.SynchronizationContext and the DownloadCompleted is executed asynchronously by a thread form the threadpool.
There is no issue with calling notifier.WaitOne() under a Console app; and the code above works as expected.
I haven't found any documentation to support this, but looking at the WebClient code in Reflector, it appears that the events are raised on the main thread, not the background thread. Since your main thread is blocking when you call notifier.WaitOne(), the event handler never gets called.
If the example you provided accurately represents your code, there's absolutely no need to use wc.DownloadFileAsync() instead of wc.DownloadFile(). That notifier.WaitOne() call ultimately makes this into a synchronous operation. If you're looking for a true asynchronous operation, you'll have to do this differently.
Here's what I ended up doing:
private AutoResetEvent notifier = new AutoResetEvent(false);
Now the main loop looks like:
foreach (JobInfo ji in jl)
{
if (ji.enabled == "0")
{
args.jobIndex++;
continue;
}
args.jobInfo = ji;
Thread t = new Thread(new ParameterizedThreadStart(startDownload));
t.Start(args);
notifier.WaitOne();
}
private void startDownload(object startArgs)
{
RunArgs args = (RunArgs)startArgs;
try
{
args.jobIndex++;
appLog.Source = args.jobInfo.eventSource;
appLog.WriteEntry(string.Format("Started job {0}...", args.jobInfo.jobName), EventLogEntryType.Information);
args.jobInfo.fullFileName = string.Format(args.jobInfo.reportFileName, string.Format("{0}-{1}-{2}", DateTime.Now.Year.ToString(), DateTime.Now.Month.ToString().PadLeft(2, '0'), DateTime.Now.Day.ToString().PadLeft(2, '0')));
args.jobInfo.fullFileName = string.Format("{0}{1}", args.jobInfo.downloadDirectory, args.jobInfo.fullFileName);
WebClient wc = new WebClient();
wc.Credentials = CredentialCache.DefaultNetworkCredentials;
wc.DownloadFileCompleted += new AsyncCompletedEventHandler(DownloadCompleted);
wc.DownloadFileAsync(new Uri(args.jobInfo.reportURL), args.jobInfo.fullFileName, args); //Pass the args params to event handler...
}
catch (Exception ex)
{
appLog.WriteEntry(string.Format("Error starting report execution: {0}", ex.Message), EventLogEntryType.Error);
DeleteFile(args.jobInfo.fullFileName);
notifier.Set();
}
}
So now the AutoResetEvent is blocking the main thread but t can successfully trigger the DownloadFileCompleteEvent. And in the DownloadFileCompleted event, I'm obviously doing a notifier.Set() too.
Thanks all!

WebRequest hangs user interface

I'm doing simple request with WebRequest and the app just hangs until the response comes back. How do I fix this?
I have read a lot of topics and they all say to use threads. I don't know how to use them; can anyone provide an example of the following that doesn't hang the user interface?
private string SimpleRequest(String url)
{
WebRequest request = WebRequest.Create(url);
WebResponse response = request.GetResponse();
Stream stream = response.GetResponseStream();
StreamReader reader = new StreamReader(stream);
string result = reader.ReadToEnd();
stream.Dispose();
reader.Dispose();
return result;
}
private void button1_Click(object sender, EventArgs e)
{
String googleHtml = simpleRequest("https://facebook.com");
}
Thanks!
If you are using c# 5.0 it is too easy
public async void Test1()
{
WebClient wc = new WebClient();
richTextBox1.Text = await wc.DownloadStringTaskAsync("https://facebook.com");
}
You can convert your method to awaitable also
private Task<string> simpleRequest(String url)
{
return Task.Factory.StartNew(() =>
{
WebRequest request = WebRequest.Create(url);
WebResponse response = request.GetResponse();
Stream stream = response.GetResponseStream();
StreamReader reader = new StreamReader(stream);
string result = reader.ReadToEnd();
stream.Dispose();
reader.Dispose();
return result;
});
}
and call as
public async void Test2()
{
richTextBox1.Text = await simpleRequest("https://facebook.com");
}
For lower versions of C#, See other answers.
You can use a BackgroundWorker in order to use another thread than UI thread. Here is a sample.
You can also cancel or display progress:
public partial class fmMain : Form
{
private void btnrunBackgroundWorker_Click( object sender, EventArgs e )
{
// Create a background worker
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler( bw_DoWork );
// run in another thread
bw.RunWorkerAsync();
}
private void bw_DoWork( object sender, DoWorkEventArgs e )
{
String facebookHtml = simpleRequest("https://facebook.com");
}
}
You're doing network access on the main UI thread. The UI has a main thread which loops and updates the UI. If you do network access on that thread, the UI will not update and it will appear hung until your method returns and the loop can continue.
Consider doing your work on a background thread. One easy way to do that in .net is the BackgroundWorker class.
The official documentation has a nice example of it's usage:
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
The BackgroundWorker class allows you to run an operation on a
separate, dedicated thread. Time-consuming operations like downloads
and database transactions can cause your user interface (UI) to seem
as though it has stopped responding while they are running. When you
want a responsive UI and you are faced with long delays associated
with such operations, the BackgroundWorker class provides a convenient
solution.
You should use a BackgroundWorker so your main UI thread does not hang!
private void button1_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync("https://facebook.com"); // start async operation
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
e.Result = simpleRequest(e.Argument.ToString()); // set result in async operation
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error == null)
{
// show result here
// or access/manage result here
MessageBox.Show(e.Result.ToString());
}
else
{
// manage/show error
MessageBox.Show(e.Error.Message);
}
}
private String simpleRequest(String url)
{
// your code goes here
}
You could implement backgroundWorker1_ProgressChanged too, so that the user can see progress of loading webSite. (sideNote: see this question for details on ProgressChanged and Context)

Windows Phone BackgroundWorker for WebClient?

Now the WebClient issue is fixed and can return on a background thread i'd like to start using it in this way.
After much searching I've come up with this code that appears to work fine, is this all there is to it?
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += (s,e) =>
{
WebClient wc = new WebClient();
wc.DownloadStringCompleted += DownloadStringCompleted;
wc.DownloadStringAsync(url);
};
bw.RunWorkerAsync();
In DownloadStringCompleted I send the result back to the UI thread.
Have I missed anything important or is it really this simple?
I don't get why you would want to run the WebClient on a background thread in the first place, since the WebClient is already creating a thread for the downloading part.
The difference is that the WebClient is running it's DownloadStringCompleted event on the UI thread. Which it still would do in your code.
I would suggest you use WebRequest class instead. The use of the WebRequest class can be greatly simplified with a simple extension method, that makes it behave like the WebClient.
public static class WebRequestEx
{
public static void DownloadStringAsync(this WebRequest request, Action<string> callback)
{
if (request == null)
throw new ArgumentNullException("request");
if (callback == null)
throw new ArgumentNullException("callback");
request.BeginGetResponse((IAsyncResult result) =>
{
try
{
var response = request.EndGetResponse(result);
using (var reader = new StreamReader(response.GetResponseStream()))
{
callback(reader.ReadToEnd());
}
}
catch (WebException e)
{
// Don't perform a callback, as this error is mostly due to
// there being no internet connection available.
System.Diagnostics.Debug.WriteLine(e.Message);
}
}, request);
}
}
The issue I referred to was that in 7.0 WebClient always returned on the UI thread regardless of where it was created, potentially making the UI unresponsive.
In the WP SDK 7.1 WebClient will return on the thread it was created from, so if it is created from a background thread DownloadStringCompleted will now return on a background thread.
If you test my example without marshalling the response you will see an Invalid Cross Thread Exception.
It seems to me unless you have a reason not to, why not use WebClient now?
Seems that easy.
Check only if everything which can be disposed get's disposed.

How to use WebClient without blocking UI?

Can someone point me to a tutorial or provide some sample code to call the System.Net.WebClient().DownloadString(url) method without freezing the UI while waiting for the result?
I assume this would need to be done with a thread? Is there a simple implementation I can use without too much overhead code?
Thanks!
Implemented DownloadStringAsync, but UI is still freezing. Any ideas?
public void remoteFetch()
{
WebClient client = new WebClient();
// Specify that the DownloadStringCallback2 method gets called
// when the download completes.
client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(remoteFetchCallback);
client.DownloadStringAsync(new Uri("http://www.google.com"));
}
public void remoteFetchCallback(Object sender, DownloadStringCompletedEventArgs e)
{
// If the request was not canceled and did not throw
// an exception, display the resource.
if (!e.Cancelled && e.Error == null)
{
string result = (string)e.Result;
MessageBox.Show(result);
}
}
Check out the WebClient.DownloadStringAsync() method, this'll let you make the request asynchronously without blocking the UI thread.
var wc = new WebClient();
wc.DownloadStringCompleted += (s, e) => Console.WriteLine(e.Result);
wc.DownloadStringAsync(new Uri("http://example.com/"));
(Also, don't forget to Dispose() the WebClient object when you're finished)
You could use a BackgroundWorker or as #Fulstow said the DownStringAsynch method.
Here's a tutorial on Backgorund worker: http://www.dotnetperls.com/backgroundworker

Categories

Resources