7

I've created a Maui windows application. I'm looking to activate the application through a URI and pass query parameters to the app.

I've added the windows protocol for calling the app via uri in the package manifest:

  <Extensions>
            <uap:Extension Category="windows.protocol">
              <uap:Protocol Name="my-app">
                <uap:DisplayName>My App</uap:DisplayName>
              </uap:Protocol>
            </uap:Extension>
      </Extensions>

When I activate the application via the browser my-app://foo.com?user=123456 the app launches, but it launches as a cold start. Within my Win UI app I've overrode the onLaunched method, but regardless of how I've launched the app I cannot get access to the protocol. I'm trying to recreate the following code from my UWP Application:

protected override void OnActivated(IActivatedEventArgs args)
    {
        if (args.Kind == ActivationKind.Protocol)
        {

            ProtocolActivatedEventArgs eventArgs = args as ProtocolActivatedEventArgs;
            var queryStr = eventArgs.Uri.Query;
            App.UserId = System.Web.HttpUtility.ParseQueryString(queryStr).Get("user");

            // Navigate to a view
            Frame rootFrame = Window.Current.Content as Frame;
            if (rootFrame == null)
            {
                rootFrame = new Frame();
                Xamarin.Forms.Forms.Init(args);
                Window.Current.Content = rootFrame;
            }

            rootFrame.Navigate(typeof(MainPage), eventArgs);
        }

        Window.Current.Activate();
    }

