February 1, 2017 3:38 pm

Adding UWP features to your existing PC software

By / Principal Program Manager Lead

With last summer’s release of the Windows 10 Anniversary Update and the announcement of the Windows Store supporting apps built with the Desktop Bridge technology, we want to share more details and sample code on how to further enhance and extend your PC software on Windows 10. In addition to Windows Store distribution and modern deployment technology, the Desktop Bridge enables you to use exciting Universal Windows Platform (UWP) features and capabilities that were previously unavailable to existing PC software. In this blog post, we will share four examples of adding such features, with full source code available on GitHub. The corresponding apps are also available in the Windows Store, so you can start exploring them on Windows 10 PCs without a development environment. We have picked a different desktop application technology for each of the samples to further emphasize that the Desktop Bridge is applicable to all PC software. The samples are calling into Windows 10 UWP APIs using the techniques that we explained in another blog post last week.

Adding a UWP XAML user experience

In our first example, we are extending a Visual Basic 6 application to use the UWP XAML Map control to visualize the location information of a database application. The sample also uses UWP APIs to pop up toast notifications.

Links to source code and app download:

Windows Store app linked here; GitHub code linked here

AppxManifest.xml

To understand how the UWP and Desktop Bridge tooling enables this scenario, let’s look at the relevant lines in the AppxManifest.xml file for the solution. The entry point here is the VB6 application that we have converted using the Desktop Bridge. Now we can add a UWP component (implemented in MapUI.exe) to the package, which provides the XAML UI. To facilitate the activation of the modern component from the existing application, we define a protocol extension in the manifest. Furthermore, note that we need to declare two capabilities: (a) ‘runFullTrust’ to keep the VB6 code running at the same level of trust as before the Desktop Bridge conversion and (b) ‘internetClient’ for the Map control to download map data from the internet.


<Applications>
  <Application
       Id="VB6App" Executable="VB6App.exe"
       EntryPoint="Windows.FullTrustApplication">
    <uap:VisualElements ... />
    <Extensions>
      <uap:Extension
          Category="windows.protocol"
          Executable="MapUI.exe"
          EntryPoint=" MapUI.App">
        <uap:Protocol Name="desktopbridgemapsample" />
      </uap:Extension>
    </Extensions>      
  </Application>
</Applications>
<Capabilities>
  <rescap:Capability Name="runFullTrust" />
  <Capability Name="internetClient" />
</Capabilities>

Code Snippets

To protocol-activate the UWP view from the VB6 application, we invoke the LaunchUriAsync UWP API by wrapping it into a VB6-callable interop function (‘LaunchMap’) and pass in the latitude and longitude as parameters:


Private Declare Function LaunchMap Lib "UWPWrappers.dll" _
  (ByVal lat As Double, ByVal lon As Double) As Boolean

Private Sub EiffelTower_Click()
    LaunchMap 48.858222, 2.2945
End Sub


And this is the underlying C++ code for the LaunchMap() API in UWPWrappers.dll, which is wrapping the calls into the actual LaunchUriAsync UWP API:


DllExport bool __stdcall LaunchMap(double lat, double lon)
{
  try
  {
    //
    // format the URI to match the defined protocol:
    // desktopbridgemapsample://location?lat={latitude}&?lon={longitude}
    //
    String ^str = ref new String(L"desktopbridgemapsample://");
    Uri ^uri = ref new Uri(
      str + L"location?lat=" + lat.ToString() + L"&?lon=" + lon.ToString());

    // now launch the UWP component
    Launcher::LaunchUriAsync(uri);
  }
  catch (Exception^ ex) { return false; }
  return true;
}

This now triggers the OnActivated event in the UWP component. The component is written in C++, but could done in any programming language that supports the UWP. Here we check if we got activated for the right protocol. If so, we will perform the actual activation and load the map page passing on the value set that includes the latitude and longitude information.


void App::OnActivated(Windows::ApplicationModel::Activation::IActivatedEventArgs^ e)
{
  if (e->Kind == ActivationKind::Protocol)
  {
    ProtocolActivatedEventArgs^ protocolArgs = (ProtocolActivatedEventArgs^)e;
    Uri ^uri = protocolArgs->Uri;
    if (uri->SchemeName == "desktopbridgemapsample")
    {
      Frame ^rootFrame = ref new Frame();
      Window::Current->Content = rootFrame;
      rootFrame->Navigate(TypeName(MainPage::typeid), uri->Query);
      Window::Current->Activate();
    }
  }
}

Additional remarks

In the UWPWrappers.dll you will also find another UWP API wrapper function (CreateToast) which is used for firing off toast notifications from the VB6 app.

