I am using the following code to invoke a method and run it asynchronously, providing feedback on the UI as it progresses. When I run the application within Visual Studio (in either debug or release config), the "lblSyncStatus" is updated, showing 10 different statuses over the course of a 60 second sync.
public async Task SyncConfigurations()
{
var progressIndicator = new Progress<string>(ReportProgress);
var repo = new SomeObject()
var cts = new CancellationTokenSource();
string result = string.Empty;
bool syncCompleted = false;
try
{
ReportProgress("Synchronizing user data...");
int request = 0;
if (Properties.Settings.Default.FirstRun)
request = await repo.Sync(progressIndicator, cts.Token);
else
request = await repo.Sync(progressIndicator, cts.Token, Properties.Settings.Default.ConfigLastUpdate);
syncCompleted = true;
result = "Synced complete.";
}
catch (OperationCanceledException ex)
{
result = "Sync failed.";
syncCompleted = false;
}
// Timestamp of sync.
if (syncCompleted)
{
Properties.Settings.Default.ConfigLastUpdate = DateTime.Now;
Properties.Settings.Default.FirstRun = false;
Properties.Settings.Default.Save();
}
ReportProgress(result);
}
void ReportProgress(string message)
{
//Update the UI to reflect the progress value that is passed back.
Application.Current.Dispatcher.Invoke(new Action(() =>
{
this.lblSyncStatus.Content = "Sync in progress: " + message;
}));
}
The issue I have however is when I publish via ClickOnce. When I publish with ClickOnce, launch the app and run it, the label is never updated. I attached the VS debugger to the app process right after launching it and I can see the async threads exiting so I know the sync is taking place. The UI is just not reflecting this.
Why does the UI update properly under debug or release within the IDE, but not once it is deployed? I invoke the SyncConfigurations method from within the Window_Loaded method like such: Task.Run(async () => await SyncConfigurations());.
If anyone could help out with this I'd appreciate it!
EDIT 1
It seems that something is causing the SyncConfigurations to fail when I instance a new SomeObject(). I am assuming an exception is being thrown and it is not bubbling up because of the async. I added the following code right after I invoke the method:
var sync = Task.Run(async () => await SyncConfigurations());
if (sync.Exception != null)
{
throw new Exception(sync.Exception.Message + "\n" + sync.Exception.StackTrace);
}
No exceptions are picked up. I also receive a message from the IDE now when I try to attach that it can't attach due to it not being a debug build (ClickOnce deployed release and I don't know how to change that).
So I guess my question is, how should I debug this outside of the IDE, where it seems to fail, and if am I handling the exceptions correctly (if one is indeed being thrown).
I was able to resolve this. I published a build with the debug configuration, attached the debugger to the process and was able to find the exception within MyObject and fix it.
When you need to debug a ClickOnce app, you need to ensure your build config is set to Debug prior to publishing. Problem solved.
Related
[EDIT]Solved, see below[/EDIT]
this is a newbie-question.
I'm just digging in to c# and async and whyt i would like to have:
click Button
run several tasks in order but in background-thread, one after another
running tasks should notifiy their progress if possible
right now i can click the botton and start the task-chain, but within the completition event i would like (for testing) show a message-box every time a task has finished. this may lead to a crash (?) and i don't know why since i thought i would be within the ui-thread ...
here are some parts of the code:
AppViewModel:
void handlePhaseCompletedEvent(object sender, SyncPhaseCompletedEventArgs e)
{
Shell.Current.DisplayAlert("TEST", "PHASE " + e.phase.ToString(), "OK"); // <<<< doesn't show up, maybe because its crashing a short time after?
syncToolService.StartSyncPhaseAsync(e.phase + 1, this); // <<<< seems to crash here?
}
[RelayCommand]
async Task StartSyncAsync()
{
syncToolService.NotifySyncPhaseCompleted += handlePhaseCompletedEvent;
syncToolService.StartSyncPhaseAsync(0, this);
}
syncToolService:
public event EventHandler<SyncPhaseCompletedEventArgs> NotifySyncPhaseCompleted;
public async Task StartSyncPhaseAsync(int phase, AppViewModel viewModel)
{
// search for Remote-peer
if (phase == 0)
{
Task t = new Task(() => Task.Delay(100)); // dummy, not implemented yet
t.ConfigureAwait(false);
t.ContinueWith(t => NotifySyncPhaseCompleted?.Invoke(this, new SyncPhaseCompletedEventArgs { phase = phase }));
t.Start();
return;
}
// Remote Sync start preparations
if (phase == 1)
{
Task t = new Task(() => Task.Delay(100)); // dummy, not implemented yet
t.ConfigureAwait(false);
t.ContinueWith(t => NotifySyncPhaseCompleted?.Invoke(this, new SyncPhaseCompletedEventArgs { phase = phase }));
t.Start();
return;
}
//////// LOCAL PREPARATIONS
// read local files
if (phase == 2)
{
Task t = new Task(() => BPMSyncToolService.loadLocalData(viewModel.DataFiles));
t.ConfigureAwait(false);
t.ContinueWith(t => NotifySyncPhaseCompleted?.Invoke(this, new SyncPhaseCompletedEventArgs { phase = phase }));
t.Start();
return;
}
}
basicly i thought StartSyncPhaseAsync would run a Task (and it seems to do so)
and it also seems to trigger the event (whicht seems not to raise the exeption)
when running line by line in debug it crashes after syncToolService.StartSyncPhaseAsync(e.phase + 1, this);
with this stack:
> [Exception] WinRT.Runtime.dll!WinRT.ExceptionHelpers.ThrowExceptionForHR.__Throw|20_0(int hr)
[Exception] Microsoft.WinUI.dll!Microsoft.UI.Xaml.Controls.ContentDialog._IContentDialogFactory.CreateInstance(object baseInterface, out System.IntPtr innerInterface)
[Exception] Microsoft.WinUI.dll!Microsoft.UI.Xaml.Controls.ContentDialog.ContentDialog()
[Exception] Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.Platform.AlertManager.AlertRequestHelper.OnAlertRequested(Microsoft.Maui.Controls.Page sender, Microsoft.Maui.Controls.Internals.AlertArguments arguments)
System.Private.CoreLib.dll!System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Private.CoreLib.dll!System.Threading.Tasks.Task.ThrowAsync.AnonymousMethod__128_1(object state)
System.Private.CoreLib.dll!System.Threading.QueueUserWorkItemCallbackDefaultContext.Execute()
System.Private.CoreLib.dll!System.Threading.ThreadPoolWorkQueue.Dispatch()
System.Private.CoreLib.dll!System.Threading.PortableThreadPool.WorkerThread.WorkerThreadStart()
i also may have a general problem in my design, any help would be great!
[UPDATE]
it runs now as expected.
newbie-thoughts:
the answer from ToolmakerSteve https://stackoverflow.com/a/73409415/4232410 i thought "hey, but thats what i tried first and it locked UI". Then i've watched https://www.youtube.com/watch?v=2moh18sh5p4&t=0s and https://www.youtube.com/watch?v=ZTKGRJy5P2M and i saw "hey, its basicly what was mentioned and there it works, so where is my fault (now as i'm writing this i saw his update, thanks alot!)
Ryan mentioned "ReportProgress" (that was the way i stumbled across the above videos), and it worked, also thank you!
so this is basicly the actual working code that seems NOT to lock the UI and doesn't crash (the crash was because of Microsoft.VisualBasic.FileIO.TextFieldParser that tried to read a line and found a field beginning with a quote and thought it would be an enclosing quote which it wasn't)
AppViewModel:
private void HandleSyncProgressChanged(object sender, SyncStatus e)
{
NumFilesProcessed = e.filesProcessed;
NumFilesNotFound = e.filesNotFound;
AktueleAufgabe = e.workingPhase;
}
[RelayCommand]
async Task StartSyncAsync()
{
Progress<SyncStatus> progress=new Progress<SyncStatus>();
progress.ProgressChanged += HandleSyncProgressChanged;
await BPMSyncToolService.StartSyncPhaseAsync(this, progress);
}
syncToolService:
public static async Task StartSyncPhaseAsync(AppViewModel viewModel, IProgress<SyncStatus> progress)
{
SyncStatus report = new SyncStatus();
report.workingPhase = "Suche Synchronisationspartner";
progress.Report(report);
// search for Remote-peer
await Task.Delay(100); // dummy, not implemented yet
report.workingPhase = "Starte Vorbereitungen beim Synchronisationspartner";
progress.Report(report);
// Remote Sync start preparations
await Task.Delay(100); // dummy, not implemented yet
//////// LOCAL PREPARATIONS
report.workingPhase = "lese lokale Dateien";
progress.Report(report);
// read local files
await BPMSyncToolService.LoadLocalDataAsync(viewModel.DataFiles, progress, report);
// [...]
}
what i actually can't see is the counting up of processed files, maybe it's too fast, don't know, will see in further tasks that will require more time
anyways, thanks, both answers helped, i will mark the one as solution, that was closer to the core problem (i think)
Given async/await, it is almost never necessary to use task continuations or ConfigureAwait.
To start a sequence in the background, wrap the sequence in Task.Run.
To report progress on UI thread, use Dispatcher.Dispatch.
Example:
// IMPORTANT: `await`.
// Otherwise, current method would continue before Task.Run completes.
await Task.Run(async () =>
{
// Now on background thread.
...
// Report progress to UI.
Dispatcher.Dispatch(() =>
{
// Code here is queued to run on MainThread.
// Assuming you don't need to wait for the result,
// don't need await/async here.
}
// Still on background thread.
...
};
// This is effectively the "continuation": Code here runs after Task.Run completes.
...
UPDATE
In response to comment, this is how you use async/await to start a sequence of tasks, without waiting for the result:
If your top-level code does UI calls:
// This queues an independent execution to MainThread.
// We don't "await" the Dispatch, because we want it to run independently.
Dispatcher.Dispatch(async () => await TopMethod());
If your top-level code does not do UI calls:
// This queues an independent execution to the Thread Pool.
// We don't "await" the Run, because we want it to run independently.
Task.Run(async () => await TopMethod());
In either case, instead of using continuations, TopMethod uses awaits to sequence the tasks:
async void TopMethod()
{
await ..Task1..;
await ..Task2..;
await ..Task3..;
}
This is equivalent to Task1.ContinueWith(Task2.ContinueWith(Task3));
(Off the top of my head; I may not have the syntax quite right on this.)
If you are on a background thread (did Task.Run), then to do UI calls, simply wrap in Dispatcher.Dispatch( ... ). As shown in first code snippet.
You can capture SynchronizationContext in your syncToolService in constructor, or by defining explicitly API for capturing, kinda:
public void CaptureSynchronizationContext(SynchronizationContext context)
{
var current = SynchronizationContext.Current;
if (context is null)
{
this.capturedScheduler = TaskScheduler.Current;
return;
}
SynchronizationContext.SetSynchronizationContext(context);
this.capturedScheduler = TaskScheduler.FromCurrentSynchronizationContext();
SynchronizationContext.SetSynchronizationContext(current);
}
Add make some wrapper for your logic to be called in specified context:
private void RunTaskWithContinuation(Task task, Action<Task> continuation)
{
task.ConfigureAwait(false);
task.ContinueWith(t => continuation(t), capturedScheduler);
task.Start();
}
So, somewhere in your UI:
// afaik you should call it once per every Window
syncToolService.CaptureSynchronizationContext(SynchronizationContext.Current);
And your code above would look like this:
// read local files
if (phase == 2)
{
Task t = new Task(() => BPMSyncToolService.loadLocalData(viewModel.DataFiles));
RunTaskWithContinuation(t, () => NotifySyncPhaseCompleted?.Invoke(this, new SyncPhaseCompletedEventArgs { phase = phase }));
}
Not tested, but i would try this idea first.
Btw, if SynchronizationContext is null, guess your problem would be persisted.
There is space for refactoring, just wanted to show the idea.
UPDATE
There is ReportProgress type - right tool for reports in multithreaded environment. May be this is what you are looking for.
But it works the same way, as i did above - via context capturing.
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.
In my Windows Silverlight phone app, I click a button to scan a barcode and then scan the barcode sucesfuly, but whenever I press the button to scan again, it gives me a strange exception.
System.Reflection.TargetInvocationException was unhandled
Message: An unhandled exception of type 'System.Reflection.TargetInvocationException' occurred in System.Windows.ni.dll
Additional information: Exception has been thrown by the target of an invocation.
I've searched a lot about it and have an idea that maybe its due to my Thread.Sleep code within that scan function.
How can I use Task.delay instead of Thread.sleep, and would that solve the problem? I think that it is a threading problem and I am not very good with multi-threading.
Here is my code:
private async void ScanClick(object sender, EventArgs e)
{
var scanner = new MobileBarcodeScanner();
var check = true;
var opts = new MobileBarcodeScanningOptions();
opts.PossibleFormats = new List<BarcodeFormat>
{
BarcodeFormat.EAN_8,
BarcodeFormat.EAN_13
};
Thread MT = new Thread(new ThreadStart(delegate
{
while (check)
{
scanner.AutoFocus();
Thread.Sleep(autoFocusInterval);//value of autoFocus is 2000
}
}));
MT.Start();
var result = await scanner.Scan(opts);
if(result!=null)
{
if(MT.IsAlive)
{
MT.Abort();//I try to abort to prevent the error, but it doesn't help.
}
MessageBox.Show("Scanned code : " + result.Text);
result = null;
MT = null;
scanner = null;
opts = null;
check = false;
}
}
Note: even if I just click the scan button first time and open the scanner, and then press back without scanning anything, and then go back second time it produces exception.
So first time is fine no matter what, but so every second time entering scanner produce exception.
Reason for delay needed:
During scanning the library I am using (ZXing.Net.Mobile) doesn't auto focus itself, so until the barcode gets a scanned result, I have to initiate the auto focus manually, after every 2 seconds.
The exception sounds like either scanner.Autofocus or the Thread.Sleep throw an exception inside the thread which you don't handle.
To do this and use the more preferred Task.Delay you could rewrite your code as follows which also uses the more modern Task approach instead of directly working with Threads:
var cts = new CancellationTokenSource();
var ct = cts.Token;
var mtTask = Task.Run(async () =>
{
while (true) // Loop will be exited when cts.Cancel is called as Task.Delay will respond with an OperationCancelledException
{
scanner.AutoFocus();
await Task.Delay(autoFocusInterval, ct);
}
}, ct);
var result = await scanner.Scan(opts);
cts.Cancel();
try
{
await mtTask;
}
catch (OperationCancelledException)
{}
if (result != null)
{
MessageBox.Show("Scanned code : " + result.Text);
}
Additionally you could define the cts variable outside the event handler, allowing you to cancel the task when your form closes, etc. If the scanner.Scan method has an overload accepting a CancellationToken you should use this overload and provide it the ct cancellation token, so you could also cancel the scan task.
I am implementing a background task for my Windows Store Application (Win 8.1 App). I have written a very simple test class, registered it, prompted for access, but when I choose to debug the task from the debug toolbar, nothing happens. I have also waited 15 minutes multiple times today and it does not output anything. Yet, it shows (from the code perspective) that the Task is registered and I am not getting any exceptions generated.
The Background Task:
public sealed class BGFunMessage : IBackgroundTask
{
public void Run(IBackgroundTaskInstance taskInstance)
{
Debug.WriteLine("Background " + taskInstance.Task.Name + " Starting...");
}
}
The Register Class:
public class RegisterWorkers
{
public static async void Run()
{
var taskRegistered = false;
var taskName = "BGFunMessage";
BackgroundTaskRegistration bTask = null;
foreach (var task in BackgroundTaskRegistration.AllTasks)
{
if (task.Value.Name == taskName)
{
//taskRegistered = true;
bTask = (BackgroundTaskRegistration)(task.Value);
bTask.Unregister(true);
break;
}
}
if (!taskRegistered)
{
string entryPoint = typeof(BGFunMessage).FullName;
bTask = RegisterBackgroundTask(entryPoint, taskName);
}
}
public static BackgroundTaskRegistration RegisterBackgroundTask(string taskEntryPoint, string name)
{
var builder = new BackgroundTaskBuilder();
builder.Name = name;
builder.TaskEntryPoint = taskEntryPoint;
builder.SetTrigger(new TimeTrigger(15, false));
BackgroundTaskRegistration task = null;
try
{
task = builder.Register();
}
catch (Exception ex)
{
LiveLog.WriteException(ex, LogType.WARNING);
}
return task;
}
}
How I call it from a page in my app:
RegisterWorkers.Run();
I have tried following multiple tutorials, that all mostly say the same thing. I am also using the MSDN Samples downloaded from GitHub and I don't see anything on the surface that makes their code any different from mine (apart from that their Register method returns a Task<>). I am able to debug the Sample project background tasks, but not my own. Is there something I am doing incorrectly here? Thanks.
After many hours of troubleshooting, I have found the solution to my problem. I am unsure of why this must be the case, but it works. Originally (unspecified above), I had the Background Task in the same project as my app. The ONLY difference I could find across the board was that everywhere I seen, the background tasks were in a WINDOWS RUNTIME COMPONENT project. I pulled my code out and into its own project, and referenced the .DLL and now it all works fine.
I should note however, if anyone ever needs this fix--that now I no longer have access to any of my data or SyncContext from Azure Mobile Services as it currently stands. I have no idea what I am going to have to re-architect to make it work now, but the issue above is now resolved.
I will assume, that you should always have a Shared Code library project that your main app project should reference, that way my Background Task can also reference the shared project and I can still have access to my models and other data.
I'm getting a strange behavior in a winForms application while testing to see how it responds on different OS.
The long running operation where the unhandled AggregateException is thrown ( when tested on a XP 32bit machine) is part of a WCF (using basicHttpBinding and Streamed transfer mode) client update procedure. The structure is similar to the following code snippet. I have ommited WCF exception handling for clarity:
var tsUIthread = TaskScheduler.FromCurrentSynchronizationContext();
int filesCount = 0;
List<MessageContract> files;
private void Update()
{
var cTokenDownloadFiles = new CancellationTokenSource();
var cTokenUpdateDatabases = new CancellationTokenSource();
var task1CheckForNewFiles = Task.Factory.StartNew(() =>
{
filesCount = proxy.GetFiles();
If(filesCount == 0)
{
cTokenDownloadFiles.Cancel();
cTokenUpdateDatabases.Cancel();
}
else
files = new List<MessageContract>();
});
var task2UpdateControls = task1CheckForNewFiles.ContinueWith(result =>
{
UpdateControlsBeforeDownload();
}, CancellationToken.None, TaskContinuationOptions.None, tsUIthread);
var task3DownloadFiles = task2.ContinueWith(result =>
{
for(int i = 0; i< filesCount; i++)
{
try
{
files.Add(proxy.DownloadFile());
}
catch(IOException)
{
cTokenUpdateDatabases.Cancel();
Task.Factory.StartNew(() => ResetControls,
CancellationToken.None, TaskCreationOptions.None, tsUIthread);
retryUpdateTimer.Start(); // System.Windows.Forms.Timer
return;
}
}
}, cTokenDownloadFiles.Token);
var task4UpdateDatabases = task3DownloadFiles.ContinueWith(result =>
{
UpdateDatabases();
},cTokenUpdateDatabases.Token);
var task5UpdateControls = task4UpdateDatabases.ContinueWith(result =>
{
UpdateControlsOnUpdateFinished();
}, CancellationToken.None, TaskContinuationOptions.None, tsUIthread);
}
You'll notice that I wrap the proxy.DownloadFile() method in a try-catch block where I'm trapping an IO.IOException. As I meantioned in the beggining of my post, my WCF services use the Streamed transfer mode of the basicHttpBinding so I need to catch this type of exception when for some reason the connection to the server is lost after the operation has begun. All I do In the catch statement is, cancelling the databases update task, reset the UI controls to their primary values and start a System.Windows.Forms.Timer with an Interval of a few seconds what will execute the Update() method again if the client has internet connectivity.
While this whole procedure works as expected on my dev environment (Windows7 32bit machine) when an IOException is thrown , when I test my winforms Application on a Windows XP 32bit machine an unhandled AggregateException at System.Threading.Tasks.ExceptionHolder.Finalize() terminates the Application.
Has anyone experienced anything similar? How is it possible for the exception to be thrown only on the XP machine? I still haven't tested it on other environments.
The actual code contains more tasks and continuations, calling downstream business components and I'm kind of lost when it comes to Task exception handling with continuations spaghetti. Could you give me some examples of how I should structure the exception handling?