So I've started with this, but UWPLaunchActivatedEventArg is consistently being returned as Launch instead of protocol.

    protected override void OnLaunched(LaunchActivatedEventArgs args)
{
  var kind =   args.UWPLaunchActivatedEventArgs.Kind;
        base.OnLaunched(e)
}
  • The value of Kind may differ in `OnLaunched` and `OnActivated` event , but MAUI does not expose `OnActivated` in App class , you can try to intercept the event by adding it in MauiProgram class ,see [docs here](https://learn.microsoft.com/en-us/dotnet/maui/fundamentals/app-lifecycle#windows) . – ColeX Jun 14 '22 at 07:26
  • I am able to intercept the action through onLaunched and OnActivated, but the problem is I cannot get the parameters off of the URI. – Meridith Spellerberg Jul 12 '22 at 17:31
  • @MeridithSpellerberg I wrote a solution, see if it works for you – Angelru Aug 22 '22 at 08:13

4 Answers4

3

Here is my solution:

var activatedEventArgs = Microsoft.Windows.AppLifecycle.AppInstance.GetCurrent().GetActivatedEventArgs();

run your URI with parameters and here is the screenshot: enter image description here

Angelru
  • 150
  • 1
  • 13
2

It took me a while to understand what the other answer meant -- got to "cast" the object.

var actEventArgs = AppInstance.GetCurrent().GetActivatedEventArgs();
if ( actEventArgs.Kind == ExtendedActivationKind.Protocol) {
    var d = actEventArgs.Data as IProtocolActivatedEventArgs;
    if (d != null) {
        var uri = d.Uri;
        var uriString = uri.AbsoluteUri;
        // do something...
    }
}

This gets me the URI string. BUT -- I get a new window created. I want to dispatch the URI to an already created window (if it exists, otherwise create).

I've done with with Xamarin.Forms UWP, by catching the OnActivated event. Haven't got that figured for MAUI yet. Bob

bobwki
  • 794
  • 6
  • 22
  • Same thing happens for me when trying to use the [WinUIEx WebAuthenticator](https://dotmorten.github.io/WinUIEx/concepts/WebAuthenticator.html) which requires opening a browser and returning to the app with the login result (I cannot use the native Maui WebAuthenticator because [it is broken on Windows](https://github.com/microsoft/WindowsAppSDK/issues/441)) – hansmbakker Nov 21 '22 at 21:27
  • Did you find the solution in MAUI? – Angelru May 25 '23 at 11:03
  • @Angelru, I posted my [implementation](https://stackoverflow.com/a/76344679/1758350), which seems to work for me. – bobwki May 26 '23 at 23:25
0
var args = Environment.GetCommandLineArgs();
alansiqueira27
  • 8,129
  • 15
  • 67
  • 111
0

I'm adding this new, more complete answer, though I don't pretend to completely understand this yet, and I did not do great job keeping track of what sample code I got from where. And, I should've gotten back to here months ago.

Note my logp() method which writes to the console, and MainPage.Inst.setOpeningString() which passes the URI along.

Windows/Program.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.Threading;
using Microsoft.UI.Dispatching;
using Microsoft.Windows.AppLifecycle;
using Windows.ApplicationModel.Activation;
using Windows.Storage;
using System.Runtime.InteropServices;

using static OpenerAppMaui.Util;
namespace OpenerAppMaui.WinUI;

class Program {
    [STAThread]
    static void Main(string[] args) {
        logp($"Program.Main({args.Length} args)");
        WinRT.ComWrappersSupport.InitializeComWrappers();
        bool isRedirect = DecideRedirection();
        if (!isRedirect) {
            logp("Program.Main() NOT isRedir");
            Microsoft.UI.Xaml.Application.Start((p) =>
            {
                var context = new DispatcherQueueSynchronizationContext(
                    DispatcherQueue.GetForCurrentThread());
                SynchronizationContext.SetSynchronizationContext(context);

                // Important: has to go to the WINDOW APP "App" class (not the generic one)
                new App();
                logp("Program.Main() after new App()");
            });
        } else {
            logp("Program.Main() IS isRedir (should get handled with OnActivated() below)");
        }
    }

    private static bool DecideRedirection() {
        bool isRedirect = false;

        AppActivationArguments args = AppInstance.GetCurrent().GetActivatedEventArgs();
        ExtendedActivationKind kind = args.Kind;
        logp($"Program.DecideRedirection() kind={kind}, ");
        if (args.Kind == ExtendedActivationKind.Protocol) {
            ProtocolActivatedEventArgs dataProtActEvArgs = args.Data as ProtocolActivatedEventArgs;
            logp($"Program.DecideRedirection()) data= {dataProtActEvArgs.Uri}");
        }
        try {
            AppInstance keyInstance = AppInstance.FindOrRegisterForKey("randomKey");

            if (keyInstance.IsCurrent) {
                keyInstance.Activated += OnActivated;
            } else {
                isRedirect = true;
                logp($"Program.DecideRedirection() Redirect to keyInstance {keyInstance}");
                RedirectActivationTo(args, keyInstance);
            }
        } catch (Exception ex) {
            logp($"Program.DecideRedirection() Exception, ex={ex.Message}");
        }

        return isRedirect;
    }


    [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
    private static extern IntPtr CreateEvent(
        IntPtr lpEventAttributes, bool bManualReset,
        bool bInitialState, string lpName);

    [DllImport("kernel32.dll")]
    private static extern bool SetEvent(IntPtr hEvent);

    [DllImport("ole32.dll")]
    private static extern uint CoWaitForMultipleObjects(
        uint dwFlags, uint dwMilliseconds, ulong nHandles,
        IntPtr[] pHandles, out uint dwIndex);

    private static IntPtr redirectEventHandle = IntPtr.Zero;

    // Do the redirection on another thread, and use a non-blocking
    // wait method to wait for the redirection to complete.
    public static void RedirectActivationTo( AppActivationArguments args, AppInstance keyInstance) {
        redirectEventHandle = CreateEvent(IntPtr.Zero, true, false, null);
        Task.Run(() =>
        {
            keyInstance.RedirectActivationToAsync(args).AsTask().Wait();
            SetEvent(redirectEventHandle);
        });
        uint CWMO_DEFAULT = 0;
        uint INFINITE = 0xFFFFFFFF;
        _ = CoWaitForMultipleObjects(
           CWMO_DEFAULT, INFINITE, 1,
           new IntPtr[] { redirectEventHandle }, out uint handleIndex);
    }

    private static void OnActivated(object sender, AppActivationArguments args) {
        ExtendedActivationKind kind = args.Kind;

        // this for reassurance that I've got the right instance (original launch) doing the work.
        AppActivationArguments thisArgs = AppInstance.GetCurrent().GetActivatedEventArgs();
        ExtendedActivationKind thisInstanceArgsKind = thisArgs.Kind;

        logp($"Program.OnActivated() kind={kind}, thisInstanceArgsKind={thisInstanceArgsKind}");    // s/b, protocol & launch
        if (args.Kind == ExtendedActivationKind.Protocol) {
            ProtocolActivatedEventArgs dataProtActEvArgs = args.Data as ProtocolActivatedEventArgs;
            logp($"Program.OnActivated() data= {dataProtActEvArgs.Uri}");

            MainThread.BeginInvokeOnMainThread( ()=> {  // hooray!  this works!  (12/30/22)
                var openStr = dataProtActEvArgs.Uri.ToString();
                logp($"Program.OnActivated() setting openStr: {openStr}");
                MainPage.Inst.setOpeningString(openStr );
                logp($"Program.OnActivated() openStr set");
            });
        }
    }

}
bobwki
  • 794
  • 6
  • 22