Windows RT - Background Task Not Firing - c#

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.

Related

why is my background task only running once?

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.

How to handle IO-heavy workload at WPF Application startup

I'm currently trying to write a simple C#-WPF-Application that functions as a simple universal 'launcher'. For different applications we program.
It's purpose is to check the current software version of the 'real' software and if a new one is available it starts to copy the installer from a network share and runs the installer afterwards.
Then it starts the 'real' application and thats it.
The user Interface mainly consists of a startup window which shows the user the currently executed action (version check, copy, installation, startup, ...).
Now I create my view and my viewModel in the overridden StartUp method in App.cs
public override OnStartup(string[] args)
{
var viewModel = new StartViewModel();
var view = new StartView();
view.DataContext = viewModel;
view.Show();
// HERE the logic for the Launch starts
Task.Run(() => Launch.Run(args));
}
The problem is that if I don't go async here the Main Thread is blocked and I cannot update the UI. Therefore I got it working by using the Task.Run(...). This solves my problem of blocking the UI thread, but I have some problems/questions with this:
I cannot await the task, because that would block the UI again. Where to await it?
Is my concept of starting this workflow here ok in the first place?
Some update to clarify: After I show the UI to the user my logic starts to do heavy IO stuff. The possible calls I came up with are the following 3 variants:
view.Show();
// v1: completely blocks the UI, exceptions are caught
DoHeavyIOWork();
// v2: doesn't block the UI, but exceptions aren't caught
Task.Run(() => DoHeavyIOWork());
// v3: doesn't block the UI, exceptions are caught
await Task.Run(() => DoHeavyIOWork());
Currently I'm not at my work PC so i apologies for not giving you the original code. This is an on the fly created version.
I guess v1 and v2 are bad because of exceptions and the UI blocking.
I thought v3 didn't work when I tried it in my office. Now it seems to work in my local example. But I'm really not sure about v3. Because I'm using async void StartUp(...) there. Is it okay here?
I cannot await the task, because that would block the UI again. Where to await it?
await doesn't block the UI. Using await here is fine.
Is my concept of starting this workflow here ok in the first place?
I usually recommend that some UI is shown immediately when doing any asynchronous operation. Then when the async operation is complete, you can update/replace the UI.
Thanks for all the replys.
After reading all your comments and combining some of your answers I came up with the following example. It is working under all circumstances I tested.
Hopefully there is not to much wrong in your opinion.
Code behind from App.xaml.cs
public partial class App : Application
{
readonly StartViewModel viewModel = new StartViewModel();
protected override async void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var view = new StartWindow
{
DataContext = viewModel
};
view.Show(); // alternative: Run(view);
// alternative: instead of calling it here it is possible
// to move these calls as callback into the Loaded of the view.
await Task.Run(() => DoHeavyIOWork());
}
private string GenerateContent()
{
var content = new StringBuilder(1024 * 1024 * 100); // Just an example.
for (var i = 0; i < 1024 * 1024 * 2; i++)
content.Append("01234567890123456789012345678901234567890123456789");
return content.ToString();
}
private void DoHeavyIOWork()
{
var file = Path.GetTempFileName();
for (var i = 0; i < 20; i++)
{
File.WriteAllText(file, GenerateContent());
File.Delete(file);
Dispatcher.Invoke(() => viewModel.Info = $"Executed {i} times.");
}
}
}
Code in StartViewModel.cs
class StartViewModel : INotifyPropertyChanged
{
private string info;
public event PropertyChangedEventHandler PropertyChanged;
public string Info
{
get => info;
set
{
info = value;
OnPropertyChanged();
}
}
private void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

Push Notification Trigger on Windows Phone 8.1 Silverlight

I'm working on the "feature" the push notification trigger available in Windows phone 8.1. My goal is to make this work with a Windows phone Silverlight 8.1 project. As far as I know, it should work based on my reading.
After 2 days, I'm totally stuck. No matter what i'm trying. When I send a raw notification to my app, my app is canceled and I'm back to the windows menu.
The output :
The program '[1852] BACKGROUNDTASKHOST.EXE' has exited with code 1 (0x1).
The program '[2712] AgHost.exe' has exited with code 1 (0x1).
State :
The app receives the notification. It trigger OnPushNotificationReceived. (After this event, the app is canceled)
The Push Notification task is declared and the entry point is defined to Push.Notification.
I create a Windows Runtime Component for Windows phone 8.1 in order to run the background task.
The background task is well registered.
private async void RegisterBackgroundTask()
{
UnregisterBackgroundTask(taskName);
BackgroundAccessStatus backgroundStatus = await BackgroundExecutionManager.RequestAccessAsync();
if (backgroundStatus != BackgroundAccessStatus.Denied && backgroundStatus != BackgroundAccessStatus.Unspecified)
{
try
{
BackgroundTaskBuilder taskBuilder = new BackgroundTaskBuilder();
taskBuilder.Name = taskName;
PushNotificationTrigger trigger = new PushNotificationTrigger();
taskBuilder.SetTrigger(trigger);
taskBuilder.TaskEntryPoint = "Push.Notification";
BackgroundTaskRegistration task = taskBuilder.Register();
}
catch (Exception ex)
{ }
}
}
The background task :
public sealed class Notification
{
public void Run(IBackgroundTaskInstance taskInstance)
{
Debug.WriteLine("Background starting...");
Debug.WriteLine("Background completed!");
}
}
I have no clue about what I'm doing wrong.
Is there someone who make it works ? (Not in theory)
For information, I have 2 warnings that worrying me :
I have a mismatch in the process architecture in my 2 projects. My windows phone project use "Any CPU" (can"t change that). My windows runetime component project use "ARM". If I use "Any CPU" wor my WinRT, i got an errror :
/platform:anycpu32bitpreferred can only be used with /t:exe, /t:winexe and /t:appcontainerexe
There is a conflict on a dependent assembly of the 2 projects. Seems to be
Found conflicts between different versions of the same dependent assembly. In Visual Studio, double-click this warning (or select it and press Enter) to fix the conflicts; otherwise, add the following binding redirects to the "runtime" node in the application configuration file:
Thanks in advance
I did finally make it works.
There was an error in my initial code I forgot to implement IBackgroundTask :
public sealed class Notification : IBackgroundTask
{
public void Run(IBackgroundTaskInstance taskInstance)
{
Debug.WriteLine("Background starting...");
Debug.WriteLine("Background completed!");
}
}
And I had to make my Task async...
public async void Run(IBackgroundTaskInstance taskInstance)
{
BackgroundTaskDeferral deferral = taskInstance.GetDeferral();
// perform your async task and then call deferral complete
await Test();
deferral.Complete();
}
private static async Task Test()
{
//TODO with an await
}
Hope this'll help someone.

Windows Phone 8.1 Background Task - Can't Debug and won't fire

Im having a issue with the Background Tasks in WP8.1
I have created a background task as a windows runtime component following this tutorial :
http://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh977055.aspx
The problem is, i can't get my background task to run. It runs onNetworkChange. When i can to flight mode and back it is not firing. When i go to lifecycle events in the Debug Location toolbar it says No Background tasks. I have debugged the code that registers the background task and it is getting registered. I am also getting 'This breakpoint will not currently be hit. No symbols have been loaded for this document' which i think is causing the problem.
I have tried
- deleting the bin and obj folder and rebuilding.
- cleaning the project.
- trying to build the project from scratch.
- turning Just my code option off.
- tried doing the same thing on another machine, still nothing.
My code for registering
var taskRegistered = false;
var exampleTaskName = "UploadTask";
foreach (var task in BackgroundTaskRegistration.AllTasks)
{
if (task.Value.Name == exampleTaskName)
{
taskRegistered = true;
break;
}
}
if (!taskRegistered)
{
var builder = new BackgroundTaskBuilder();
builder.Name = exampleTaskName;
builder.TaskEntryPoint = "Tasks.Upload";
builder.SetTrigger(new SystemTrigger(SystemTriggerType.NetworkStateChange, false));
BackgroundTaskRegistration task = builder.Register();
}
package manifest file is as follows
<Extensions>
<Extension Category="windows.backgroundTasks" EntryPoint="Tasks.Upload">
<BackgroundTasks>
<Task Type="systemEvent" />
<m2:Task Type="deviceUse" />
</BackgroundTasks>
</Extension>
</Extensions>
My task looks like this :
namespace Tasks
{
public sealed class Upload : IBackgroundTask
{
public void Run(IBackgroundTaskInstance taskInstance)
{
Debug.WriteLine("Am i even getting here?");
}
}
}
Can anyone help as i've spent far too long getting this to work. Thanks
As I've tried your code, there is a problem with this specific SystemTriggerType.NetworkStateChange - indeed I also don't see the registered BackgroundTask in Lifecycle Events dropdown. But if I only change the SystemTriggerType for example to SystemTriggerType.TimeZoneChange then I'm able to see it.
Here is the code modified a little:
await BackgroundExecutionManager.RequestAccessAsync();
if (!taskRegistered)
{
Debug.WriteLine("Registering task inside");
var builder = new BackgroundTaskBuilder();
builder.Name = exampleTaskName;
builder.TaskEntryPoint = "Tasks.Upload";
builder.SetTrigger(new SystemTrigger(SystemTriggerType.TimeZoneChange, false));
BackgroundTaskRegistration task = builder.Register();
await new MessageDialog("Task registered!").ShowAsync();
}
I'm not sure why with the original code the BackgroundTask is not visible in VS, though it is being registered - it's in BackgroundTaskRegistration.AllTasks - in this case maybe try to debug with different SystemTriggerType and swich to desired one with release version.
I've also tested if the BackgroundTask with the problematic SystemTriggerType.NetworkStateChange works - and indeed - it is working. I've modified your BackgroundTask a little to send a toast message when NetworkState changes. After registering the task, when I turn the WiFi on/off, I get a toast messgae. The code for the task:
public sealed class Upload : IBackgroundTask
{
public void Run(IBackgroundTaskInstance taskInstance)
{
Debug.WriteLine("Hello Pat");
ToastTemplateType toastTemplate = ToastTemplateType.ToastText02;
XmlDocument toastXml = ToastNotificationManager.GetTemplateContent(toastTemplate);
XmlNodeList textElements = toastXml.GetElementsByTagName("text");
textElements[0].AppendChild(toastXml.CreateTextNode("Upload Task - Yeah"));
textElements[1].AppendChild(toastXml.CreateTextNode("I'm message from your Upload task!"));
ToastNotificationManager.CreateToastNotifier().Show(new ToastNotification(toastXml));
}
}
The complete example you can download here.

ClickOnce System.Progress not working

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.

Categories