CsWinRT icon indicating copy to clipboard operation
CsWinRT copied to clipboard

ApplicationTrigger RequestAsync throws COMException

Open EricLVertiGIS opened this issue 1 year ago • 0 comments

Describe the bug I am trying to implement background tasks for .NET MAUI apps. In the past Xamarin Forms used UWP, but now that Maui is based on WinUI3, I have to implement background tasks using the WinUI3 method with COMServer. Using code from this sample https://github.com/microsoft/DesktopBridgeToUWP-Samples/tree/master/Samples/BackgroundTaskWinMainComSample, I could get the background tasks triggered with a TimeTrigger or by manually triggering it through Visual Studio. However, when I use an ApplicationTrigger, and then call RequestAsync(). It throws COMException with error value E_FAIL | Unspecified failure | 0x80004005.

*Edit : seems that prior to calling RequestAsync, builder.Register() throws 0x80070032

To Reproduce Creating a trigger and initializing the view.:

        public MainPage()
        {
            InitializeComponent();

            _trigger = new ApplicationTrigger();
        }

Function to register to the COMServer:

        static void RegisterProcessForBackgroundTask<TaskType, TaskInterface>() where TaskType : TaskInterface, new()
        {
            // If the application is compiled with the full .NET frameworks, the
            // built-in RegistrationServices class may be used to use COM server
            // registration APIs.

            /*
                        RegistrationServices registrationServices = new RegistrationServices();
                        registrationServices.RegisterTypeForComClients(typeof(TaskType),
                                                                       RegistrationClassContext.LocalServer,
                                                                       RegistrationConnectionType.MultipleUse);
            */

            // This app is currently using the .NET Core framework. Use the
            // manual C++ imported implementation of the CoRegisterClassObject
            // API.

            Guid taskGuid = typeof(TaskType).GUID;
            ComApi.CoRegisterClassObject(ref taskGuid,
                                            new ComApi.BackgroundTaskFactory<TaskType, TaskInterface>(),
                                            ComApi.CLSCTX_LOCAL_SERVER,
                                            ComApi.REGCLS_MULTIPLEUSE,
                                            out _RegistrationToken);
        }

Registering background task:

public IBackgroundTaskRegistration RegisterBackgroundTaskWithSystem(IBackgroundTrigger trigger, Guid entryPointClsid, string taskName)
{
    foreach (var registrationIterator in BackgroundTaskRegistration.AllTasks)
    {
        if (registrationIterator.Value.Name == taskName)
        {
            return registrationIterator.Value;
        }
    }

    BackgroundTaskBuilder builder = new BackgroundTaskBuilder();

    builder.SetTrigger(trigger);
    builder.SetTaskEntryPointClsid(entryPointClsid);
    builder.Name = taskName;

    BackgroundTaskRegistration registration;
    try
    {
        registration = builder.Register();
    }
    catch (Exception)
    {
        registration = null;
    }

    return registration;
}

Registering background tasks by calling the helper functions above on button click:

       private async void Button_Clicked(object sender, EventArgs e)
        {

            BackgroundExecutionManager.RemoveAccess();
            var requestStatus = await BackgroundExecutionManager.RequestAccessAsync();
            if (requestStatus != BackgroundAccessStatus.AlwaysAllowed && requestStatus != BackgroundAccessStatus.AllowedSubjectToSystemPolicy)
            {
                // Depending on the value of requestStatus, provide an appropriate response
                // such as notifying the user which functionality won't work as expected
                Debug.WriteLine("Access Denied By RequestAccessAsync");
                return;
            }

            RegisterBackgroundTaskWithSystem(_trigger, typeof(BackgroundTask).GUID, typeof(BackgroundTask).Name);
            RegisterProcessForBackgroundTask<BackgroundTask, IBackgroundTask>();

            if(_trigger != null)
            {
                await _trigger.RequestAsync();
            }
        }

appx:

	  <Extensions>
		<!-- Register a COM32 server CLSID for handling background activation -->
		<com:Extension Category="windows.comServer">
			<com:ComServer>
				<com:ExeServer Executable="BackgroundTaskTest\BackgroundTaskTest.exe" DisplayName="Background Activator">
					<!-- this CLSID is taken from BackgroundTaskTest::BackgroundTask -->
					<com:Class Id="A8082001-73F7-4607-8521-60F03476E462" DisplayName="Notification Scheduler"/>
				</com:ExeServer>
			</com:ComServer>
		</com:Extension>
	</Extensions>

Expected behavior RequestAsync() executes successfully and the background tasks is run.

Version Info

Additional context Seeing some similar discussion about this in https://github.com/microsoft/CsWinRT/discussions/1296. I'm not 100% sure which project owns ApplicationTrigger but any suggestions or workarounds would be great. I basically want to trigger a background task through code, unfortunately TimeTrigger needs a minimum 15 minute delay to trigger which is too long. Thanks

EricLVertiGIS avatar Feb 27 '24 17:02 EricLVertiGIS