How to run background tasks in .net Maui Windows App

Iyad Alomar 0 Reputation points
2024-06-25T09:10:41.27+00:00

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
}
.NET
.NET
Microsoft Technologies based on the .NET software framework.
3,905 questions
.NET MAUI
.NET MAUI
A Microsoft open-source framework for building native device applications spanning mobile, tablet, and desktop.
3,579 questions
0 comments No comments
{count} votes

1 answer

Sort by: Most helpful
  1. Yonglun Liu (Shanghai Wicresoft Co,.Ltd.) 43,926 Reputation points Microsoft Vendor
    2024-06-26T03:08:48.36+00:00

    Hello,

    MAUI uses the WinUI3 technology framework on Windows, not UWP.

    For WinUI3, it has some restrictions on background tasks, so the problem you mentioned will occur.

    There is an existed issue about the Service/Background Task in the .net maui. You could refer to the customer for more detailed information.

    In addition, you could check this discussion about Windows App SDK need better support for periodic background tasks. There is a workaround about Timer-triggered background task in the WinUI in it.

    Best Regards,

    Alec Liu.


    If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.