why UI freezes during long running operations [duplicate] - c#

This question already has answers here:
WinForm Application UI Hangs during Long-Running Operation
(3 answers)
Closed 8 years ago.
I'm studying c# and tried other solutions that I've found on stackoverflow.. But I failed..
I'm trying to check if an URL exists when click a button.
When the button is clicked, a progressBar is set to marquee and the verification begins.
But the system stops until the result backs..
Here is the button click:
private void button1_Click(object sender, EventArgs e)
{
this.progressBar1.Style = System.Windows.Forms.ProgressBarStyle.Marquee;
if (RemoteFileExists("http://www.gofdisodfi.com/"))
{
// OK
}
else
{
//FAIL
}
}
And here is the check:
private bool RemoteFileExists(string url)
{
try
{
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
request.Method = "HEAD";
HttpWebResponse response = request.GetResponse() as HttpWebResponse;
return (response.StatusCode == HttpStatusCode.OK);
}
catch
{
return false;
}
}

You could use the "async and await" syntax for asynchronous action. It won't freeze the UI
private async void button1_Click(object sender, EventArgs e)
{
this.progressBar1.Style = System.Windows.Forms.ProgressBarStyle.Marquee;
var result = await RemoteFileExists("http://www.google.com/");
if (result)
{
// OK
MessageBox.Show("OK");
}
else
{
//FAIL
MessageBox.Show("Fail");
}
}
private async Task<bool> RemoteFileExists(string url)
{
try
{
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
request.Method = "HEAD";
HttpWebResponse response = await request.GetResponseAsync() as HttpWebResponse;
return (response.StatusCode == HttpStatusCode.OK);
}
catch
{
return false;
}
}
You can read more about it here: http://blog.stephencleary.com/2012/02/async-and-await.html

Related

How can I prevent UI freezing while using HttpWebRequest?