Note that in many scenarios, you will need the main app and the added UWP component to communicate with each other back and forth. This sample doesn’t demonstrate the communication workflow, but you can refer to this sample on GitHub that shows how an AppServiceConnection can be used for two-way communication between two components within an app package.

Exposing an UWP App Service

In the second example, we are extending a WinForms data application that exposes an App Service to provide other apps controlled access to its database, even if the WinForms app is not running.

Links to source code and app download:

Windows Store app linked here; GitHub code linked here

AppxManifest.xml

The entry point here is the Windows Forms application, that we have packaged using the Desktop Bridge. Now we can add a UWP component (implemented in a Windows Runtime component called ‘MyAppService.dll’) to the package, which provides the App Service implementation. This component gets activated out-of-proc to the main application process, so we also need to declare a package-level extension to explain to the system how the class will get activated.


<Applications>
  <Application
      Id="WinformWithAppService"
      Executable="WinformWithAppService.exe"
      EntryPoint="Windows.FullTrustApplication">
    <uap:VisualElements ...>
    <Extensions>
      <uap:Extension
          Category="windows.appService"
          EntryPoint="MyAppService.AppServiceTask">
        <uap:AppService Name="com.microsoft.samples.winforms" />
      </uap:Extension>
    </Extensions>
  </Application>
<Applications>
<Capabilities>
  <rescap:Capability Name="runFullTrust" />
</Capabilities>
<Extensions>
  <Extension
      Category="windows.activatableClass.inProcessServer">
    <InProcessServer>
      <Path>MyAppService.dll</Path>
      <ActivatableClass
          ActivatableClassId="MyAppService.AppServiceTask"
          ThreadingModel="both" />
    </InProcessServer>
  </Extension>
</Extensions>

Code Snippet

In the App Service implementation, we will want to validate and handle requests from other applications that request access to our database:


public sealed class AppServiceTask : IBackgroundTask
{
    private BackgroundTaskDeferral backgroundTaskDeferral;

    public void Run(IBackgroundTaskInstance taskInstance)
    {
        this.backgroundTaskDeferral = taskInstance.GetDeferral();
        taskInstance.Canceled += OnTaskCanceled; 
        var details = taskInstance.TriggerDetails as AppServiceTriggerDetails;
        details.AppServiceConnection.RequestReceived += OnRequestReceived;
    }

    private async void OnRequestReceived(AppServiceConnection sender,
                                         AppServiceRequestReceivedEventArgs args)
    {
        var messageDeferral = args.GetDeferral();
        ValueSet message = args.Request.Message;
        string id = message["ID"] as string;
        ValueSet returnData = DataBase.GetData(id);
        await args.Request.SendResponseAsync(returnData); 
        messageDeferral.Complete();
    }


    private void OnTaskCanceled(IBackgroundTaskInstance sender,
                                BackgroundTaskCancellationReason reason)
    {
        if (this.backgroundTaskDeferral != null)
        {
            this.backgroundTaskDeferral.Complete();
        }
    }
}

And here is example code for a client application that consumes the App Service:


private async void button_Click(object sender, RoutedEventArgs e)
{
    AppServiceConnection dataService = new AppServiceConnection();
    dataService.AppServiceName = "com.microsoft.samples.winforms";
    dataService.PackageFamilyName = "Microsoft.SDKSamples.WinformWithAppService";

    var status = await dataService.OpenAsync();
    if (status == AppServiceConnectionStatus.Success)
    {
        string id = int.Parse(textBox.Text);
        var message = new ValueSet();
        message.Add("ID", id);
        AppServiceResponse response = await dataService.SendMessageAsync(message);
        string result = "";

        if (response.Status == AppServiceResponseStatus.Success)
        {
            if (response.Message["Status"] as string == "OK")
            {
                DisplayResult(response.Message["Result"]);
            }
        }
    }
}

Making your PC software a share target

In this example, we are extending a WPF picture viewer to become a share target. This way users can easily share pictures from Microsoft Edge, the Photos app and other applications with our application. Sharing pictures is just one of many scenarios that can be enabled by making your app a sharing target. Several other data formats are supported as well. See the Windows Dev Center documentation page for a complete list and more details.

Links to source code and app download:

Windows Store app linked here; GitHub code linked here

AppxManifest.xml

The structure of our manifest looks very similar to the XAML example earlier in this post. In fact, our sharing UI is also XAML. The only difference here is that we are declaring the ‘windows.shareTarget’ extension instead of protocol activation, to let the system know we want to activate if the user is sharing the specified data type and selects our app as the sharing target.


