September 2017

Volume 32 Number 9

[Modern Apps]

Protocol Registration and Activation in UWP Apps

By Frank La

Frank La VigneIn my last column, I explored how to use the Launcher class to extend the functionality of a Universal Windows Platform (UWP) app by leveraging the features of other apps built into Windows. For example, I was able to add fully featured mapping functionality, such as searching for a good place to find pizza and finding directions complete with traffic reports, without having to add significant complexity to my app’s code base. In this month’s column, I’ll explore the other side of the equation: How to expose a UWP app’s functionality to other apps.

Of course, programmatically launching applications and passing parameters along from other appli­cations predates the UWP. In fact, many Windows apps support this via command-line switches. For example, to launch Microsoft Word in Safe Mode, where add-ons and plug-ins are disabled, pass the parameter “/safe” to the WinWord.exe file. To test this out, press Windows Key+R, to bring up the Run dialog box. In the textbox, type “Winword.exe /safe” and click OK. If you have Microsoft Word installed on your system, then the program will launch in Safe Mode. Additionally, similar commands exist for Excel, PowerPoint and others. Clearly, launching a program with parameters isn’t a new mechanism. What’s new, however, is the additional mechanism that UWP provides: Uniform Resource Identifier (URI) activation.

URI Activation

In my last column (msdn.com/magazine/mt784669), I used URI activation to launch and pass parameters to several apps. The apps then took these input parameters and acted upon them. For example, to get driving directions between Washington, D.C., and New York City with the default Maps app in Windows 10, the app simply passes the following URI to the Launcher class:

bingmaps:?rtp=adr.Washington,%20DC~adr.New%20York,%20NY&mode=d&trfc=1

Alternatively, typing this command into the Run dialog box will also yield the same result. The Maps app has been registered to handle the “bingmaps” protocol. The text after the question mark represents parameters passed along to the Maps app. In this case, it sets the start and end points, along with a mode to indicate driving (“d”) and a 1 to add traffic data to the results.

What if you wanted your app to perform in a similar manner? What are the steps you’d need to take in order to register a protocol and handle the subsequent parameters passed to it?

The first step is to create a new project. Create a new blank UWP project in Visual Studio by choosing New Project from the File menu. Expand Installed Templates | Windows | Blank App (Universal Windows). Name the project ProtocolActivation and then click OK. Immediately afterward, a dialog box will appear asking you which version of Windows the app should target. For this project, the default options will be fine, so you can simply click OK.

Registering a URI Protocol

Now that the project has been created, open the appmanifest to register a protocol. In the Solution explorer, look for the Package.appmanifest file and double-click on it to open it. Click on the Declarations tab. Choose Protocol from the dropdown underneath Available Declarations. Now, click Add to add the declaration to the manifest. The screen should look similar to Figure 1.

Adding a Protocol Declaration (Note the Validation Errors)
Figure 1 Adding a Protocol Declaration (Note the Validation Errors)

While it’s highly recommended to provide entries for the Logo and Display name fields, the only required field is Name. Enter “bingmaps” into the textbox. Press the Tab key. Note that the validation errors disappear after the textbox loses focus. Run the project by pressing F5, choosing Start Debugging from the Debug Menu, or simply clicking on the play icon on the toolbar. Once the app is running, press Windows Key+R to launch the Run dialog. Enter “bingmaps:” into the textbox and click OK. Note that the default Maps app started just as before.

This brings up an important caveat: Some protocols cannot be overridden. For a complete list, either refer to my previous column or refer to the MSDN documentation on Reserved File and URI Scheme Names at bit.ly/2st28Er. Choosing any of these reserved names will not result in an error message; the app simply won’t get activated.

Registering a Custom Protocol

Close the app if it’s still running. Go back into the manifest file and change the contents of the Name field to “msdn­colors” and save the project. Now, press Windows Key+R to bring up the Run dialog, enter “msdncolors” into the textbox and click OK. A system-wide dialog box appears suggesting to look for an app in the Store that can handle this protocol, as shown in Figure 2.

