System.Threading.Tasks.TaskCanceledException: 'A task was canceled.' when Closing App - c#

I have a thread that change the field
private void SongChange(object sender, RoutedEventArgs e)
{
SongChangeAction();
songLength = (int)BGMPlayer.NaturalDuration.TimeSpan.TotalSeconds;
songProgress = new Thread(SongProgressUpdate) { IsBackground = true };
songProgress.Start();
}
private void SongProgressUpdate()
{
while (true)
{
Dispatcher.Invoke(() => { workingResources.SongProgress = BGMPlayer.Position.TotalSeconds / songLength * 100; });
Thread.Sleep(1000);
}
}
Note: songLength is a double, BGMPlayer is a MediaElement to play music in background, workingResources.SongProgress is a double bind to a Progress Bar
When I close the Program using "X" button or on taskbar, The program threw exception System.Threading.Tasks.TaskCanceledException Even I try to set the thread IsBackground = true but it work not throw exception If I stop the program using Visual Studio.
The Program Throw exception at line:
Dispatcher.Invoke(() => { workingResources.SongProgress = BGMPlayer.Position.TotalSeconds / songLength * 100; });
I can prevent it by using try/catch but I want to find something more efficient

Try using a CancellationToken on close of the window:
private readonly CancellationTokenSource _shutDown = new CancellationTokenSource();
public WindowName()
{
this.Closed =+ (s, e) => this._shutDown.Cancel();
}
private void SongChange(object sender, RoutedEventArgs e)
{
SongChangeAction();
songLength = (int)BGMPlayer.NaturalDuration.TimeSpan.TotalSeconds;
songProgress = Task.Factory.StartNew(SongProgressUpdate, this._shutDown.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
}
private void SongProgressUpdate()
{
while (!this._shutDown.IsCancellationRequested)
{
Dispatcher.Invoke(() => { workingResources.SongProgress = BGMPlayer.Position.TotalSeconds / songLength * 100; });
Thread.Sleep(1000);
}
}

Try using Microsoft's Reactive Framework. Then you can do this:
private void SongChange(object sender, RoutedEventArgs e)
{
SongChangeAction();
songLength = (int)BGMPlayer.NaturalDuration.TimeSpan.TotalSeconds;
IDisposable subscription =
Observable
.Interval(TimeSpan.FromSeconds(1.0))
.ObserveOnDispatcher()
.Subscribe(x => workingResources.SongProgress = BGMPlayer.Position.TotalSeconds / songLength * 100);
}
You just need to make sure that you save a reference to subscription and call .Dispose() on it when you are closing your app.
NuGet "System.Reactive" and "System.Reactive.Windows.Threading", and add using System.Reactive.Linq; to your code.

Related

Stopping and starting tasks using CancellationTokenSource

this is just a simplified version of what I am trying to do
public partial class MainWindow : Window
{
private CancellationTokenSource tokenSource = new CancellationTokenSource();
public MainWindow()
{
InitializeComponent();
PrintButtonText("None");
}
private void PrintButtonText(string buttonText)
{
Console.WriteLine("Update!");
Task.Factory.StartNew(() =>
{
while (!tokenSource.Token.IsCancellationRequested)
{
Console.WriteLine("Button Pressed Text: " + buttonText);
}
}, tokenSource.Token);
}
private void Button1_Click(object sender, RoutedEventArgs e)
{
tokenSource.Cancel();
PrintButtonText("Button1");
}
private void Button2_Click(object sender, RoutedEventArgs e)
{
tokenSource.Cancel();
PrintButtonText("Button2");
}
}
After I do
tokenSource.Cancel();
PrintButtonText("Button1");
It's not able to start the task again and continue to print my line. I need it to work this way for my program.
I want to stop the thread and start it again with some different parameters. How can I achieve this? thanks
EDIT:
Since I didn't get a solution with my simplified version, here is full code and what I am trying to do. Basically, on wpf window camera rendering starts on start. There is a button to start saving to file, but in order to save I have to update the config and start "pipeline" again.
public partial class MainWindow : Window
{
private Pipeline pipeline = new Pipeline(); // Create and config the pipeline to sream color and depth frames.
private CancellationTokenSource tokenSource = new CancellationTokenSource();
private bool saveDataToFile = false;
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
Config cfg = SetupConfig(false);
PipelineProfile pp = pipeline.Start(cfg);
StartRenderFrames(pp);
}
private void StartRenderFrames(PipelineProfile pp)
{
Colorizer colorizer = new Colorizer(); // The colorizer processing block used to visualize the depth frames.
// Allocate bitmaps for rendring. Since the sample aligns the depth frames to the color frames, both of the images will have the color resolution
using (var p = pp.GetStream(Stream.Color) as VideoStreamProfile)
{
imgColor.Source = new WriteableBitmap(p.Width, p.Height, 96d, 96d, PixelFormats.Rgb24, null);
imgDepth.Source = new WriteableBitmap(p.Width, p.Height, 96d, 96d, PixelFormats.Rgb24, null);
}
Action<VideoFrame> updateColor = UpdateImage(imgColor);
Action<VideoFrame> updateDepth = UpdateImage(imgDepth);
Task.Factory.StartNew(() =>
{
while (!tokenSource.Token.IsCancellationRequested)
{
// Wait for the next available FrameSet
using (var frames = pipeline.WaitForFrames())
{
var colorFrame = frames.ColorFrame.DisposeWith(frames);
var depthFrame = frames.DepthFrame.DisposeWith(frames);
// We colorize the depth frame for visualization purposes, .
var colorizedDepth = colorizer.Process(depthFrame).DisposeWith(frames);
// Render the frames.
Dispatcher.Invoke(DispatcherPriority.Render, updateDepth, colorizedDepth);
Dispatcher.Invoke(DispatcherPriority.Render, updateColor, colorFrame);
}
}
}, tokenSource.Token);
}
private Config SetupConfig(bool saveDepthFile)
{
Config cfg = new Config();
cfg.EnableStream(Stream.Depth, 640, 480, framerate: 15);
cfg.EnableStream(Stream.Color, 640, 480, format: Format.Rgb8, framerate: 15);
if (saveDepthFile)
{
cfg.EnableRecordToFile(#"C:\temp\My_test111.bag");
}
return cfg;
}
static Action<VideoFrame> UpdateImage(Image img)
{
var wbmp = img.Source as WriteableBitmap;
return new Action<VideoFrame>(frame =>
{
using (frame)
{
var rect = new Int32Rect(0, 0, frame.Width, frame.Height);
wbmp.WritePixels(rect, frame.Data, frame.Stride * frame.Height, frame.Stride);
}
});
}
private void StartSaving_Button_Click(object sender, RoutedEventArgs e)
{
tokenSource.Cancel();
pipeline.Stop();
// This is where I have a problem. Rendering thread does not stop before I want to start again.
Config cfg = SetupConfig(true);
PipelineProfile pp = pipeline.Start(cfg);
StartRenderFrames(pp);
}
}
You should use Microsoft's Reactive Framework (aka Rx) - NuGet System.Reactive.Windows.Forms and add using System.Reactive.Linq; - then you can do this:
private void Form1_Load(object sender, EventArgs e)
{
IObservable<string> button1Clicks =
Observable
.FromEventPattern<EventHandler, EventArgs>(h => button1.Click += h, h => button1.Click -= h)
.Select(ep => "Button1");
IObservable<string> button2Clicks =
Observable
.FromEventPattern<EventHandler, EventArgs>(h => button2.Click += h, h => button2.Click -= h)
.Select(ep => "Button2");
IDisposable subscription =
button1Clicks
.Merge(button2Clicks)
.StartWith("None")
.Select(x => Observable.Timer(TimeSpan.Zero, TimeSpan.FromMilliseconds(500.0)).Select(n => x))
.Switch()
.ObserveOn(this)
.Subscribe(x => Console.WriteLine(x));
}
That's the entire code needed to make what you want to work.
The only thing that you need to do is move subscription out into a private field and then simply call subscription.Dispose() to shut this all down.
This is much simpler than messing with cancellation tokens.

How to use WPFtoolkit busyindicator into the WPF application

I have made an application in WPF where I am making a long conversion process. so I wanted to put progress bar window, so i have used BusyIndicator progress bar from wpftoolkit dll and trying to show when my conversion will start.
But when I click on Conversion button it showing exception -
"the calling thread cannot access this object because a different thread owns it"...
My Code -
private void ConvertBtn_OnClick(object sender, RoutedEventArgs e)
{
Task.Factory.StartNew(() =>
{
ConversionToExcel();//My conversion methode
for (int i = 0; i < 10; i++)
{
Dispatcher.Invoke(DispatcherPriority.Normal, new Action(
() => { ProgressIndicator.BusyContent = string.Format("Inprogress, please wait..."); }
));
Thread.Sleep(1000);
}
}).ContinueWith((task) => { ProgressIndicator.IsBusy = false; }, TaskScheduler.FromCurrentSynchronizationContext()
);
}
Set IsBusy=true before starting new thread.
private void ConvertBtn_OnClick(object sender, RoutedEventArgs e)
{
ProgressIndicator.BusyContent = string.Format("Inprogress, please wait...");
Task.Factory.StartNew(() =>
{
ConversionToExcel();//My conversion method
}).ContinueWith((task) => { ProgressIndicator.IsBusy = false; }, TaskScheduler.FromCurrentSynchronizationContext()
);
}
Also, I think you should have your own IsBusy property and bind it to ProgressIndicator.IsBusy in XAML.

bind log Asynchronous in winForm c#

I tried to add something to log inside WinForm while doind something
private async Task SaveChunk(DataChunkSaver chunk)
{
int i = 0;
int step = 10;
while (chunk.saveChunk(i, step))
{
i += step;
AddLog(chunk.Log);
}
}
where:
private async Task AddLog(string text)
{
LogBulider.AppendLine(text);
LogBox.Text = LogBulider.ToString();
}
AndLogBulider is a simple global StringBulider.
The problem is when I fire button with SaveChunk task my form freezes, so I can see the LogBox after everything is done and I wanned it to bisplayed after each step of chunk.saveChunk.
I tried to fire them by few methods, but I can't handle it
What Am I doing wrong?
private async void button2_Click(object sender, EventArgs e)
{
await Task.Factory.StartNew(() => SaveChunk(chunk));
Task T = SaveChunk(chunk);
// none of these works, I also tried few other
//ways to do it, but none prevents my winForm from freezing
}
I tried to modify your code using a Progress<string>:
private async void button2_Click(object sender, EventArgs e)
{
var progress = new Progress<string>(msg =>
{
LogBulider.AppendLine(msg);
LogBox.Text = LogBulider.ToString();
});
await Task.Run(() => SaveChunk(chunk, progress));
}
and
private async Task SaveChunk(DataChunkSaver chunk, IProgress<string> progress)
{
int i = 0;
int step = 10;
while (chunk.saveChunk(i, step))
{
i += step;
progress?.Report(chunk.Log); // Always use progress as if it could be null!
}
}

Asynchronously update UI

I'm playing with asynchronous operations and my goal right now is very simple. I just want to update a text field at the beginning of a large computation, and the problem I'm facing is that the text field gets updated but only when calculate() returns, even though the method computingMessage() is called immediately:
private void Window_PreviewKeyDown(object sender, KeyEventArgs e)
{
e.Handled = true;
annunciate();
calculate();
}
private void annunciate(){
Thread updateUI = new Thread( new ThreadStart( computingMessage ));
updateUI.Start();
}
private void computingMessage(){
txtVerification.Dispatcher.Invoke((Action)(
() => txtVerification.Text = "Calculating..."
));
}
Please, check Task object.
private void Button_Click(object sender, RoutedEventArgs e)
{
TextBoxOutput.Text = "calculating...";
Task.Factory
.StartNew(() => Calculate())
.ContinueWith(t =>
{
TextBoxOutput.Text = t.Result.ToString(CultureInfo.InvariantCulture);
}, TaskScheduler.FromCurrentSynchronizationContext());
}
private int Calculate()
{
Thread.Sleep(2000); //--similate working....
return Environment.TickCount ^ 43;
}
I hope it helps you.
Although I agree with #dbvega in spirit. I cannot agree with the usage of Task.Factory.StartNew. Nor should you use Dispatcher.Invoke. By default, Task.Factory.CurrentScheduler will be set to the WPF message pump scheduler when running WPF. When running WinForms, there is a WinForm scheduler that is automatically set...
private async void Button_Click(object sender, RoutedEventArgs e)
{
TextBoxOutput.Text = "calculating...";
var result = await Task.Run(Calculate);
TextBoxOutput.Text = result.ToString(CultureInfo.InvariantCulture);
}
private int Calculate()
{
Thread.Sleep(2000); //--similate working....
return Environment.TickCount ^ 43;
}

WyUpdate - C# Console application, update not responding working... how to run synchronous?

If anyone has any feedback on why my WyUpdate application doesn't want to work please let me know.
I'm trying to create a basic console application which launches the WyUpdate process, and then on completion executes my main application (which has just been updated).
Following the instructions on http://wyday.com/wybuild/help/silent-update-windows-service.php gives me no luck at all. The application runs, and executes the "ForceCheckForUpdate" process, but I don't receive any feedback :(
here is a complete listing of my code.
I've added a while loop to hopefully catch the response from the auBackend, but that doesn't seem to work. is there an easy way to run the process synchronously and wait for the response before closing the application?
Thanks in advance.
using System;
using System.Threading;
using wyDay.Controls;
namespace NPS.CeAUpdateLauncher
{
class Program
{
private static AutomaticUpdaterBackend auBackend;
private static bool receivedFeedback;
static void Main(string[] args)
{
auBackend = new AutomaticUpdaterBackend
{
//TODO: set a unique string.
// For instance, "appname-companyname"
GUID = "CeALauncher_AutoUpdate",
// With UpdateType set to Automatic, you're still in
// charge of checking for updates, but the
// AutomaticUpdaterBackend continues with the
// downloading and extracting automatically.
UpdateType = UpdateType.Automatic,
};
auBackend.CheckingFailed += auBackend_CheckingFailed;
auBackend.UpdateAvailable += auBackend_UpdateAvailable;
auBackend.DownloadingFailed += auBackend_DownloadingFailed;
auBackend.ExtractingFailed += auBackend_ExtractingFailed;
auBackend.ReadyToBeInstalled += auBackend_ReadyToBeInstalled;
auBackend.UpdateSuccessful += auBackend_UpdateSuccessful;
auBackend.UpdateFailed += auBackend_Failed;
// Initialize() and AppLoaded() must be called after events have been set.
// Note: If there's a pending update to be installed, wyUpdate will be
// started, then it will talk back and say "ready to install,
// you can close now" at which point your app will be closed.
auBackend.Initialize();
auBackend.AppLoaded();
if (!auBackend.ClosingForInstall)
{
//TODO: do your normal service work
CheckForUpdates();
}
//
while(!receivedFeedback)
Thread.Sleep(10000);
}
static void CheckForUpdates()
{
// Only ForceCheckForUpdate() every N days!
// You don't want to recheck for updates on every app start.
if (//(DateTime.Now - auBackend.LastCheckDate).TotalDays > 10 &&
auBackend.UpdateStepOn == UpdateStepOn.Nothing)
{
auBackend.ForceCheckForUpdate();
}
}
static void auBackend_CheckingFailed(object sender, FailArgs e)
{
receivedFeedback = true;
}
static void auBackend_UpdateAvailable(object sender, EventArgs e)
{
receivedFeedback = true;
}
static void auBackend_DownloadingFailed(object sender, FailArgs e)
{
receivedFeedback = true;
}
static void auBackend_ExtractingFailed(object sender, FailArgs e)
{
receivedFeedback = true;
}
static void auBackend_ReadyToBeInstalled(object sender, EventArgs e)
{
// ReadyToBeInstalled event is called when
// either the UpdateStepOn == UpdateDownloaded or UpdateReadyToInstall
if (auBackend.UpdateStepOn == UpdateStepOn.UpdateReadyToInstall)
{
//TODO: Delay the installation of the update until
// it's appropriate for your app.
//TODO: Do any "spin-down" operations. auBackend.InstallNow() will
// exit this process using Environment.Exit(0), so run
// cleanup functions now (close threads, close running programs,
// release locked files, etc.)
// here we'll just close immediately to install the new version
auBackend.InstallNow();
}
receivedFeedback = true;
}
static void auBackend_UpdateSuccessful(object sender, SuccessArgs e)
{
receivedFeedback = true;
}
static void auBackend_Failed(object sender, FailArgs e)
{
receivedFeedback = true;
}
}
}
With the code I attached in my initial post, the problem was that the console application would be running indefinitely. I thought that was because the wyUpdate process never returned a value. I found out it was because I didn't raise all events.
After implementing the eventhandler for "UpToDate" I received a response and was able to set the indicator to have the application exit successfully.
I also replaced the boolean indicator and the 'while' method of waiting for a response to the 'resetEvent' as per the vendor's instructions, and it is working very well.
static readonly ManualResetEvent resetEvent = new ManualResetEvent(false);
static void Main(string[] args)
{
...
auBackend.UpToDate += auBackend_UpToDate;
...
// Blocks until "resetEvent.Set()" on another thread
resetEvent.WaitOne();
}
static void auBackend_UpToDate(object sender, SuccessArgs e)
{
resetEvent.Set();
}
Thanks for the responses.
If anyone ever needs a very versatile application updater solution, I would suggest the wyBuild/wyUpdate package.
Njoy!
If you're able to use the Reactive Extensions for .NET (Rx) then I've got an easier alternative.
As long as the AutomaticUpdaterBackend class is firing its events on a background thread then this code should do what you want:
void Main(string[] args)
{
var auBackend = new AutomaticUpdaterBackend
{
GUID = "CeALauncher_AutoUpdate",
UpdateType = UpdateType.Automatic,
};
var checkingFailed = Observable.FromEvent<FailArgs>(h => auBackend.CheckingFailed += h, h => auBackend.CheckingFailed -= h);
var updateAvailable = Observable.FromEvent<EventArgs>(h => auBackend.UpdateAvailable += h, h => auBackend.UpdateAvailable -= h);
var downloadingFailed = Observable.FromEvent<FailArgs>(h => auBackend.DownloadingFailed += h, h => auBackend.DownloadingFailed -= h);
var extractingFailed = Observable.FromEvent<FailArgs>(h => auBackend.ExtractingFailed += h, h => auBackend.ExtractingFailed -= h);
var readyToBeInstalled = Observable.FromEvent<EventArgs>(h => auBackend.ReadyToBeInstalled += h, h => auBackend.ReadyToBeInstalled -= h);
var updateSuccessful = Observable.FromEvent<SuccessArgs>(h => auBackend.UpdateSuccessful += h, h => auBackend.UpdateSuccessful -= h);
var updateFailed = Observable.FromEvent<FailArgs>(h => auBackend.UpdateFailed += h, h => auBackend.UpdateFailed -= h);
var readyToBeInstalledStepReady = readyToBeInstalled.Where(e => auBackend.UpdateStepOn == UpdateStepOn.UpdateReadyToInstall);
var readyToBeInstalledStepNotReady = readyToBeInstalled.Where(e => auBackend.UpdateStepOn != UpdateStepOn.UpdateReadyToInstall);
var failed = Observable.Merge(new []
{
checkingFailed.Select(e => "Checking Failed"),
downloadingFailed.Select(e => "Downloading Failed"),
extractingFailed.Select(e => "Extracting Failed"),
updateFailed.Select(e => "Update Failed"),
readyToBeInstalledStepNotReady.Select(e => "Update Not Ready"),
});
var success = Observable.Merge(new []
{
readyToBeInstalledStepReady.Select(e => "Update Ready"),
updateSuccessful.Select(e => "Update Successful"),
});;
failed.Subscribe(e => Console.Error.WriteLine(e));
success.Subscribe(e => Console.WriteLine(e));
readyToBeInstalledStepReady.Subscribe(e => auBackend.InstallNow());
auBackend.Initialize();
auBackend.AppLoaded();
if (!auBackend.ClosingForInstall)
{
if (auBackend.UpdateStepOn == UpdateStepOn.Nothing)
{
auBackend.ForceCheckForUpdate();
}
}
success.Merge(failed).First();
}
Yes, there aren't any module-level variables and no event handler methods. Just locals and lambdas. :-)

Categories

Resources