I'm working on a Universal Windows 10 App. At the moment I have a background task that gets triggered once the user receives a notification. The purpose of this BG task is to copy the content of the notification. The problem is that the Clipboard.setcontent method appears to be single threaded as opposed to the multi threaded BG task. I have tried using the corewindow dispatcher but that didn't work, I also tried using tasks. Could someone point me out to the solution please?
E.g. the following code in a background task throws the exception:
Activating a single-threaded class from MTA is not supported (Exception from HRESULT: 0x8000001D).
Code:
var dataPackage = new DataPackage { RequestedOperation = DataPackageOperation.Copy };
dataPackage.SetText("Hello World!");
Clipboard.SetContent(dataPackage);
Save the content somewhere and assign the string to the Clipboard while the app is about to resume.
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
() =>
{
var dataPackage = new DataPackage { RequestedOperation = DataPackageOperation.Copy };
dataPackage.SetText("Hello World!");
Clipboard.SetContent(dataPackage);
getText();
});
private async void getText()
{
string t = await Clipboard.GetContent().GetTextAsync();
}
Related
I am using a UWP project with a background task that is triggered by the internet being available. Once triggered, a toast notification is displayed.
The problem is that the background task seems to only run once after launching the UWP application. it even works after closing the application and restarting my computer as long as I haven't triggered it before doing so, but only if it is untriggered before restarting.
What am I doing wrong? am I missing something or misusing the background task?
For clarification, I want it to send a notification every time the internet is connected. The background task should run independent of the main application.
Below is the code for the background task:
namespace AppService
{
public sealed class testNoteUpdaterTask : IBackgroundTask
{
BackgroundTaskDeferral _deferral; // Note: defined at class scope so that we can mark it complete inside the OnCancel() callback if we choose to support cancellation
public void Run(IBackgroundTaskInstance taskInstance)
{
// Get a deferral so that the service isn't terminated.
_deferral = taskInstance.GetDeferral();
// Construct the content
new ToastContentBuilder()
.AddArgument("action", "testNote")
.AddArgument("conversationId", 9813)
.AddText("Program update avaliable for testNote")
// Buttons
.AddButton(new ToastButton()
.SetContent("testNote stuff")
.AddArgument("action", "open")
.SetBackgroundActivation())
.Show();
_deferral.Complete();
}
}
}
And here is the code which I use to register the background task inside the main UWP application:
public static BackgroundTaskRegistration RegisterBackgroundTask(string taskEntryPoint, string taskName, IBackgroundTrigger trigger, IBackgroundCondition condition = null)
{
// Check for existing registrations of this background task.
foreach (var cur in BackgroundTaskRegistration.AllTasks) {
if (cur.Value.Name == taskName){
// The task is already registered.
return (BackgroundTaskRegistration)(cur.Value);
}
}
// Register the background task.
var builder = new BackgroundTaskBuilder();
builder.Name = taskName;
builder.TaskEntryPoint = taskEntryPoint;
builder.SetTrigger(trigger);
if (condition != null) {
builder.AddCondition(condition);
}
BackgroundTaskRegistration task = builder.Register();
return task;
}
public MainPage()
{
this.InitializeComponent();
RegisterBackgroundTask("AppService.testNoteUpdaterTask", "testNoteUpdaterX", new SystemTrigger(SystemTriggerType.InternetAvailable, true));
}
I checked your code. It seems that when you are registering the SystemTrigger here:
new SystemTrigger(SystemTriggerType.InternetAvailable, true)
You are setting the oneShot parameter as true, which means the system event trigger will be used only once. Please set this value to false if you want the system event trigger to be used every time the event occurs.
More information here:SystemTrigger(SystemTriggerType, Boolean) Constructor.
Please use the following code:
new SystemTrigger(SystemTriggerType.InternetAvailable, false)
You could also take a look at the official background task sample here:
BackgroundTask Sample line 166.
I'm trying to chain tasks, so as soon as the one finishes the next starts, but the UI doesn't update. I did a course in react and the one lesson is where you update the UI based on state changes in the application, and that is what I'm trying to replicate. Change the state of the application (basically I'll be running methods that run return a bool for validation), and then update the UI accordingly, I'm also using binding, but for some reason its not running as intended, I don't know if I follow the documentation incorrectly. What can I change or fix to make this work and is it practically correct to use more than one task in a single async Task<T> method
public async Task<string> Connect_To_Ip()
{
await Task.Run(() =>
{
details.State = "Connection To IP 127.0.01.258.....";
Task.Delay(5000).Wait();
}).ContinueWith(result => new Task(async () =>
{
await Task.Run(() =>
{
if (result.Status == TaskStatus.RanToCompletion)
{
details.State = "Validating Card Number......";
}
});
}), TaskContinuationOptions.OnlyOnRanToCompletion);
return details.State;
}
How I'm calling the original task
Task connect = Connect_To_Ip();
await connect;
When you use await then you don't need Task.ContinueWith. Everything that follows the awaited operation is a continuation. Since you want to validate on a background thread, you must post the changes back to the UI thread in order to update the UI elements, otherwise you will produce cross-thread exceptions.
This is because UI elements can't be updated from a background thread, except the update occurs via INotifyPropertyChanged and data binding.
One way to do this is to use the Dispatcher to invoke UI manipulations on the UI thread or use the Progress<T> class, which will always execute the registered callback on the UI thread.
Your fixed and simplified code could look like this example:
public async Task ValidateAsync()
{
// Register the callback that updates the UI with the 'progressReporter'.
// Progress<T> must be instantiated on the UI thread it is associated with
var progressReporter = new Progress<string>(message => details.State = message);
// Execute the operation on a background thread
await Task.Run(() => ConnectToIp(progressReporter));
// Continuation starts here, after await
}
public async Task ConnectToIp(IProgress<string> progressReporter)
{
progressReporter.Report("Connection To IP 127.0.01.258.....");
await Task.Delay(TimeSpan.FromSeconds(5));
// Continuation starts here, after await
progressReporter.Report("Validating Card Number......");
}
It is recommended to use async APIs when possible instead of using background threads. For example, to connect to a server without blocking the UI you can use
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync("http://www.contoso.com/");
Many IO classes provide an async API.
Furthermore, I recommend to take a look at the INotifyDataErrorInfo interface. It is the recommended way to implement property validation and allows to provide UI error feedback in a very easy way.
I did this in Windows Forms (I had a test Windows Forms project open), but it should be about the same in WPF. I dropped a button, a label and a text box on the form. Then I wrote this code:
private async void button1_Click(object sender, EventArgs e)
{
var result = await ValidateTextBox();
if (result != null)
{
label1.Text = result;
return;
}
var intResult = await ReadTextBox();
label1.Text = intResult.ToString();
await IncrementTextBox();
intResult = await ReadTextBox();
label1.Text = intResult.ToString();
}
private async Task<string> ValidateTextBox()
{
await Task.Delay(2000);
if (!int.TryParse(textBox1.Text, out _)) {
return "Not Valid";
}
//otherwise
return null;
}
private async Task<int> ReadTextBox()
{
await Task.Delay(3000);
if (!int.TryParse(textBox1.Text, out var result))
{
throw new Exception("Don't do that");
}
return result;
}
private async Task IncrementTextBox()
{
await Task.Delay(3000);
if (!int.TryParse(textBox1.Text, out var result))
{
throw new Exception("Don't do that");
}
textBox1.Text = (result + 1).ToString();
}
If you type something that's not an int into the text box and press the button, a few seconds go by, and then Not Valid shows up in the label.
If there is a number there, then there is a pause and the number shows up in the label. Then another pause and the text box number will increment by 1. Finally after another pause, the label will show the incremented value.
Note that this all runs on a single thread. But, in spite of all the delays, the UI remains responsive the whole time.
Put breakpoints at the start of each function and on the lines after each of the awaits in the button click handler. Step through (not into) the whole thing and you'll see how the awaits create continuations
I am trying to play a sound from a BackgroundActivated Task (Single Process) that is activated as a result of Windows Notification Service WNS event.
If the Applications is launched in the Foreground and then minimized ... Things work fine.
If the Application is not currently launched ... This does not work.
My App's OnBacgroundActivated:
protected override async void OnBackgroundActivated(BackgroundActivatedEventArgs args) {
PersistLog.v(TAG, "OnBackgroundActivated");
await VANotification.Process(args.TaskInstance);
//base.OnBackgroundActivated(args);
PersistLog.v(TAG, "OnBackgroundActivated:Completed");
}
The VANotification.Proces
public async Task Process(IBackgroundTaskInstance taskInstance) {
PersistLog.v(TAG, "Process");
var Deferral = taskInstance.GetDeferral();
RawNotification notification = (RawNotification)taskInstance.TriggerDetails;
var msg = notification.Content;
PersistLog.i(TAG, "Msg:" + msg);
var a = Record(msg);
await TTSSandSound.Say(a);
Deferral.Complete();
PersistLog.v(TAG, "Process:Complete!");
}
The TTSAndSound.Say ...
public static async Task Say(String text) {
PersistLog.v(TAG, "Say:" + text);
sSynthesize.Voice = SpeechSynthesizer.DefaultVoice;
var ss = sSynthesize.SynthesizeTextToStreamAsync(text);
var t = new TaskCompletionSource<MediaSource>();
ss.Completed = (stream, status) => {
var sr = stream.GetResults();
MediaSource ms = MediaSource.CreateFromStream(sr,sr.ContentType);
t.TrySetResult(ms);
};
var _ms = await t.Task;
var r = await WaitFor(_ms);
PersistLog.v(TAG, "Say:Complete:" + r);
}
private static async Task<bool> WaitFor(MediaSource ms) {
var mp = new MediaPlayer();
mp.Source = ms;
var t = new TaskCompletionSource<bool>();
mp.MediaEnded += (p, o) => {
t.TrySetResult(true);
};
mp.MediaFailed += (p, o) => {
t.TrySetResult(true);
};
mp.Play();
return await t.Task;
}
I am actually not sure if this is a MediaPlayer problem or the fact that the BackgroundActivated task does not seem to honer the deferal. I can see from the log that the MediaPlayer finishes properly. But I get an App Suspending event about the same time I try to do a MediaPlayer Play ... And then none of the async code after the call to:
await TTSSandSound.Say(a);
Seems to run. I have followed:
Background media playback sample
NOTE ... THis always works when the App is in activated in forground mode when the WNS arrives.
The problem appears to be related to the fact that often the Background Activated Task in the same process as the foreground is being suspended before the MediaPlayer is setup properly.
In a few test cases .. the MediaPlayer seems to get setup properly ... and in those cases the MedialPlayer works correctly. But the async tasks not associated with the MediaPlayer still throw exceptions: .
at Hap.App.<OnBackgroundActivated>d_27.MoveNext()
This seems to be related to the Timing of the app being suspended. It seems that a Local Background Activated Task that is triggered by a WNS is only allowed less than 1 second before being suspended.
I'm working on a UWP app designed for phones. It's designed to sync data with a server running on your local home network. This syncing might take quite some time so a background task isn't the best place to sync the data; it'll probably take more than the 30 seconds I'm allotted. The idea, however, is to use a background task with a timer trigger; it'll call the server to check if there are any updates to consume and then pop up a toast notification asking if it can run in the foreground to perform the synchronization.
The code works great... if the screen is on. But if the screen is turned off, then I never get any notifications. At first I thought the timertrigger wasn't triggering, but I logged whenever it ran and sure enough, ir ran every 15 minutes on time. I looked deeper into it, and it's failing. Specifically, it's failing on the network call; HttpClient.GetAsync, with the following error:
"The text associated with this error code could not be found.\r\n\r\nA connection with the server could not be established\r\n"
Now I checked the server; it's running. I turn the screen on and the code suddenly works again. I've set up the trigger to only run when an unmetered connection is available:
var status = await BackgroundExecutionManager.RequestAccessAsync();
if(status.In(BackgroundAccessStatus.DeniedBySystemPolicy, BackgroundAccessStatus.DeniedByUser))
{
return;
}
var builder = new BackgroundTaskBuilder();
builder.Name = Constants.BackgroundTaskName;
builder.SetTrigger(new TimeTrigger(15, false));
builder.AddCondition(new SystemCondition(SystemConditionType.FreeNetworkAvailable));
BackgroundTaskRegistration task = builder.Register();
So I would think that the timer only gets triggered when the Wifi is available. But then when I actually perform the HTTP Get using this code:
async protected override void OnBackgroundActivated(BackgroundActivatedEventArgs args)
{
if (BackgroundWorkCost.CurrentBackgroundWorkCost == BackgroundWorkCostValue.High)
return;
if (!NetworkInterface.GetIsNetworkAvailable())
return;
base.OnBackgroundActivated(args);
if (args.TaskInstance.Task.Name == Constants.BackgroundTaskName)
{
var cancel = new CancellationTokenSource();
args.TaskInstance.Canceled += (s, e) =>
{
cancel.Cancel();
cancel.Dispose();
};
var deferral = args.TaskInstance.GetDeferral();
try
{
HttpClient client = GetClient();
var response = await client.GetAsync(ConstructUrl(client.BaseAddress, "updates"), cancel.Token);
var info = await ParseHttpResponse<UpdateInformation>(response);
}
catch { }
finally
{
deferral.Complete();
}
}
Now the funny thing is, NetworkInterface.GetIsNetworkAvailable() returns "true", telling me there's a network available. But still, when I make the call, I get "A connection with the server could not be established". I have no idea what I'm doing wrong here.
Any ideas?
It is very likely that you are required to specify "IsNetworkRequested" on your background task registration in order for the network to be functional during connected standby (which occurs while the screen is off).
Refer to the documentation here:
https://learn.microsoft.com/en-us/uwp/api/Windows.ApplicationModel.Background.BackgroundTaskBuilder
Though this is an old Question, this is still valid as of today.. I got the answer from this post HttpClient GetAsync fails in background task on Windows 8 ..
Adding the answer here for future developers who run into this issue.
The HttpClient will not work when call to the method, in which it is instantiated is not await - ed as the background task will not wait for the HttpClient to finish its task and continue execution to the end of the flow.
For example:
The following won't work when the initiate() method is called from the Run(IBackgroundTaskInstance taskInstance) of your background task class.
//In Your background task class
public void Run(IBackgroundTaskInstance taskInstance)
{
BackgroundTaskDeferral deferral = taskInstance.GetDeferral();
initiate();
deferral.Complete();
}
public async void initiate()
{
//some code
HttpClient client = new HttpClient();
HttpResponseMessage responseMessage = await client.GetAsync(new Uri(url));
}
Solution:
//In Your background task class
public async void Run(IBackgroundTaskInstance taskInstance)
{
BackgroundTaskDeferral deferral = taskInstance.GetDeferral();
await initiate();
//Background task will wait for the initiate() to complete then call defferal.Complete().
deferral.Complete();
}
public async Task initiate()
{
//some code
HttpClient client = new HttpClient();
HttpResponseMessage responseMessage = await client.GetAsync(new Uri(url));
}
I am writing Windows Phone 8.1 Application using GeoFence API. My problem is that I can't trigger change of location in Background Task, because app exits with code 1.
I have read multiple threads about this error, but no solution solved my problem.
I have checked if my BackgroundTask is a Runtime Component, and it is.
I have checked name of my class and it is correct.
I have checked if I use any await function in my BackgroundTask function and I didn't find any.
I have checked if I registered Background Task in app manifest and yes, I did (with entry point ofc)
In fact error appears even before running Run function from BackgroundTask.
namespace BackgroundTask
{
public sealed class geoFenceBackgroundTask : IBackgroundTask
{
public void Run(IBackgroundTaskInstance taskInstance)
{
ToastTemplateType toastTemplate = ToastTemplateType.ToastText02;
XmlDocument toastXml = ToastNotificationManager.GetTemplateContent(toastTemplate);
XmlNodeList toastTextElements = toastXml.GetElementsByTagName("text");
toastTextElements[0].AppendChild(toastXml.CreateTextNode("MY APP"));
toastTextElements[1].AppendChild(toastXml.CreateTextNode("Test"));
//IXmlNode toastNode = toastXml.SelectSingleNode("/toast");
//XmlElement audio = toastXml.CreateElement("audio");
ToastNotification toast = new ToastNotification(toastXml);
ToastNotificationManager.CreateToastNotifier().Show(toast);
}
}
}
And my register function:
async private void RegisterBackgroundTask()
{
// Get permission for a background task from the user. If the user has already answered once,
// this does nothing and the user must manually update their preference via PC Settings.
BackgroundAccessStatus backgroundAccessStatus = await BackgroundExecutionManager.RequestAccessAsync();
// Regardless of the answer, register the background task. If the user later adds this application
// to the lock screen, the background task will be ready to run.
// Create a new background task builder
BackgroundTaskBuilder geofenceTaskBuilder = new BackgroundTaskBuilder();
geofenceTaskBuilder.Name = "geoFenceBackgroundTask";
geofenceTaskBuilder.TaskEntryPoint = "BackgroundTask.geoFenceBackgroundTask";
// Create a new location trigger
var trigger = new LocationTrigger(LocationTriggerType.Geofence);
// Associate the locationi trigger with the background task builder
geofenceTaskBuilder.SetTrigger(trigger);
// If it is important that there is user presence and/or
// internet connection when OnCompleted is called
// the following could be called before calling Register()
// SystemCondition condition = new SystemCondition(SystemConditionType.UserPresent | SystemConditionType.InternetAvailable);
// geofenceTaskBuilder.AddCondition(condition);
// Register the background task
var geofenceTask = geofenceTaskBuilder.Register();
geofenceTask.Completed += (sender, args) =>
{
// MY CODE HERE
};
geofenceTask = geofenceTaskBuilder.Register();
}
I have no other ideas. Any help?
just stumbled upon a similar issue (Visual Studio stopped Debugging when I started the Background Task over the "Lifecyle-Events-Dropdown", stating "BACKGROUNDTASKHOST.EXE' has exited with code 1 (0x1)" in the Console-Output-Window.
Adding the missing reference to my Tasks-Assembly (winmd) Project in the WP8 Project solved it.
The project BackgroundTask need be (windows runtime component).