<Applications>
  <Application
      Id="PhotoStore"
      Executable="Win32\PhotoStore.exe"
      EntryPoint="Windows.FullTrustApplication">
    <uap:VisualElements ... />
    <Extensions>
      <uap:Extension
          Category="windows.shareTarget"
          Executable="ShareTarget.exe"
          EntryPoint="ShareTarget.App">
        <uap:ShareTarget>
          <uap:SupportedFileTypes>
            <uap:SupportsAnyFileType />
          </uap:SupportedFileTypes>
          <uap:DataFormat>Bitmap</uap:DataFormat>
        </uap:ShareTarget>
      </uap:Extension>
    </Extensions>      
  </Application>
</Applications>
<Capabilities>
  <rescap:Capability Name="runFullTrust" />
</Capabilities>

Code Snippet

When the user selects our application as the sharing target, the OnNavigatedTo event fires in the UWP component and we get access to the shared data. Now we can, for example, save it in our local app storage or process it in other ways that are appropriate for the given app context.


protected override async void OnNavigatedTo(NavigationEventArgs e)
{
  this.shareOperation = (ShareOperation)e.Parameter;
  if (this.shareOperation.Data.Contains(StandardDataFormats.StorageItems))
  {
      this.sharedStorageItems =
        await this.shareOperation.Data.GetStorageItemsAsync();
      
      foreach (StorageFile item in this.sharedStorageItems)
      {
          ProcessSharedFile(item);
      }
  }
}

Adding a UWP background task

In this example, we are extending an MFC application to receive raw push notifications from a server application, regardless whether the client software is running or not. If the application is running, it will receive the notification event and process the payload within the application process. If the application is not running, a background task will be triggered to handle the payload, for example to save to disk, notify the user with a toast notification, update the Live Tile, etc.

Links to source code and app download:

Windows Store app linked here; GitHub code linked here 

AppxManifest.xml

The structure of the manifest should look mostly familiar by now. In addition to the main MFC application, we are declaring a background task type “pushNotification”. One special thing to note here is that because the background task is implemented using managed code, we need to declare ‘CLRHost.dll’ as the inProcessServer, as we will need this platform component to handle the activation for us here.


<Applications>
  <Application
      Id="MFCSampleIDE" Executable="MFCSampleIDE.exe"
      EntryPoint="Windows.FullTrustApplication">
    <uap:VisualElements ...>
      <Extensions>
        <Extension
            Category="windows.backgroundTasks"
            EntryPoint="BackgroundTasks.PushNotificationTask">
          <BackgroundTasks>
            <Task Type="pushNotification" />
          </BackgroundTasks>
        </Extension>
      </Extensions>
  </Application>
</Applications>
<Capabilities>
  <Capability Name="internetClient" />
  <rescap:Capability Name="runFullTrust" />
</Capabilities>
<Extensions>
  <Extension
      Category="windows.activatableClass.inProcessServer">
    <InProcessServer>
      <Path>PushNotificationBackgroundTask.dll</Path>
      <ActivatableClass
          ActivatableClassId="PushNotificationBackgroundTask.MyTask"
          ThreadingModel="both" />
    </InProcessServer>
  </Extension>
</Extensions>

Code Snippet

With the following code, we are subscribing to get notified via an event (in-proc) and background task (out-of-proc) whenever the server application sends a raw push notification to our application instance.


// Hook up push notification foreground event
task<PushNotificationChannel^>(
  PushNotificationChannelManager::CreatePushNotificationChannelForApplicationAsync())
  .then([](PushNotificationChannel ^channel)
{
	channel->PushNotificationReceived +=
           ref new TypedEventHandler<PushNotificationChannel^,
                                     PushNotificationReceivedEventArgs ^>
                                     (&OnPushNotificationReceived);

});


// Register push notification background trigger

BackgroundTaskBuilder ^builder = ref new BackgroundTaskBuilder();
builder->Name = "pushTask";
builder->TaskEntryPoint = "PushNotificationBackgroundTask.MyTask";
builder->SetTrigger(ref new PushNotificationTrigger());
builder->Register();

Additional remark

Note how this last sample keeps the legacy code separate from the Windows 10 enhancements, which are all implemented in UWPFeatures.dll. This DLL is being loaded dynamically when running on Windows 10 to light up those features. On earlier versions of the operation system, such as Windows 7, the same application EXE can still be deployed as before as all enhancements are cleanly separated from the existing code base.

Conclusion

With UWP and Windows 10, applications can take advantage of several exciting new features to increase their user engagement. With the Desktop Bridge platform and tooling enhancements, existing PC software can now be part of the UWP ecosystem and take advantage of the same set of new platform features and operating system capabilities.

For more information on the Desktop Bridge, please visit the Windows Dev Center.

Ready to submit your app to the Windows Store? Let us know!

Updated February 2, 2017 10:15 am

Join the conversation