I have a progressBar on my form and a button.
When user clicks this button, the progressBar should be styled as "marquee" and the program begins to check is an URL is valid or not.. OK.
But when I click the button, the UI freezes until the HttpStatusCode returns true or false...
Here is the check code:
private bool RemoteFileExists(string url)
{
try
{
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
request.Method = "HEAD";
HttpWebResponse response = request.GetResponse() as HttpWebResponse;
return (response.StatusCode == HttpStatusCode.OK);
}
catch
{
return false;
}
}
And here is the button click code:
private async void button1_Click(object sender, EventArgs e)
{
this.progressBar1.Style = System.Windows.Forms.ProgressBarStyle.Marquee;
var result = RemoteFileExists("http://www.google.com/");
if (Completed)
{
//ok
}
else
{
//not ok
}
}
The UI freezes because you are executing the RemoteFileExists method on the UI thread and receiving a response from a HttpWebRequest takes some time.
To solve this you have to execute RemoteFileExists in a different thread than the UI thread.
As your button1_Click method is already declared async the easiest way would be to declare RemoteFileExists as async too.
Then you can use the HttpWebRequest.GetResponseAsync method to asynchronously receive the response object.
private async Task<bool> RemoteFileExists(string url)
{
try
{
HttpWebRequest request = WebRequest.CreateHttp(url);
request.Method = "HEAD";
using(var response = (HttpWebResponse) await request.GetResponseAsync())
{
return (response.StatusCode == HttpStatusCode.OK);
}
}
catch
{
return false;
}
}
Also when dealing with IDisposables you should take care of releasing all used resources by using the using statement or calling Dispose().
If you are using .NET Framework 4+ you can also use WebRequest.CreateHttp(string) to create your HttpWebRequest.
Putting it simple just use this:
private void button1_Click(object sender, EventArgs e)
{
this.progressBar1.Style = System.Windows.Forms.ProgressBarStyle.Marquee;
Thread thread = new Thread(() => RemoteFileExists("http://www.google.com/"));
thread.IsBackground = true;
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
And do the check inside RemoteFileExists .

Windows Phone 8.1 print result of an HTTP request

I can not print or process the data that I receive after making an HTTP request.
This is the code I wrote:
private void Button_Click(object sender, RoutedEventArgs e)
{
String jsonUri = "hxxp://xxxxx/zzzz/yyyyyy; //censured
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(jsonUri);
request.BeginGetResponse(GetDataCallback, request);
}
void GetDataCallback(IAsyncResult result)
{
HttpWebRequest request = result.AsyncState as HttpWebRequest;
if (request != null)
{
WebResponse response = request.EndGetResponse(result);
testo.Text = response.GetResponseStream().ToString();
}
}
I tried various solutions but I just can not print the result.
apache I see the call, and the data from the app I could see that coming

Windows Phone 8: passing data from PHP page to windows phone 8 app

I need to send some data from php page to windows phone 8(C#) and need to display it.
Here is my wp8 side Code :
private void Track_Click(object sender, RoutedEventArgs e)
{
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(attackUri);
request.BeginGetResponse(Showtext, request);
}
}
void Showtext(IAsyncResult result)
{
HttpWebRequest request = result.AsyncState as HttpWebRequest;
if (request != null)
{
try
{
WebResponse response = request.EndGetResponse(result);
var txt = request.Content.ReadAsStringAsync();
//to display data passed from PHP page
MessageBox.Show(txt.Result);
}
catch (WebException e)
{
}
}
}
I'm not quite sure, where you got your request.Content from, but it does not seem to be WP8 native.
Try the following. This is how it always worked for me:
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(new Uri("http://www.google.de"));
request.BeginGetResponse(ShowText, request);
private void ShowText(IAsyncResult result)
{
HttpWebRequest request = (HttpWebRequest)result.AsyncState;
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(result);
using (StreamReader streamReader = new StreamReader(response.GetResponseStream()))
{
string content = streamReader.ReadToEnd();
Debug.WriteLine(content);
}
}

Consuming Streaming API Call using HTTP Web Request [duplicate]

How can I use HttpWebRequest (.NET, C#) asynchronously?
Use HttpWebRequest.BeginGetResponse()
HttpWebRequest webRequest;
void StartWebRequest()
{
webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), null);
}
void FinishWebRequest(IAsyncResult result)
{
webRequest.EndGetResponse(result);
}
The callback function is called when the asynchronous operation is complete. You need to at least call EndGetResponse() from this function.
By far the easiest way is by using TaskFactory.FromAsync from the TPL. It's literally a couple of lines of code when used in conjunction with the new async/await keywords:
var request = WebRequest.Create("http://www.stackoverflow.com");
var response = (HttpWebResponse) await Task.Factory
.FromAsync<WebResponse>(request.BeginGetResponse,
request.EndGetResponse,
null);
Debug.Assert(response.StatusCode == HttpStatusCode.OK);
If you can't use the C#5 compiler then the above can be accomplished using the Task.ContinueWith method:
Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse,
request.EndGetResponse,
null)
.ContinueWith(task =>
{
var response = (HttpWebResponse) task.Result;
Debug.Assert(response.StatusCode == HttpStatusCode.OK);
});
Considering the answer:
HttpWebRequest webRequest;
void StartWebRequest()
{
webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), null);
}
void FinishWebRequest(IAsyncResult result)
{
webRequest.EndGetResponse(result);
}
You could send the request pointer or any other object like this:
void StartWebRequest()
{
HttpWebRequest webRequest = ...;
webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), webRequest);
}
void FinishWebRequest(IAsyncResult result)
{
HttpWebResponse response = (result.AsyncState as HttpWebRequest).EndGetResponse(result) as HttpWebResponse;
}
Greetings
Everyone so far has been wrong, because BeginGetResponse() does some work on the current thread. From the documentation:
The BeginGetResponse method requires some synchronous setup tasks to
complete (DNS resolution, proxy detection, and TCP socket connection,
for example) before this method becomes asynchronous. As a result,
this method should never be called on a user interface (UI) thread
because it might take considerable time (up to several minutes
depending on network settings) to complete the initial synchronous
setup tasks before an exception for an error is thrown or the method
succeeds.
So to do this right:
void DoWithResponse(HttpWebRequest request, Action<HttpWebResponse> responseAction)
{
Action wrapperAction = () =>
{
request.BeginGetResponse(new AsyncCallback((iar) =>
{
var response = (HttpWebResponse)((HttpWebRequest)iar.AsyncState).EndGetResponse(iar);
responseAction(response);
}), request);
};
wrapperAction.BeginInvoke(new AsyncCallback((iar) =>
{
var action = (Action)iar.AsyncState;
action.EndInvoke(iar);
}), wrapperAction);
}
You can then do what you need to with the response. For example:
HttpWebRequest request;
// init your request...then:
DoWithResponse(request, (response) => {
var body = new StreamReader(response.GetResponseStream()).ReadToEnd();
Console.Write(body);
});
public static async Task<byte[]> GetBytesAsync(string url) {
var request = (HttpWebRequest)WebRequest.Create(url);
using (var response = await request.GetResponseAsync())
using (var content = new MemoryStream())
using (var responseStream = response.GetResponseStream()) {
await responseStream.CopyToAsync(content);
return content.ToArray();
}
}
public static async Task<string> GetStringAsync(string url) {
var bytes = await GetBytesAsync(url);
return Encoding.UTF8.GetString(bytes, 0, bytes.Length);
}
I ended up using BackgroundWorker, it is definitely asynchronous unlike some of the above solutions, it handles returning to the GUI thread for you, and it is very easy to understand.
It is also very easy to handle exceptions, as they end up in the RunWorkerCompleted method, but make sure you read this: Unhandled exceptions in BackgroundWorker
I used WebClient but obviously you could use HttpWebRequest.GetResponse if you wanted.
var worker = new BackgroundWorker();
worker.DoWork += (sender, args) => {
args.Result = new WebClient().DownloadString(settings.test_url);
};
worker.RunWorkerCompleted += (sender, e) => {
if (e.Error != null) {
connectivityLabel.Text = "Error: " + e.Error.Message;
} else {
connectivityLabel.Text = "Connectivity OK";
Log.d("result:" + e.Result);
}
};
connectivityLabel.Text = "Testing Connectivity";
worker.RunWorkerAsync();
.NET has changed since many of these answers were posted, and I'd like to provide a more up-to-date answer. Use an async method to start a Task that will run on a background thread:
private async Task<String> MakeRequestAsync(String url)
{
String responseText = await Task.Run(() =>
{
try
{
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
WebResponse response = request.GetResponse();
Stream responseStream = response.GetResponseStream();
return new StreamReader(responseStream).ReadToEnd();
}
catch (Exception e)
{
Console.WriteLine("Error: " + e.Message);
}
return null;
});
return responseText;
}
To use the async method:
String response = await MakeRequestAsync("http://example.com/");
Update:
This solution does not work for UWP apps which use WebRequest.GetResponseAsync() instead of WebRequest.GetResponse(), and it does not call the Dispose() methods where appropriate. #dragansr has a good alternative solution that addresses these issues.
public void GetResponseAsync (HttpWebRequest request, Action<HttpWebResponse> gotResponse)
{
if (request != null) {
request.BeginGetRequestStream ((r) => {
try { // there's a try/catch here because execution path is different from invokation one, exception here may cause a crash
HttpWebResponse response = request.EndGetResponse (r);
if (gotResponse != null)
gotResponse (response);
} catch (Exception x) {
Console.WriteLine ("Unable to get response for '" + request.RequestUri + "' Err: " + x);
}
}, null);
}
}
Follow up to the #Isak 's answer, which is very good. Nonetheless it's biggest flaw is that it will only call the responseAction if the response has status 200-299. The best way to fix this is:
private void DoWithResponseAsync(HttpWebRequest request, Action<HttpWebResponse> responseAction)
{
Action wrapperAction = () =>
{
request.BeginGetResponse(new AsyncCallback((iar) =>
{
HttpWebResponse response;
try
{
response = (HttpWebResponse)((HttpWebRequest)iar.AsyncState).EndGetResponse(iar);
}
catch (WebException ex)
{
// It needs to be done like this in order to read responses with error status:
response = ex.Response as HttpWebResponse;
}
responseAction(response);
}), request);
};
wrapperAction.BeginInvoke(new AsyncCallback((iar) =>
{
var action = (Action)iar.AsyncState;
action.EndInvoke(iar);
}), wrapperAction);
}
And then as #Isak follows:
HttpWebRequest request;
// init your request...then:
DoWithResponse(request, (response) => {
var body = new StreamReader(response.GetResponseStream()).ReadToEnd();
Console.Write(body);
});
I've been using this for async UWR, hopefully it helps someone
string uri = "http://some.place.online";
using (UnityWebRequest uwr = UnityWebRequest.Get(uri))
{
var asyncOp = uwr.SendWebRequest();
while (asyncOp.isDone == false) await Task.Delay(1000 / 30); // 30 hertz
if(uwr.result == UnityWebRequest.Result.Success) return uwr.downloadHandler.text;
Debug.LogError(uwr.error);
}

