Launch a Universal App from a WPF App

If you want to jump straight to the code, it lives here: https://github.com/arunjeetsingh/SampleCode/tree/master/WPFAppLaunchingUniversalApp.

The Windows Win32 APIs have been around for a long time. A lot of very useful technology like Windows Forms and Windows Presentation Foundation (WPF) have built on top of Win32 APIs over the years. WPF and WinForms also power many applications around the world. With the introduction of the Universal Windows Platform (UWP) an app developer can now reach pretty much any device that runs Windows 10. However, there are times when one needs to connect an existing desktop Windows application built in WPF or WinForms to newer universal apps. In a previous post, I talked about how a WPF or WinForms app can call into an app service provided by a Universal Windows App (UWA). In this post, I’d like to talk about how a WPF or WinForms app can launch a UWA.

The process of launching a universal app from a WPF/WinForms app actually looks very similar to launching a universal app from another universal app. To follow along with this blog post, clone the repo https://github.com/arunjeetsingh/SampleCode and open the solution at WPFAppLaunchingUniversalApp\WPFAppLaunchingUniversalApp.sln.

Putting Together the Universal Target App

We start with the UniversalTargetApp project. This project is a Windows 10 universal app that implements a protocol handler. It does so by declaring the windows.protocol extension in it’s package manifest. Open the Package.appxmanifest file in this project to see the extension declaration. Normally, it should open in the manifest designer which presents declaration information in the Declarations tab. As you will see, we sign up for the protocol scheme name com.aruntalkstech.universaltarget. Using the reverse domain naming convention like I do here is just a good idea because it lowers the odds of your custom scheme colliding with someone else’s. I own aruntalkstech.com so I know someone else is unlikely to use the same scheme randomly. I also talked about this in my Build talk this year.

Another thing we do in the UniversalTargetApp is print out the Package Family Name of the app on it’s main page. Here’s the code to do that:

UniversalTargetApp\MainPage.xaml.cs

protected override void OnNavigatedTo(NavigationEventArgs e)
{
  PFN.Text = Windows.ApplicationModel.Package.Current.Id.FamilyName;
}

We will use the Package Family Name (PFN) later when we launch the UniversalTargetApp from our universal app. In addition to this, we have the UniversalTargetApp go to a different page (ProtocolActivationPage.xaml) when it is activated with a protocol scheme. The code to do that lives in UniversalTargetApp\App.xaml.cs:

protected override void OnActivated(IActivatedEventArgs args)
{
Frame rootFrame = Window.Current.Content as Frame;
if (rootFrame == null)
{
rootFrame = new Frame();
rootFrame.NavigationFailed += OnNavigationFailed;
// Place the frame in the current Window
Window.Current.Content = rootFrame;
}
string uri = string.Empty;
var protocolArgs = args as ProtocolActivatedEventArgs;
if(protocolArgs != null)
{
uri = protocolArgs.Uri.ToString();
}
rootFrame.Navigate(typeof(ProtocolActivationPage), uri);

  // Ensure the current window is active
  Window.Current.Activate();
}

All this code does is figure out whether the app was “protocol activated” and if it was it pulls out the URI it was activated with and navigates to the ProtocolActivationPage with the URI as a parameter. On the ProtocolActivationPage we pull out the URI and present it to the user in a TextBlock. Code for that is in UniversalTargetApp\ProtocolActivationPage.xaml.cs.

The WPF Launcher App

Next, lets take a look at the WPF project that contains the app that will do the launching. I use a WPF project here because that is the .NET user interface technology I am most familiar with. However, code along similar lines should also work for WinForms or plain old Win32 applications.

First, we must ensure we can reference Windows 10 APIs inside our WPF app. To do that, I updated the project file for the WPF project (WPFLauncherApp.csproj) to add a reference to Windows 10 metadata. You can see the additions yourself. Right-click the project, then selecting Unload Project, then right-click again and select Edit WPFLauncherApp.csproj. Here’s what the new references look like:

