WinUI 3 Out-of-Process Background Task (TimeTrigger) never runs (COM/CsWinRT)

1 day ago 4
ARTICLE AD BOX

I am trying to implement an Out-of-Process Background Task in my WinUI 3 (.NET 9) application using the WindowsAppSDKBackgroundTask approach with COM/CsWinRT. This is a TimeTrigger task designed to run independently outside of the main app process every 15 minutes, but the task never seems to run. I have verified that the task registers successfully, but the Run method is never hit (logging doesn't fire).

Here is my project structure and configuration.

Project Structure

ProjectX.sln ├── ProjectX (Package) │ ├── Package.appxmanifest │ └── ProjectX (Package).wapproj ├── ProjectX.BackgroundTask (Class Library) │ ├── ProjectX.BackgroundTask.csproj │ └── WallpaperTask.cs └── ProjectX.Core └── Services └── BackgroundTaskService.cs

ProjectX.BackgroundTask.csproj I have configured the project as a CsWinRT component with WindowsAppSDKBackgroundTask enabled.

<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net9.0-windows10.0.22621.0</TargetFramework> <OutputType>Library</OutputType> <WindowsAppSDKBackgroundTask>true</WindowsAppSDKBackgroundTask> <CsWinRTComponent>true</CsWinRTComponent> <!-- The class that implements IBackgroundTask --> <CsWinRTIncludes>ProjectX.BackgroundTask.WallpaperTask</CsWinRTIncludes> </PropertyGroup> ... </Project>

ProjectX (Package).wapproj I am manually including the background task's DLL, WinMD, and the WinRT host DLL in the package.

<ItemGroup> <!-- Include the WinMD metadata file for COM registration --> <Content Include="..\ProjectX.BackgroundTask\bin\$(Platform)\$(Configuration)\net9.0-windows10.0.22621.0\ProjectX.BackgroundTask.winmd"> <Link>ProjectX.BackgroundTask.winmd</Link> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> <!-- Include the WinRT.Host.dll which acts as the COM server host --> <Content Include="..\ProjectX.BackgroundTask\bin\$(Platform)\$(Configuration)\net9.0-windows10.0.22621.0\WinRT.Host.dll"> <Link>WinRT.Host.dll</Link> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> <!-- Include the implementation DLL --> <Content Include="..\ProjectX.BackgroundTask\bin\$(Platform)\$(Configuration)\net9.0-windows10.0.22621.0\ProjectX.BackgroundTask.dll"> <Link>ProjectX.BackgroundTask.dll</Link> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> <!-- UniversalBGTask.dll --> <Content Include="..\ProjectX\bin\$(Platform)\$(Configuration)\net9.0-windows10.0.22621.0\runtimes\win-$(Platform)\native\Microsoft.Windows.ApplicationModel.Background.UniversalBGTask.dll"> <Link>Microsoft.Windows.ApplicationModel.Background.UniversalBGTask.dll</Link> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> </ItemGroup>

Package.appxmanifest I am using the UniversalBGTask entry point and registering my task as a COM surrogate server.

<Extensions> <!-- Background Task Extension --> <Extension Category="windows.backgroundTasks" EntryPoint="Microsoft.Windows.ApplicationModel.Background.UniversalBGTask.Task"> <BackgroundTasks> <Task Type="general" /> <Task Type="timer" /> </BackgroundTasks> </Extension> <!-- COM Server Extension --> <com:Extension Category="windows.comServer"> <com:ComServer> <com:SurrogateServer DisplayName="ProjectX Background Task"> <!-- Replaced actual CLSID with generic placeholder --> <com:Class Id="12345678-1234-1234-1234-123456789abc" Path="WinRT.Host.dll" ThreadingModel="Both" DisplayName="Wallpaper Task" /> </com:SurrogateServer> </com:ComServer> </com:Extension> </Extensions> <!-- Activatable Class Extension for UniversalBGTask --> <Extensions> <Extension Category="windows.activatableClass.inProcessServer"> <InProcessServer> <Path>Microsoft.Windows.ApplicationModel.Background.UniversalBGTask.dll</Path> <ActivatableClass ActivatableClassId="Microsoft.Windows.ApplicationModel.Background.UniversalBGTask.Task" ThreadingModel="both" /> </InProcessServer> </Extension> </Extensions>

WallpaperTask.cs (The Implementation) Matches the CLSID in the manifest.

[ComVisible(true)] [Guid("12345678-1234-1234-1234-123456789abc")] // Matches manifest public sealed class WallpaperTask : IBackgroundTask { private BackgroundTaskDeferral _deferral; public void Run(IBackgroundTaskInstance taskInstance) { // I grab the deferral immediately _deferral = taskInstance.GetDeferral(); // Start async work (Void return to keep Run synchronous) _ = RunAsyncWork(); } private async Task RunAsyncWork() { try { // ... Application Logic ... } finally { _deferral?.Complete(); } } }

BackgroundTaskService.cs (Registration) I am registering via SetTaskEntryPointClsid using the same GUID to set up the 15-minute interval.

var builder = new BackgroundTaskBuilder(); builder.Name = "WallpaperBackgroundTask_TimeTrigger_15min"; // Register using CLSID instead of string EntryPoint builder.SetTaskEntryPointClsid(Guid.Parse("12345678-1234-1234-1234-123456789abc")); // 15 minute trigger builder.SetTrigger(new TimeTrigger(15, false)); builder.Register();

Questions:

Is my Package.appxmanifest configuration correct for a WindowsAppSDKBackgroundTask running via TimeTrigger? Does SetTaskEntryPointClsid work correctly with the UniversalBGTask entry point defined in the manifest? Are there any known issues with .NET 9 and CsWinRTComponent that would prevent the background task process from launching?
Read Entire Article