App hangs when not being debugged in Visual Studio

I have this app which authenticates a user with external web service and should navigate to a different view once authenticated.
The authentication is done in a HttpWebRequest:
private void btnLogin_Click(object sender, RoutedEventArgs e)
{
HttpWebRequest request = (HttpWebRequest)System.Net.WebRequest.Create("http://webservice");
request.Method = "GET";
request.BeginGetResponse(new AsyncCallback(CheckLogin), request);
}
Then here is the callback:
private void CheckLogin(IAsyncResult asynchronousResult)
{
HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
bool success = false;
try
{
// End the operation
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asynchronousResult);
Stream streamResponse = response.GetResponseStream();
StreamReader streamRead = new StreamReader(streamResponse);
string responseString = streamRead.ReadToEnd();
if (responseString.Contains("ok"))
{
success = true;
}
streamResponse.Dispose();
streamRead.Dispose();
response.Dispose();
}
catch (Exception e)
{
}
request.Abort();
request = null;
if (success)
{
this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
Frame.Navigate(typeof(Main2));
}).AsTask().Wait();
}
}
This is working perfectly when debugging in Visual Studio but when I have published the application and installed the package, it hangs on Frame.Navigate. I guess this is because the CheckLogin method is not running in the UI thread.
Any ideas on how to Frame.Navigate(..) in a background thread?
It seems to be no real problems between Dispatcher.RunAsync and Frame.Navigate. So I assume you're not looking in the right direction.
Maybe your page's content is faulty, instead of the navigation.

Categories