<!– Light up Windows 10 features –>
<Reference Include=System.Runtime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL>
<SpecificVersion>False</SpecificVersion>
<HintPath>C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Runtime.dll</HintPath>
</Reference>

<Reference Include=System.Runtime.WindowsRuntime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL>
<SpecificVersion>False</SpecificVersion>
<HintPath>C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Runtime.WindowsRuntime.dll</HintPath>
</Reference>

<Reference Include=Windows>
<HintPath>C:\Program Files (x86)\Windows Kits\10\UnionMetadata\Windows.winmd</HintPath>
</Reference>
<!– Light up Windows 10 features –>

Now we can reload the project and reference any Windows 10 APIs we want to. In the WPF project we have a button in MainWindow.xaml that will be used to launch the universal target app we built. Before we launch the universal target we also try and detect whether it is installed. We do this in the OnActivated handler for MainWindow.xaml. The code lives in WPFLauncherApp\MainWindow.xaml.cs. Here’s what it looks like:

protected override async void OnActivated(EventArgs e)
{
  //Test whether the app we want to launch is installed
  var supportStatus = await Launcher.QueryUriSupportAsync(uri, LaunchQuerySupportType.Uri, TargetPackageFamilyName);

  if (supportStatus != LaunchQuerySupportStatus.Available)
    {
     Status.Text = “Can’t launch com.aruntalkstech.universaltarget: because the app we need is “ + supportStatus.ToString();
    }
}

Launcher.QueryUriSupportAsync is a new Windows 10 API. This API lets us check whether a specific custom scheme (com.aruntalkstech.universaltarget in our case) is supported on a device. What’s more, we can use the PackageFamilyName of the universal target app to ask it whether the app that supports it is indeed the universal target app we just built. This is great because now we can be sure we are launching exactly the app we wanted to launch. If the app is not available we show the user a message.

When the user clicks the Launch Target App button on MainWindow.xaml we launch the universal target app in the button’s Click handler. Here’s what the click handler for the Launch Target App button looks like in WPFLauncherApp\MainWindow.xaml.cs:

private async void LaunchTargetApp_Click(object sender, RoutedEventArgs e)
{
  var options = new LauncherOptions { TargetApplicationPackageFamilyName = TargetPackageFamilyName };
bool success = await Launcher.LaunchUriAsync(uri, options);
Debug.WriteLine(success);
}

That’s it! Note that we use the Package Family Name of the universal target app here as well to make sure we launch the app we want. Also bear in mind that you can use app services to continue communicating with the universal target app once you’ve launched. This post goes into details about how that would work.

Happy hacking!

@aruntalkstech

Advertisements

3 thoughts on “Launch a Universal App from a WPF App

  1. I’m trying to run your sample project, and the project file seems to be corrupted.

    I’m getting a pile of errors, but the critical one seems to be “Multiple assemblies with equivalent identity have been imported: ‘C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Runtime.dll’ and ‘C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5.2\Facades\System.Runtime.dll’. Remove one of the duplicate references.” for WPFLauncherApp.

    And when I look at the properties for the UniversalTargetApp, on the Application tab I see “An error occurred trying to load the page.
    InvalidArgument=Value of ‘0’ is not valid for ‘SelectedIndex’.
    Parameter name: SelectedIndex.”

    Like

  2. When I look at your code, I see that you’re initializing TargetPackageFamilyName to what looks like a GUID, and then use TargetPackageFamilyName to set LauncherOptions.TargetApplicationPackageFamilyName.

    What wasn’t not clear to me is where that GUID came from. Clearly, it had to refer to the UniversalTargetApp, in some way. It’s the Name= attribute of the element, in the Package.appxmanifest.

    Like

  3. I’m encountering a compile error in the WPFLauncherApp, apparently due to the way you’re referencing the Universal Windows assemblies:

    “Multiple assemblies with equivalent identity have been imported: ‘C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Runtime.dll’ and ‘C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5.2\Facades\System.Runtime.dll’. Remove one of the duplicate references.”

    Any ideas?

    Are you sure this is the canonical way to reference the Universal Windows assemblies?

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s