Greetings,
I am trying to run a back ground task in my .net Maui windows applicaion. I tried using IBackgroundTask interface. The task is registerd successfully, but it does not run when triggred. I've tried the same exact code with UWP (Blank UWP for the executable and WindowsRunTime Component for the task). When I tried adding a Windows Runtime Componenet to the .netMaui solution, the project did not build because Windows Runtime is not supported. Therefore, I tried placing the task class in ...project path/Platforms/Windows. Moreover, I ran into trouble registering the background task in the Manifet file.
To sum up, I managed to gergister a background task in .net Maui desktop app, but it won't run on the designated trigger. Below is the my code:
<?xml version="1.0" encoding="utf-8"?>
<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
xmlns:com="http://schemas.microsoft.com/appx/manifest/com/windows10"
xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10/"
IgnorableNamespaces="uap rescap com desktop">
<Identity Name="maui-package-name-placeholder" Publisher="CN=User Name" Version="0.0.0.0" />
<mp:PhoneIdentity PhoneProductId="705F9D36-2B0E-4E3B-B5F0-F73D7185F025" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
<Properties>
<DisplayName>$placeholder$</DisplayName>
<PublisherDisplayName>User Name</PublisherDisplayName>
<Logo>$placeholder$.png</Logo>
</Properties>
<Dependencies>
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
</Dependencies>
<Resources>
<Resource Language="x-generate" />
</Resources>
<Applications>
<Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="$targetentrypoint$">
<Extensions>
<Extension Category="windows.backgroundTasks" EntryPoint="TaskTest.Platforms.Windows.SampleBackgroundTask">
<BackgroundTasks>
<Task Type="systemEvent"/>
<Task Type="timer"/>
</BackgroundTasks>
</Extension>
</Extensions>
<uap:VisualElements
DisplayName="$placeholder$"
Description="$placeholder$"
Square150x150Logo="$placeholder$.png"
Square44x44Logo="$placeholder$.png"
BackgroundColor="transparent">
<uap:DefaultTile Square71x71Logo="$placeholder$.png" Wide310x150Logo="$placeholder$.png" Square310x310Logo="$placeholder$.png" />
<uap:SplashScreen Image="$placeholder$.png" />
</uap:VisualElements>
</Application>
</Applications>
<Capabilities>
<rescap:Capability Name="runFullTrust" />
<rescap:Capability Name="backgroundTask" />
<Capability Name="internetClient" />
</Capabilities>
<Extensions>
<Extension Category="windows.activatableClass.inProcessServer">
<InProcessServer>
<Path>Personal_Expenses.dll</Path>
<ActivatableClass ActivatableClassId="TaskTest.Platforms.Windows.SampleBackgroundTask" ThreadingModel="both" />
</InProcessServer>
</Extension>
</Extensions>
</Package>
using System.Diagnostics;
using System.Runtime.InteropServices;
using Windows.ApplicationModel.Background;
using Windows.Storage;
using Windows.System.Threading;
using Windows.UI.Notifications;
namespace TaskTest.Platforms.Windows
{
{
BackgroundTaskCancellationReason _cancelReason = BackgroundTaskCancellationReason.Abort;
volatile bool _cancelRequested = false;
BackgroundTaskDeferral _deferral = null;
ThreadPoolTimer _periodicTimer = null;
uint _progress = 0;
public void Run(IBackgroundTaskInstance taskInstance)
{
Debug.WriteLine("Background " + taskInstance.Task.Name + " Starting...");
var cost = BackgroundWorkCost.CurrentBackgroundWorkCost;
var settings = ApplicationData.Current.LocalSettings;
settings.Values["BackgroundWorkCost"] = cost.ToString();
taskInstance.Canceled += new BackgroundTaskCanceledEventHandler(OnCanceled);
_deferral = taskInstance.GetDeferral();
_taskInstance = taskInstance;
_periodicTimer = ThreadPoolTimer.CreatePeriodicTimer(new TimerElapsedHandler(PeriodicTimerCallback), TimeSpan.FromSeconds(1));
var toastXml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText02);
var textNodes = toastXml.GetElementsByTagName("text");
textNodes.Item(0).AppendChild(toastXml.CreateTextNode("Background Notification"));
textNodes.Item(1).AppendChild(toastXml.CreateTextNode("This notification is from the Maui Service Test."));
var toast = new ToastNotification(toastXml);
ToastNotificationManager.CreateToastNotifier().Show(toast);
}
private void OnCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
{
_cancelRequested = true;
_cancelReason = reason;
Debug.WriteLine("Background " + sender.Task.Name + " Cancel Requested...");
}
// Simulate the background task activity.
//
private void PeriodicTimerCallback(ThreadPoolTimer timer)
{
if ((_cancelRequested == false) && (_progress < 100))
{
_progress += 10;
_taskInstance.Progress = _progress;
}
else
{
_periodicTimer.Cancel();
var key = _taskInstance.Task.Name;
String taskStatus = (_progress < 100) ? "Canceled with reason: " + _cancelReason.ToString() : "Completed";
var settings = ApplicationData.Current.LocalSettings;
settings.Values[key] = taskStatus;
Debug.WriteLine("Background " + _taskInstance.Task.Name + settings.Values[key]);
// Indicate that the background task has completed.
//
_deferral.Complete();
}
}
}
}
#if WINDOWS
using Windows.Data.Xml.Dom;
using Windows.UI.Notifications;
using Windows.ApplicationModel.Background;
using Windows.Storage;
using Personal_Expenses.Platforms.Windows;
#endif
namespace TaskTest.MainViews;
public partial class MainPage : ContentPage
{
public Budgets()
{
InitializeComponent();
#if WINDOWS
foreach (var task in BackgroundTaskRegistration.AllTasks)
{
if (task.Value.Name == BackgroundTaskSample.SampleBackgroundTaskName)
{
AttachProgressAndCompletedHandlers(task.Value);
BackgroundTaskSample.UpdateBackgroundTaskRegistrationStatus(BackgroundTaskSample.SampleBackgroundTaskName, true);
break;
}
}
UpdateUI();
#endif
}
private void Button_Clicked_1(object sender, EventArgs e)
{
#if WINDOWS
var task = BackgroundTaskSample.RegisterBackgroundTask(BackgroundTaskSample.SampleBackgroundTaskEntryPoint,
BackgroundTaskSample.SampleBackgroundTaskName,
new SystemTrigger(SystemTriggerType.TimeZoneChange, false),
//new TimeTrigger(15, false),
null);
AttachProgressAndCompletedHandlers(task);
UpdateUI();
#endif
}
private void Button_Clicked_2(object sender, EventArgs e)
{
#if WINDOWS
BackgroundTaskSample.UnregisterBackgroundTasks(BackgroundTaskSample.SampleBackgroundTaskName);
UpdateUI();
#endif
}
#if WINDOWS
private void AttachProgressAndCompletedHandlers(IBackgroundTaskRegistration task)
{
task.Progress += new BackgroundTaskProgressEventHandler(OnProgress);
task.Completed += new BackgroundTaskCompletedEventHandler(OnCompleted);
}
private void OnProgress(IBackgroundTaskRegistration task, BackgroundTaskProgressEventArgs args)
{
var progress = "Progress: " + args.Progress + "%";
BackgroundTaskSample.SampleBackgroundTaskProgress = progress;
UpdateUI();
}
private void OnCompleted(IBackgroundTaskRegistration task, BackgroundTaskCompletedEventArgs args)
{
UpdateUI();
}
private async void UpdateUI()
{
RegisterButton.IsEnabled = !BackgroundTaskSample.SampleBackgroundTaskRegistered;
UnregisterButton.IsEnabled = BackgroundTaskSample.SampleBackgroundTaskRegistered;
ProgressLabel.Text = BackgroundTaskSample.SampleBackgroundTaskProgress;
StatusLabel.Text = BackgroundTaskSample.GetBackgroundTaskStatus(BackgroundTaskSample.SampleBackgroundTaskName);
}
class BackgroundTaskSample
{
public const string SampleBackgroundTaskEntryPoint = "TaskTest.Platforms.Windows.SampleBackgroundTask";
public const string SampleBackgroundTaskName = "SampleBackgroundTask";
public static string SampleBackgroundTaskProgress = "";
public static bool SampleBackgroundTaskRegistered = false;
public static string ApplicationTriggerTaskResult = "";
public static BackgroundTaskRegistration RegisterBackgroundTask(String taskEntryPoint, String name, IBackgroundTrigger trigger, IBackgroundCondition condition, BackgroundTaskRegistrationGroup group = null)
{
if (TaskRequiresBackgroundAccess(name))
{
var requestTask = BackgroundExecutionManager.RequestAccessAsync();
Debug.Write(requestTask.Status);
}
var builder = new BackgroundTaskBuilder();
builder.Name = name;
builder.TaskEntryPoint = taskEntryPoint;
builder.SetTrigger(trigger);
if (condition != null)
{
builder.AddCondition(condition);
builder.CancelOnConditionLoss = true;
}
if (group != null)
{
builder.TaskGroup = group;
}
BackgroundTaskRegistration task = builder.Register();
UpdateBackgroundTaskRegistrationStatus(name, true);
var settings = ApplicationData.Current.LocalSettings;
settings.Values.Remove(name);
return task;
}
public static void UnregisterBackgroundTasks(String name, BackgroundTaskRegistrationGroup group = null)
{
if (group != null)
{
foreach (var cur in group.AllTasks)
{
if (cur.Value.Name == name)
{
cur.Value.Unregister(true);
}
}
}
else
{
foreach (var cur in BackgroundTaskRegistration.AllTasks)
{
if (cur.Value.Name == name)
{
cur.Value.Unregister(true);
}
}
}
UpdateBackgroundTaskRegistrationStatus(name, false);
}
public static BackgroundTaskRegistrationGroup GetTaskGroup(string id, string groupName)
{
var group = BackgroundTaskRegistration.GetTaskGroup(id);
if (group == null)
{
group = new BackgroundTaskRegistrationGroup(id, groupName);
}
return group;
}
public static void UpdateBackgroundTaskRegistrationStatus(String name, bool registered)
{
SampleBackgroundTaskRegistered = registered;
}
public static String GetBackgroundTaskStatus(String name)
{
var registered = false;
registered = SampleBackgroundTaskRegistered;
var status = registered ? "Registered" : "Unregistered";
object taskStatus;
var settings = ApplicationData.Current.LocalSettings;
if (settings.Values.TryGetValue(name, out taskStatus))
{
status += " - " + taskStatus.ToString();
}
return status;
}
public static bool TaskRequiresBackgroundAccess(String name)
{
return true;
}
}
#endif
}