Launch a WPF (Win32) App from a Universal Windows App

If you want to jump straight into the code go here: https://github.com/arunjeetsingh/SampleCode/tree/master/UniversalAppLaunchingWPFApp

In my last post I talked about launching a Universal Windows App from a WPF app. Today, we go the other way around. Not many people are aware of this but Windows has had support for custom protocol schemes (mailto:, ftp:, skype: are good examples) aka deep linking since about 1996. In fact, the Internet Explorer team introduced the idea in the Platform Developers Conference of ’96. I even talked about it during my Build session here.

What does that mean for us? Well, it means that practically any app can sign up for a custom scheme. That includes apps written in Windows Presentation Foundation (WPF), WinForms, plain old Win32 and even Visual Basic. All an app developer has to do is create a specific set of registry entries to let Windows know that they would like to handle a custom scheme. Of course, if you’re writing a universal app you can simply put that information in your manifest and we’ll take care of creating the registry entries. In this blog post, we will learn how to create a WPF app that registers for a custom scheme. We will then see an example of a universal app that launches the WPF app we created using the custom scheme. Let’s get started!

To follow along, clone the repository at https://github.com/arunjeetsingh/SampleCode and open the solution at UniversalAppLauncherWPFApp\UniversalAppLaunchingWPFApp.sln.

Creating the WPF Protocol Handler

The WPFProtocolHandler project is a simple WPF app. All of the interesting code lives in the MainWindow.xaml.cs file. The first piece of code that warrants our attention is the code that installs a protocol handler. Before you click that button, know that the code requires admin privileges to run. The best way I found to accomplish this was to simply start WPFProtocolHandler.exe as administrator and then install the protocol. Here’s what we do to install a protocol handler:

private void InstallProtocol_Click(object sender, RoutedEventArgs e)
{
    using (var hkcr = Registry.ClassesRoot)
    {
        if (hkcr.GetSubKeyNames().Contains(SchemeName.Text))
        {
            MessageBox.Show(string.Format(“Looks like {0} is already installed.”, SchemeName.Text));
            return;
        } 

        using (var schemeKey = hkcr.CreateSubKey(SchemeName.Text))
        {
            //[HKEY_CLASSES_ROOT\com.aruntalkstech.wpftarget]
            //@=”Url:WPF Target Protocol”
            //”URL Protocol”=””
            //”UseOriginalUrlEncoding”=dword:00000001
            schemeKey.SetValue(string.Empty, “Url: WPF Target Protocol”);
            schemeKey.SetValue(“URL Protocol”, string.Empty);
            schemeKey.SetValue(“UseOriginalUrlEncoding”, 1, RegistryValueKind.DWord); 

            //[HKEY_CLASSES_ROOT\com.aruntalkstech.wpf\shell]
            using (var shellKey = schemeKey.CreateSubKey(“shell”))
            {
                //[HKEY_CLASSES_ROOT\com.aruntalkstech.wpf\shell\open]
                using (var openKey = shellKey.CreateSubKey(“open”))
                {
                    //[HKEY_CLASSES_ROOT\com.aruntalkstech.wpf\shell\open\command]
                    using (var commandKey = openKey.CreateSubKey(“command”))
                    {
                        //@=”C:\\github\\SampleCode\\UniversalAppLaunchingWPFApp\\WPFProtocolHandler\\bin\\Debug\\WPFProtocolHandler.exe \”%1\””
                        commandKey.SetValue(string.Empty, Assembly.GetExecutingAssembly().Location + ” %1″);
                        commandKey.Close();
                    }

                    openKey.Close();
                }
                shellKey.Close();
            }

            schemeKey.Close();
        }

        hkcr.Close();
    } 

    MessageBox.Show(string.Format(“Custom scheme {0}: installed.”, SchemeName.Text));
}

As you can see, all that code is doing is creating a bunch of registry entries. The file at UniversalAppLauncherWPFApp\SolutionItems\InstallWin32ProtocolHandler.reg presents the registry entries in a slightly more readable format. By creating these registry entries, we instruct Windows to register whatever text was in SchemeName.Text as a custom scheme. We then point this custom scheme at the WPFProtocolHandler executable. The Uninstall button’s click handler simply finds the registry key corresponding to SchemeName.Text and remove it.
So what happens when WPFProtocolHanlder is activated with a custom scheme? That’s where the MainWindow() constructor comes in. In here, we take apart the incoming custom scheme URI and present it to the user. Note that the custom scheme URI comes in as a command-line argument to the WPF app. This is because we put the little %1 when we were setting up the command registry key’s default value above. Here’s what MainWindow() looks like:

public MainWindow()
{
    InitializeComponent();

    var args = Environment.GetCommandLineArgs();
    if (args.Length > 1)
    {
        Uri argUri;
        if (Uri.TryCreate(args[1], UriKind.Absolute, out argUri))
        {
            var decoder = new WwwFormUrlDecoder(argUri.Query);
            if (decoder.Any())
            {
                InputUrI.Text = string.Empty;
                foreach (var entry in decoder)
                {
                    InputUrI.Text += entry.Name + “=” + entry.Value + “,”;
                    inputs[entry.Name] = entry.Value;
                } 

                InputUrI.Text = InputUrI.Text.Remove(InputUrI.Text.Length – 1);
            }
        }
    }
}

We simply break apart the incoming URI and put key/value pairs in the query string into the InputUrl textbox.

The Windows10Launcher App

Now that we’ve built the WPF app we want to launch, lets take a quick look at the Universal Windows App that will be doing the launching. The Windows10Launcher app is exceedingly simple. It has a textbox where the URI we want to launch goes and a button that takes the text, converts it into a URI and launches it using our old friend Launcher.LaunchUriAsync. Here’s the click handler for the Launch WPF App button:

private async void LaunchWpfApp_Click(object sender, RoutedEventArgs e)
{
    var launchUri = new Uri(UriToLaunch.Text);
    await Launcher.LaunchUriAsync(launchUri);
}

It’s that simple! It is easy to imagine a WPF or Win32 app that might start up and establish a connection back to the universal app that launched it by way of an app service as described here. This makes it really easy to pair your Win32 and universal apps together and have them communicate with each other. It also gives us a way to slowly migrate code from a WPF app over to a universal app over time without having to rewrite the whole thing in one go.
As always, happy hacking!
Advertisements

One thought on “Launch a WPF (Win32) App from a Universal Windows App

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