Dialog Box That Appears When the System Sees an Unfamiliar Protocol
Figure 2 Dialog Box That Appears When the System Sees an Unfamiliar Protocol

While the protocol might have been declared in the app’s manifest file, it hasn’t yet been registered on the system. This happens at install time. Run the solution now and then enter “msdncolors” into the Run dialog once again. The same dialog box appears, but with a new option added: the ProtocolActivation app, as shown in Figure 3. Click OK. The ProtocolActivation app will launch. Close the app. Enter “msdncolors” once more into the Run dialog box. This time, there will be no dialog box, the app will simply run whether the “Always use this app” option was checked.

The New App Now Appears as an Option
Figure 3 The New App Now Appears as an Option

Handling Activation

UWP apps can be activated any number of ways, from being launched by the user when its tile is tapped or when activated by protocol. In order to detect how the app was launched, the OnActivated event handler must be overridden.

In Solution Explorer, open the App.xaml.cs file, and add the following code to the App class:

protected async override void OnActivated(IActivatedEventArgs e)
{
  if (e.Kind == ActivationKind.Protocol)
  {
    var dialog = new MessageDialog("App Activated by Protocol.");
    await dialog.ShowAsync();
  }
  Window.Current.Activate();
}

Run the solution once again, open the Run dialog, type in “msdncolors:” and click OK. This time, you’ll see a message dialog. Stop the app from running. Now, re-open the Run dialog by pressing Windows Key+R, enter “msdncolors:” into the textbox and click OK. Note that the same message dialog appears, but the app doesn’t progress beyond the splash screen. The reason why will be addressed later.

Passing Parameters via URI

While launching an app through URI activation has its uses, the real power comes in passing parameters from one app to another. This can be done by appending parameters to the protocol request. This mechanism operates nearly the same way as HTTP GET requests.

For example, URIs follow a pattern of:

[protocol]:[host address]?[parameter1]=[value1]& [parameter2]=[value2]

For instance, given the URI https://bing.com/search?q=data%20driven%20podcast, the protocol is HTTP, the host address is bing.com, and the value of “data%20driven%20podcast” is passed to the parameter “q.” Note that the spaces in “data driven podcast” are converted to “%20.” Parameter names and values passed along in a URI must be encoded.

Modify the code inside the OnActivated method to contain the changes shown in Figure 4.

Figure 4 Modifying the Code Inside the OnActivated Method

if (e.Kind == ActivationKind.Protocol)
{
  var args = e as ProtocolActivatedEventArgs;
  var message = "no parameters passed";
  if (args.Uri.PathAndQuery != string.Empty)
  {
    var queryString = args.Uri.Query;
    var absolutePath = args.Uri.AbsolutePath;
    message = $"Query String: {queryString}\n AbsolutePath: {absolutePath}";
  }
  var dialog = new MessageDialog(message);
  await dialog.ShowAsync();
}
Window.Current.Activate();

The code in Figure 4 casts the IActivatedEventArgs parameter to a ProtocolActivatedEventArgs class, providing access to a number of properties specific to protocol activation scenarios. In this instance, I’m mainly concerned with the URI property. The URI will contain the information passed to the app via protocol activation. Run the app again, open the Run dialog, enter “msdncolors:background?red” into the textbox, and click OK. Once again, the app will launch. This time, however, the Message dialog will have the Query String and AbsolutePath values displayed.

Stop the app and re-run the same command from the Run dialog. Once again, the app doesn't progress beyond the splash screen. It’s time to address that.

Modify the OnActivated method in App.xaml.cs to match the code in Figure 5. While this code looks complex, it’s actually not. Much of the additional code concerns revolve around finding the current content of the active window. In cases where the app is already running, the current content for this app will be the MainPage class. When the app isn’t running and is launched by protocol activation, there’s no current content, and Window.Current.Content has a null value. In those cases, the code creates a new Frame, sets the appropriate event handlers and content, and navigates to the MainPage. For more information about the Window and Frame classes, refer to bit.ly/2tGwt1H and bit.ly/2sQ8LTY, respectively.

