I'm trying to make an asynchronous HTTP GET request using Webclient, however, the registered callback never gets called. I've also tried with the sync one, and it worked fine. What am I doing wrong?
WebClient asyncWebRequest;
public AsyncWebRequest(Uri url)
{
asyncWebRequest = new WebClient();
url = new Uri("http://www.google.com/");
// string test = asyncWebRequest.DownloadString(url); // this works
asyncWebRequest.DownloadStringCompleted += new DownloadStringCompletedEventHandler(asyncWebRequest_DownloadStringCompleted);
asyncWebRequest.DownloadStringAsync(url);
}
void asyncWebRequest_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
throw new NotImplementedException();
}
Maybe because you disposing the WebClient before it finished downloading. The code execution don't stop on asyncWebRequest.DownloadStringAsync(url); and you are disposing the WebClient object by closing the using statement.
try to dispose the WebClient on asyncWebRequest_DownloadStringCompleted.
results
The simpliest solution is to add Console.ReadKey() at the end of AsyncWebRequest(url) method. This way asyncWebRequest.DownloadStringAsync(url) will be able to retrieve data.
Related
First of all hello guys i just wanted to add button that downloads zip files from link and then unzips and i ran into problems i get this error:
"System.IO.IOException: 'The process cannot access the file
'C:\GTA\TEST.zip' because it is being used by another process.'"
It looks really simple but i can't solve it so i hope you guys help me. this is code:
private void button2_Click(object sender, EventArgs e)
{
string root = #"C:\GTA";
//this if directory doesn't exist
if (!Directory.Exists(root))
{
Directory.CreateDirectory(root);
}
progressBar1.Value = 0;
WebClient webcl = new WebClient();
webcl.DownloadFileCompleted += Webcl_DownloadFileCompleted;
webcl.DownloadProgressChanged += Webcl_DownloadProgressChanged;
webcl.DownloadFileAsync(new Uri("https://download1474.mediafire.com/17r5hin4vceg/izkb8vk7pudg5g4/TEST.zip"), #"C:\GTA\TEST.zip");
string targetfolder = #"C:\GTA\UNZIPEDFolder";
string sourceZipFile = #"C:\GTA\TEST.zip";
ZipFile.ExtractToDirectory(sourceZipFile, targetfolder);
}
I'm no expert here, however you get the file asynchronosly without awaiting it.
DownloadFileAsync
So you make a call to extract the file while it's being downloaded.
You calling ExtractToDirectory before file will be actually downloaded, as file downloading is async. So, you need to await when downloading process will finish. To do so, you will need the following
make the whole event click handler async - private async void button2_Click(object sender, EventArgs e).
replace DownloadFileAsync which returns void and thus is not async/await-friendly with DownloadFileTaskAsync, which is awaitable.
Then you will able to await downloading with await webcl.DownloadFileTaskAsync(...args here...);
finally, you can remove DownloadFileCompleted subscription, as you may be sure that after await the file downloading is completed.
By the way, WebClient is considered as an old API and is not recommended for using in the new code. You may consider to switch to HttpClient.
To elaborate a bit on the two previous answers, you are in fact trying to unzip the file before you have downloaded it. You should change your code as follows:
private async void button2_Click(object sender, EventArgs e)
{
string root = #"C:\GTA";
//this if directory doesn't exist
if (!Directory.Exists(root))
{
Directory.CreateDirectory(root);
}
progressBar1.Value = 0;
WebClient webcl = new WebClient();
webcl.DownloadFileCompleted += Webcl_DownloadFileCompleted;
webcl.DownloadProgressChanged += Webcl_DownloadProgressChanged;
await webcl.DownloadFileAsync(new Uri("https://download1474.mediafire.com/17r5hin4vceg/izkb8vk7pudg5g4/TEST.zip"), #"C:\GTA\TEST.zip");
string targetfolder = #"C:\GTA\UNZIPEDFolder";
string sourceZipFile = #"C:\GTA\TEST.zip";
ZipFile.ExtractToDirectory(sourceZipFile, targetfolder);
}
Note the async as well as the await before DownloadFileAsync().
Additionally, you might want to refactor that a bit and move the download / unzip part out of the Button Event Handler.
I'm getting an error:
ObjectDisposedException: Cannot access a disposed object. Object name: 'Dispatcher'.
My code in modelview:
public CultureEventViewModel()
{
CultureEvents = new List<CultureEvent>();
WebClient webClient = new WebClient();
webClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(webClient_DownloadStringCompleted);
webClient.DownloadStringAsync(new Uri("sampleuri"));
}
public void webClient_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
CultureEvents = JsonConvert.DeserializeObject<List<CultureEvent>>(e.Result);
}
I observed that it returns no error when I delete a line in webClient_DownloadStringCompleted. Any ideas or more code needed?
The scope of webclient is limited to the public CultureEventViewModel() instantiation (in other words it can be garbage collected as soon as the object is instantiated. Because there is an outstanding asynchronous task being performed (DownloadStringAsync) the garbage collector can not collect your webClient object.
Once the string has been downloaded the webClient is fair game and can be disposed of. To keep the web client you need to give it an existance outside the instantiation.
for example
Class CultureEventViewModel
private WebClient webclient
public CultureEventViewModel()
{
CultureEvents = new List<CultureEvent>();
WebClient webClient = new WebClient();
...
but note that this will not dispose of the webclient instance untill the class instance is disposed of.
private void button1_Click(object sender, EventArgs e)
{
this.icon_testLOAD.Visible = true;
this.icon_testOK.Visible = false;
this.icon_testBAD.Visible = false;
this.debug("Test Service Button Clicked");
rabbitmq_test t = new rabbitmq_test(button_rabbitmq_test);
this.debug("Calling BeginInvoke on button_rabbitmq_test delegate");
t.BeginInvoke(null, null);
}
So I have this button click event. The first three lines are turning on and off PictureBoxes that contain icons.
this.debug() merely calls EventLog.WriteEntry()
The button_rabbitmq_test method looks like:
protected void button_rabbitmq_test()
{
this.debug("Creating new rabbitmq connection factory");
IConnection connection;
try
{
ConnectionFactory rq_factory = new ConnectionFactory();
rq_factory.Port = Convert.ToInt16(this.psistats_config.rabbitmq_port);
rq_factory.HostName = this.psistats_config.rabbitmq_server;
rq_factory.UserName = this.psistats_config.rabbitmq_username;
rq_factory.Password = this.psistats_config.rabbitmq_password;
rq_factory.RequestedConnectionTimeout = 15000;
this.debug("Creating new rabbitmq connection");
connection = rq_factory.CreateConnection();
this.debug("Changing icon to successful");
rabbitmq_icon_delegate d = new rabbitmq_icon_delegate(this.testOK);
connection.Close();
this.test_button.Invoke(d);
}
catch (Exception exc)
{
if (connection != null)
{
connection.Close();
}
this.debug("Failed testing the rabbit server");
this.debug(exc.Message);
this.debug(exc.StackTrace);
rabbitmq_icon_delegate d = new rabbitmq_icon_delegate(this.testFailed);
this.test_button.Invoke(d);
}
}
This code works fine on the computer I'm doing the development on. The method executes, the event log is populated as expected. However, when I run this application on a second machine, the BeginInvoke method doesn't seem to do anything at all and I have absolutely no idea why.
The last message I see in the event log is "Calling BeginInvoke..." but the event logs from the method that performs the actual test are not seen anywhere.
The application is also not frozen. I can still use it.
I am at a loss as to what I am doing wrong and any advice would be welcome.
The code is fundamentally flawed, you must call the delegate's EndInvoke() method. Best done by not passing null as the first argument, use a callback method that then calls EndInvoke().
If you don't call EndInvoke() then you'll leak resources, lasts for 10 minutes. And the ultimate problem you are asking about, you can't see an exception that was raised by the method. So you cannot find out why it didn't work. Calling EndInvoke() rethrows that exception.
Using a delegate's BeginInvoke() method is a low-level programming technique that's best avoided, too easy to make mistakes like this and too difficult to deal with exceptions. Use a BackgroundWorker or a Task instead.
Is there any way to know which file has just now finished downloading from the DownloadFileCompleted event of WebClient.
Thanks in advance.
You can use UserState to do this. Something like this
WebClient client = new WebClient();
client.DownloadDataCompleted +=
new DownloadDataCompletedEventHandler(client_DownloadDataCompleted);
client.DownloadDataAsync(new Uri("YourURL"), "YourIdentifier");
Handler
static void client_DownloadDataCompleted(object sender, DownloadDataCompletedEventArgs e)
{
var calledBy = e.UserState; //This will be "YourIdentifier"
}
Hope this works for you.
When downloading file use webClient.DownloadFileAsync(uri, name, state) method.
This 3rd parameter (state) will be sent to your in the UserState property of the DownloadFileCompleted event arguments.
Just pass the uri or the file name there and you will have it back nicely :)
I have a windows service that calls a page after a certain interval of time. The page in turn creates some reports.
The problem is that the service stops doing anything after 2-3 calls. as in it calls the page for 2-3 times and then does not do any work though it shows that the service is running...i am using timers in my service..
please can someone help me with a solution here
thank you
the code:(where t1 is my timer)
protected override void OnStart(string[] args)
{
GetRecords();
t1.Elapsed += new ElapsedEventHandler(OnElapsedTime);
t1.Interval = //SomeTimeInterval
t1.Enabled = true;
t1.Start();
}
private void OnElapsedTime(object source, ElapsedEventArgs e)
{
try
{
GetRecords();
}
catch (Exception ex)
{
EventLog.WriteEntry(ex.Message);
}
}
public void GetRecords()
{
try
{
string ConnectionString = //Connection string from web.config
WebRequest Request = HttpWebRequest.Create(ConnectionString);
Request.Timeout = 100000000;
HttpWebResponse Response = (HttpWebResponse)Request.GetResponse();
}
catch (Exception ex)
{
}
}
Well, what does the code look like? WebClient is the easiest way to query a page:
string result;
using (WebClient client = new WebClient()) {
result = client.DownloadString(address);
}
// do something with `result`
The timer code might also be glitchy if it is stalling...
It's possible that HttpWebRequest will restrict the number of concurrent HTTP requests to a specific page or server, as is generally proper HTTP client practice.
The fact that you're not properly disposing your objects most likely means you are maintaining 2 or 3 connections to a specific page, each with large timout value, and HttpWebRequest is queueing or ignoring your requests until the first few complete (die from a client or server timeout, most likely the server in this case).
Add a 'finally' clause and dispose of your objects properly!
I think you're missing something about disposing your objects like StreamReader, WebRequest, etc.. You should dispose your expensive objects after using them.
possibly the way you are requesting athe page is throwing an unnhandled exception which leaves the service in an inoperable state.
Yes, we need code.
Marc's advice worked for me, in the context of a service
Using WebClient worked reliably, where WebRequest timed out.
#jscharf explanation looks as good as any to me.