How to fix GDI+ Errors when downloading files? - c#

I have been making a client that installs a program I am also making. The problem is when I go to download the files. Sometimes, it gets stuck. I get thrown a error stating
System.Runtime.InteropServices.ExternalException: 'A generic error occurred in GDI+.'
...and my UI freaks out (Buttons go blank, labels disapper, images go missing, ect). I have researched this error and it seems it happens with images, but I am downloading zip files that contain no images. I have found that it might have something to do with 5 files I am extracting from the zip files but thats not entirely accurate because it doesnt always happen and I have no real way to determine EXACTLY whats causing it. I suspect its because I cannot download so much in a short period of time but I do not know if this is why exactly either.
Also to add to this, the files still complete downloading when in debug mode, they ever continue through the async process, awaiting properly and everything.
I have tried narrowing down what file is causing it but I dont have any evidence to support it is a specific file. I have also tried spliting up the zip files to see if its the size of how much Im downloading at once, still no luck.
These are the download functions.
The RunWorkerTaskAsync() is a custom reference I created to allow a worker to be "awaited". I privide the code below.(I take no credit as its pieces of code I have pulled from others)
private async Task DownloadLibs()
{
Response.Text = "Updating Libraries...";
this.Update();
string url = #"http://akumamc.com/AkumaMC/Libraries.zip";
if (!string.IsNullOrEmpty(url))
{
Uri uri = new Uri(url);
string fileName = System.IO.Path.GetFileName(uri.AbsolutePath);
await DLclient.DownloadFileTaskAsync(uri, #"C:\temp\" + fileName);
DLclient.Dispose();
}
FileZipName = #"C:\temp\Libraries.zip";
FileZipPath = #"C:\temp\.minecraft";
Response.Text = "Extracting Libraries...";
this.Update();
await extractFile.RunWorkerTaskAsync();
}
private async Task DownloadMods()
{
Response.Text = "Updating Mods (1/2)...";
this.Update();
string url = #"http://akumamc.com/AkumaMC/Mods.zip";
if (!string.IsNullOrEmpty(url))
{
Uri uri = new Uri(url);
string fileName = System.IO.Path.GetFileName(uri.AbsolutePath);
await DLclient.DownloadFileTaskAsync(uri, #"C:\temp\" + fileName);
DLclient.Dispose();
}
FileZipName = #"C:\temp\Mods.zip";
FileZipPath = #"C:\temp\.minecraft";
Response.Text = "Extracting Mods (1/2)...";
this.Update();
await extractFile.RunWorkerTaskAsync();
}
private async Task DownloadExtras()
{
Response.Text = "Updating Mods (2/2)...";
this.Update();
string url = #"http://akumamc.com/AkumaMC/Mods2.zip";
if (!string.IsNullOrEmpty(url))
{
Uri uri = new Uri(url);
string fileName = System.IO.Path.GetFileName(uri.AbsolutePath);
await DLclient.DownloadFileTaskAsync(uri, #"C:\temp\" + fileName);
DLclient.Dispose();
}
FileZipName = #"C:\temp\Mods2.zip";
FileZipPath = #"C:\temp\.minecraft";
Response.Text = "Extracting Mods (2/2)...";
this.Update();
await extractFile.RunWorkerTaskAsync();
}
RunWorkerTaskAsync:
public static Task<object> RunWorkerTaskAsync(this BackgroundWorker backgroundWorker)
{
var tcs = new TaskCompletionSource<object>();
RunWorkerCompletedEventHandler handler = null;
handler = (sender, args) =>
{
if (args.Cancelled)
tcs.TrySetCanceled();
else if (args.Error != null)
tcs.TrySetException(args.Error);
else
tcs.TrySetResult(args.Result);
};
backgroundWorker.RunWorkerCompleted += handler;
try
{
backgroundWorker.RunWorkerAsync();
}
catch
{
backgroundWorker.RunWorkerCompleted -= handler;
throw;
}
return tcs.Task;
}
I expect the files to download without the form causing UI glitches and crashing.
EDIT: Link to author's client code (taken from comment below)

This is a summary of my comments beneath the OP's question
System.Runtime.InteropServices.ExternalException: 'A generic error occurred in GDI+.'
So the "interop" error implies some form of component object model (COM) problem and the things that stick out are:
the use of some 3rd party library that may be using COM
your RunWorkerTaskAsync extension method seems to be making multiple calls to BackgroundWorker.RunWorkerAsync without first checking that the worker is busy.
BackgroundWorker.RunWorkerAsync() is a void, it does not return a Task and so can't be used in async/await. Therefore your extension method is essentially kicking off a background worker without waiting for it to complete. Your extension method RunWorkerTaskAsync() (which isn't entirely async) returns immediately to those that called it.
You need to for the worker to complete before calling RunWorkerAsync again.
A possible fix:
Inside your extension method, check BackgroundWorker.IsBusy before telling it to run. (A better way is to wait for RunWorkerCompleted and kicking off a new one there)
Call RunWorkerAsync
Because you want to wait for this "task" to complete before returning control to say DownloadMods(), your extension method will need to monitor RunWorkerCompleted. This is kinda ugly as it goes against the original best practices of BackgroundWorker where everything is event-driven.
Alternatives
Considering you are using async/await anyway, why use BackgroundWorker at all? Consider wrapping up the essence of your extension method into a new method and call it via Task.Run().
You can still have a async Task ExtractFilesAsync method that runs in a child task (because we used Task.Run() it will also be a child thread) can report progress.
Something like (pseudo code):
await Task.Run ( async () => await
UnzipFilesAsync ( p =>
{
myProgressBar.BeginInvoke (new Action( () =>
myprogressBar.Progress = p; ));
});
.
.
.
UnzipFilesAsync (Action<int> progressCallback)
{
.
.
.
int percent = ...;
progressCallback (percent);
}
Tell me more about async progress bar updates
By the way, you shouldn't call MessageBox.Show or update the UI directly in a child thread, even if the call is a dialog with its own message pump.

Related

Wait till all files are downloaded and wait till proccessing with those files are finished

I have a listOfFilesToDownload. I want to download all files in list in parallel
.........
Parallel.ForEach(listOfFilesToDownload, (file) =>
{
SaveFile(file, myModel);
});
private static void SaveFile(string file, MyType myModel)
{
filePath = "...";
try
{
using (WebClient webClient = new WebClient())
{
webClient.DownloadFileTaskAsync(file, filePath)
}
//some time consuming proccess with downloaded file
}
catch (Exception ex)
{
}
}
In SaveFile method I download the file, then I want to wait till it is downloaded, then make some processing with this file, and wait till this processing is finished. The full iteration have to be - download file and process it
So, the questions are:
how to wait till the file is downloaded in the best way, so nothing is blocked and with maximum performance (I mean if I would use just DownloadFile it will block the thread till the file downloading, and I think this is not so good)
How to ensure that the file is downloaded and only then start processing it (cause if I start to process not existing file or not fully downloaded file I will have an error or wrong data)
How to be sure processing with file is finished (because I tried to use webClient.DownloadFileCompleted event and process the file there, but I didn't manage to ensure that the processing is finished, example down below)
In complex the question is how to wait for a file to download asynchronously AND wait till it's processed
using (WebClient webClient = new WebClient())
{
webClient.DownloadFileCompleted += DownloadFileCompleted(filePath, myModel);
webClient.DownloadFileTaskAsync(file, filePath);
}
DownloadFileCompleted returns AsyncCompletedEventHandler:
public static AsyncCompletedEventHandler DownloadFileCompleted(string filePath, MyType myModel)
{
Action<object, AsyncCompletedEventArgs> action = (sender, e) =>
{
if (e.Error != null)
return;
//some time consuming proccess with downloaded file
};
return new AsyncCompletedEventHandler(action);
}
Many thanks in advance!
Have you considered Task.WhenAll? Something like:
var tasks = listOfFilesToDownload
.AsParallel()
.Select(f => SaveFile(f, myModel))
.ToList();
await Task.WhenAll(tasks);
private static async Task SaveFile(string file, MyType myModel)
{
filePath = "...";
using (WebClient webClient = new WebClient())
{
await webClient.DownloadFileTaskAsync(file, filePath);
// process downloaded file
}
}
The .AsParallel() call is helpful if you have CPU-bound work you're doing after downloading the file. Otherwise you're better off without it.
As stated on this answer The whole idea behind Parallel.ForEach() is that you have a set of threads and each thread processes part of the collection so you can't await the saving part to finish. What you could do is to use Dataflow instead of Parallel.ForEach, which supports asynchronous Tasks well.
Like this:
var downloadTasks = listOfFilesToDownload.Select(file =>
{
SaveFile(file, myModel);
});
var downloaded = await Task.WhenAll(customerTasks);
You await until all the files are saved.
Other answers on the same question might be useful to you.

How can I tell if my code is running async?

This is my first attempt to get code to run async and I can't tell if it actually is. The function download report does not have an "await" and there is a warning saying it will run synchronously.
I am attempting to download multiple reports at the same time and then zip them all into one file.
The result is as expected but I would like to be certain that the code is actually performing in async.
static async Task Main(string[] args)
{
string folderName = "Batch123";
string fullDir = Path.Combine(ConfigurationManager.AppSettings["path"], folderName);
Directory.CreateDirectory(fullDir);
await RunReports(folderName);
string zipPath = Path.Combine(ConfigurationManager.AppSettings["path"], "Zip", folderName);
Directory.CreateDirectory(zipPath);
ZipFile.CreateFromDirectory(fullDir, Path.Combine(fullDir, zipPath, "CRAs.zip"));
}
private static async Task RunReports(string folderName)
{
string[] dunsToProcess = new string[] {"d1"
,"d2"
,"d3"
};
await Task.WhenAll(dunsToProcess.Select(i => DownloadReport(i, folderName)));
}
private static async Task DownloadReroport(string DUNS, string folderName)
{
NetworkCredential cred = new NetworkCredential(ConfigurationManager.AppSettings["networkUser"]
, ConfigurationManager.AppSettings["networkPassword"]);
string fullPath = Path.Combine(ConfigurationManager.AppSettings["path"], folderName, string.Format("CRA for DUNS {0}.pdf", DUNS));
WebClient wc = new WebClient();
wc.Credentials = cred;
wc.DownloadFile(#"http://xxxxxxx&pcDUNS=" + DUNS
, fullPath);
}
I hope it is right as it will be the basis of a lot of other changes. If not, can you point out what I am doing wrong.
Feel free to ridicule anything with my code!!! I have had no c# training at all.
Thank you.
I believe you are referring and threads and tasks and trying to find something async.
As First, I am not sure what you are doing and how this even works when your main method is Task, this make no sense to me, so, try following :
let main to be as this is default, there is nothing to do in this declaration change
made class which is doing what you want to do (downloads probably)
then made Tasks or threads to call many tasks or threads in time. Keep in mind that there is no point to made to many of them, you may want to limit number of tasks/threads executing in time. You may want to have all tasks identified, or you may want just to wait for all of them to be executed, your need and decision, but Task have properites IsCompleted, IsCanceled, etc ... and if you are generating threads you may want to investigate following classes and methods :
ManualResetEvent
WaitHandle
As short resume, you approach is wrong if I am even able to understand what you are trying to do in this code

Use AsyncTask for Downloading files and show the Progress in a Progress Bar

I'm currently working on a small Mini-game. The Game has the ability to open some Options, in the Options you can click "Enable Music" for example. Because the Application would be too big including the Audio files, I want the User to choose if he wants Music, and has to Download it, or not.
I'm using an Async-Task for this Option since the Options should Pop-Up while downloading, this is my Code so far:
public void CheckForFiles()
{
// searches the current directory and sub directory
int fCount = Directory.GetFiles(path, "*", SearchOption.AllDirectories).Length;
//If there are NOT all of the Audiofiles, delete all of them and re-download!
if (fCount != 2)
{
//Just to be sure, delete all previous files!
Array.ForEach(Directory.GetFiles(#"C:\Users\Public\Documents\Ultimate Tic-Tac-Toe\Audios\"), File.Delete);
//Change the Button from "Apply" to "Download"
apply_options.Text = "Download";
//Set the Warning Text
warning_text.Text = "Warning! Downloading the needed Audiofiles. DO NOT interrupt the Proccess!";
//SHow the Download-Progress Bar
download_progress.Visible = true;
//Download all
WebClient webClient = new WebClient();
webClient.DownloadProgressChanged += (s, y) =>
{
download_progress.Value = y.ProgressPercentage;
};
webClient.DownloadFileCompleted += (s, y) =>
{
download_progress.Visible = false;
warning_text.Text = "Complete!";
CheckForFiles();
};
webClient.DownloadFileAsync(new Uri(remoteUri_dark), fileName_dark);
//Text = "Downloading File one of" + WAITLIST;
}
This downloads one file, but I need two.
So I tried to wait for my Progress-Bar to fill, download the next one, and if I have 2 Files, Finish! But the Code jumps right into "DownloadFileCompleted", so that does not work. I'm sitting at this for round about 2 hours now, fiddling around.
How do I get the Async Task to create a "Que" of two files, download them and then jump to "DownloadFileCompleted" and still show the Progress?
Thank you!
Something that might be useful to understand async-await might be Eric Lippert's restaurant metaphor that explains very well why you would like to use async-await and in which cases not. Search somewhere half way the page for the question What's the difference between Asynchrony and Parallelism?
Stephen Cleary explains the basics in this article
The nice thing about async-await is that your program looks sequential and can be read as-if sequential, while in fact as soon as it has to wait for something your thread looks around if it can do something else instead of waiting. In Eric Lippert's restaurant metaphor: instead of waiting until the bread is toasted it starts boiling water to make some tea.
One of the things you forgot to do is to await for the download async. As Stephen Cleary explained, this can only be done if you declare your function async while returning a Task:
public async Task CheckForFiles()
{
// let your thread do all the things sequentially, until it has to wait for something
// you don't need the events, just await for the DownloadAsync to complete
download_progress.Visible = true;
//Download all
WebClient webClient = new WebClient();
await webClient.DownloadFileAsync(new Uri(remoteUri_dark), fileName_dark);
// if here, you know the first download is finished
ShowProgressFirstFileDownloaded();
await webClient.DownloadFileAsync( /* 2nd file */);
// if here: 2nd file downloaded
ShowProgess2ndFileDownLoaded();
download_progress.Visible = false;
}
If you want, you can start the two download simultaneously. This is not always faster:
public async Task CheckForFiles()
{
... do the preparations
//Download all
download_progress.Visible = true;
WebClient webClient = new WebClient();
var taskDownload1 = webClient.DownloadFileAsync(/* params 1st file */);
// do not await yet, start downloading the 2nd file
var taskDownload2 = webClient.DownloadFileAsync( /* params 2nd file */);
// still do not await, first show the progress
ShowProgessDownloadStarted();
// now you have nothing useful to do, await until both are finished:
await Task.WhenAll(new Task[] {taskDownload1, taskDownload2});
// if here, both tasks finished, so:
download_progress.Visible = false;
}

Get host entry async freezes

I have a problem that occurs when i have deployed the code to the production server. It does not happen locally on my computer. When i run the line
var host = await Dns.GetHostEntryAsync(adresse);
The production environment freezes or awaits indefinitely. Why does this happen?
Here is the code:
public async Task<string> HentAsync(string ip)
{
if (string.IsNullOrWhiteSpace(ip) || kLoopbackIp.Contains(ip))
{
return string.Empty;
}
if (IpHostname.ContainsKey(ip))
{
UpdateAndRemoveOldElements(ip);
return IpHostname[ip].Item1;
}
try
{
IPAddress adresse = IPAddress.None;
if (!IPAddress.TryParse(ip, out adresse))
{
return string.Empty;
}
var host = await Dns.GetHostEntryAsync(adresse);
string hostname = host.HostName;
IpHostname.AddOrUpdate(ip,
new Tuple<string, DateTime>(hostname, DateTime.Now),
(x, y) => new Tuple<string, DateTime>(hostname, DateTime.Now));
return hostname;
}
catch (Exception)
{
// Tar i mot exception og returnerer tom string
return string.Empty;
}
}
UPDATE:
The async method is called like this from a sync method that is doing the same thing (could this be the culprit).
public string Hent(string ip)
{
return HentAsync(ip).Result;
}
The most likely cause is that the origin of this call is running on the UI thread; perhaps the call to Hent() updates a UI element?
The problem is well documented here: await works but calling task.Result hangs/deadlocks
Hent() calls HentAsync() and then await Dns.GetHostEntryAsync(). This last call spawns a new thread, but wants to return to the calling thread when it has finished, which in this case is the UI thread, but it cannot do this because .Result is squatting on the thread rather than yielding it temporarily like it would have with await.
As #spender and #Dirk point out, one solution is to add .ConfigureAwait(false) to await Dns.GetHostEntryAsync(). In fact you should add this to every await where you don't care what thread context the function continues in.
This approach is still quite fragile though, as any time you forget to do it or execution takes a path with an unmodified await, the problem will happen again. The best option is to make the top-level UI-threaded function async and then await every function call that could cause a delay. This is the canonical use-case for async / await and leads to flat, easy to follow code with very little chance of deadlock.

WebClient DownloadStringAsync blocked - never finished

I have specific problem with WebClient in my Windows Phone app (using MVVM)
private string _lastCurrencyRatesJson;
private bool _lastCurrencyRatesJsonLoaded = false;
private void GetLastCoursesFromApiAsync()
{
var uri = new Uri(string.Format(OperationGetLastCourses, AppSettings.ApiEndpoint, AppSettings.ApiKey));
var client = new WebClient { Encoding = Encoding.UTF8 };
client.DownloadStringCompleted += client_DownloadStringCompleted;
client.DownloadStringAsync(uri);
}
void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
_lastCurrencyRatesJson = e.Result;
_lastCurrencyRatesJsonLoaded = true;
}
public List<CurrencyRate> GetLastCourses()
{
var worker = new Thread(GetLastCoursesFromApiAsync);
worker.Start();
while (!_lastCurrencyRatesJsonLoaded)
{
}
.....
The problem is that client_DownloadStringCompleted is never fired BUT when I change GetLastCourses this way:
public List<CurrencyRate> GetLastCourses()
{
var worker = new Thread(GetLastCoursesFromApiAsync);
worker.Start();
// whetever here, but any while...
client_DownloadStringCompleted is fired and data are obtained. It means, connectivity is ok.
I had very similar problems with DownloadStringTaskAsyn. Example:
private async Task<string> GetCoursesForDayFromApiAsJson(DateTime date)
{
var uri = new Uri(string.Format(OperationGetCoursesForDay, AppSettings.ApiEndpoint, AppSettings.ApiKey, date.ToString(DateFormat)));
var client = new WebClient { Encoding = Encoding.UTF8 };
return await client.DownloadStringTaskAsync(uri);
}
Again, at the line with await is application waiting for the data but the DownloadStringTaskAsync is never finished and my UI is still loading.
Any ideas what could be wrong?
SITUATION ONE DAY AGO
So, it looks that WP application is working just with one thread. It means, current thread have to be "finished" and then is DownloadStringTaskAsync finished and the code under the await executed. When I want to work with Task.Result I can not. Never.
When I create another Thread and I am trying to wait for thread completetion (using Join()), created Thread is never finsihed and the code after Join() is never executed.
There is any example on the Internet and I absolutely don't know, why exists some DownloadStringTaskAsync when it is not applicable.
You're blocking the UI thread by your while loop and at the same time, the DownloadStringCompleted event wants to execute on the UI loop. This causes a deadlock, so nothing happens. What you need to do is to let GetLastCourses() return (and whatever method calls that), so that the event handler can execute. This means that the code that handles the results should be in that event handler (not in GetLastCourses()).
With async-await, you didn't provide all of your code, but it's likely that you're encountering pretty much the same issue by calling Wait() or Result on the returned Task. If replace that with await, you code will work. Though that requires you to make all your code from GetCoursesForDayFromApiAsJson() up async.
I'd recommend to use the HttpClient class from Microsoft NuGet package and use the async/await downloading pattern rather than using event-based WebClient class:
Uri uri = new Uri(string.Format(OperationGetLastCourses, AppSettings.ApiEndpoint, AppSettings.ApiKey));
using (HttpClient client = new HttpClient())
{
string result = await client.GetStringAsync(uri);
}

Categories