Figure 5 Updated OnActivated Code

protected override void OnActivated(IActivatedEventArgs e)
{
  if (e.Kind == ActivationKind.Protocol)
  {
    var args = e as ProtocolActivatedEventArgs;
    if (args.Uri.PathAndQuery != string.Empty)
    {
      Frame rootFrame = Window.Current.Content as Frame;
      if (rootFrame == null)
      {
        rootFrame = new Frame();
        rootFrame.NavigationFailed += OnNavigationFailed;
        Window.Current.Content = rootFrame;
      }
      if (rootFrame.Content == null)
      {
        if (!rootFrame.Navigate(typeof(MainPage)))
        {
          throw new Exception("Failed to create initial page");
        }
      }
      var fragment = args.Uri.Fragment;
      var absolutePath = args.Uri.AbsolutePath;
      var mainPage = rootFrame.Content as MainPage;
      mainPage.NavigateWithParameters(absolutePath, fragment);
    }
  }
  Window.Current.Activate();
}

The rest of the code is a slight modification from before, with the addition of making a call to a NavigateWithParameters method. This is how the information from the URI will get sent to the MainPage class. Now is the time to add that method to the MainPage.xaml.cs file and add some UI elements to the MainPage.xaml file.

First, modify the MainPage.xaml file to have the following XAML inside the Page element:

<Grid Name="grdBackground" Background=
  "{ThemeResource ApplicationPageBackgroundThemeBrush}">
  <Grid Name="grdForeground" Width="100" Height="100"></Grid>
</Grid>

This adds the name “grdBackground” to the Grid control already present and adds an inner grid named “grdForegound.”

Next, add the code in Figure 6 to the MainPage.xaml.cs file. The bulk of the code converts a hex code ARGB color value like #FFAA3CC7 into something that a SolidColorBrush can use. The conversion code drew inspiration from the blog post at bit.ly/2tGuFFH, which also contains a great explanation of how the conversion works. The remainder of the code is fairly simple; it changes the color of one grid or the other based on the target parameter.

Figure 6 The NavigateWithParameters Method

public void NavigateWithParameters(string target, string color)
{
  string hexCode = color.Replace("#", string.Empty);
  byte a = (byte)(Convert.ToUInt32(hexCode.Substring(0, 2), 16));
  byte r = (byte)(Convert.ToUInt32(hexCode.Substring(2, 2), 16));
  byte g = (byte)(Convert.ToUInt32(hexCode.Substring(4, 2), 16));
  byte b = (byte)(Convert.ToUInt32(hexCode.Substring(6, 2), 16));

  var brush = new SolidColorBrush(Color.FromArgb(a, r, g, b));

  if (target.ToLower() == "background")
  {
    grdBackground.Background = brush;
  }
  else
  {
    grdForeground.Background = brush;
  }
}

Once all the code is in place, run the app. In the Run dialog box type “msdncolors:foreground?#FFAA3CC7” and click OK. This should turn the foreground grid a shade of purple. Now, type “msdncolors:background?#FFCFCFCF” into the textbox of the Run dialog and click OK. The background Grid should turn a light gray. Close the app. Now, re-enter the previous commands to see that the app is the same when it’s not already running.

Wrapping Up

In this column, I demonstrated how to register a UWP app to handle a particular protocol. Adding this feature will make your UWP apps accessible to other apps on a user’s device. This lets other developers create interesting uses for the apps you develop and makes your app more desirable. While much of the activation in this column was done using the Run dialog, passing the same URI to the Launcher class will yield the same results.


Frank La Vigne is chief evangelist at DataLeader.IO, where he helps customers leverage technology in order to create a better world. He is co-host of the data science podcast Data Driven (DataDriven.tv) and blogs regularly at FranksWorld.com.

Thanks to the following technical expert for reviewing this article: Jose Luis Manners


Discuss this article in the MSDN Magazine forum