UWP with Desktop Extension – Part 2

desktop_blue

If you haven’t read part 1 of this series yet, please start there first. I have covered the basic project setup in VS and walked through the “Hello World” version of a UWP with Desktop extension. Now the next set of questions I want to answer in this post are:

  • What type of processes can I use as extensions?
  • What about EXEs that are not in my package?
  • Can I have multiple Desktop extensions for my app?
  • How do I pass parameters into those EXEs?

TL;DR;

Short answers: Any type of Win32 or .NET desktop project type can be used. Can be background-only or with UI. Can come from the package or from elsewhere (read the fine print below), and you can launch with custom command line parameters.

Just show me the code on github …
https://github.com/StefanWickDev/UWP-FullTrust/tree/master/UWP_FullTrust_2

Let me run your sample from the Microsoft Store …
https://www.microsoft.com/store/apps/9PLMLGKSCZQW

Multiple Desktop Processes

If you have studied the ‘windows.fullTrustProcess’ extension schema and corresponding API surface you have noticed that there is only one extension per <Application> node in your package. For multi-process scenarios, you implement them in the declared extension process. There you can use the Process class (.NET) or CreateProcess API (Win32) to create and manage your child processes. To see how this is done, take a look at the “Launcher” project in my sample #2 , which launches several different process types based on input from the UWP.

// determine the package root, based on own location
string result = Assembly.GetExecutingAssembly().Location;
int index = result.LastIndexOf("\\");
string rootPath = $"{result.Substring(0, index)}\\..\\";

// process object to keep track of your child process
Process newProcess = Process.Start(rootPath + @"Win32\Win32.exe");

screenshot3

In-package vs out-of-package

Launching EXEs that come with your package is easy, as the sample demonstrates. You know where they are located and how to launch them as they are your binaries. You can also launch EXEs from other locations on the system. The sample shows this by launching mstsc.exe (the system’s remote desktop app). Be careful here though: In Windows 10 S mode, only Microsoft-signed code can run (OS binaries & apps from the Microsoft Store), so you can’t just CreateProcess/Process.Start an EXE that you’ve downloaded from a share or website. That will fail in S mode. Launching mstsc or notepad will work of course as they are Microsoft-signed.

Another detail to be aware of is process identity: EXEs from your package will be launched with your package’s identity. EXEs from other locations will run under their own identity. Running with package identity also means those desktop processes have the same state separation for Registry and AppData as Desktop Bridge apps (learn all the details here).

You can easily visualize the identity grouping in the task bar and task manager, as the processes are grouped together – whereas the Remote Desktop app is separate from the UWP app:

screenshot6

Background Processes

As you can see in the sample, the desktop extension can run as a headless background process. This can be very powerful as you are in control of the lifetime of this process. It can run for the entire duration of the user session. However, with great power comes great responsibility and you should make sure you don’t burn CPU without delivering user value, especially when run on battery. In my sample I am using a 60 sec timeout on my background process (even though it is not adding any value), just to give folks enough time to find it in task manager to verify its existence.

Also note that running a fulltrust background process for long periods of time may delay your app from getting updated.

Passing in Launch Parameters

From the extension schema, the API surface and/or the MSDN snippet you may have already gathered that the FullTrustProcessLauncher only accepts a finite list of predefined command line arguments that get passed to the desktop extension process. This design is based on the principle, that a less trusted process should not be able to send arbitrary input to a more trusted process. They should communicate via well-defined protocols. Here is how I am using predefined parameter groups in my sample to launch the various types of processes:

<desktop:Extension Category="windows.fullTrustProcess" Executable="Launcher\Launcher.exe">
 <desktop:FullTrustProcess>
 <desktop:ParameterGroup GroupId="Background" Parameters="/background" />
 <desktop:ParameterGroup GroupId="WPF" Parameters="/wpf" />
 <desktop:ParameterGroup GroupId="WinForms" Parameters="/winforms" />
 <desktop:ParameterGroup GroupId="Win32" Parameters="/win32" />
 <desktop:ParameterGroup GroupId="RemoteDesktop" Parameters="/mstsc" />
 <desktop:ParameterGroup GroupId="Parameters" Parameters="/parameters" />
 </desktop:FullTrustProcess>
</desktop:Extension>

In the UWP app code, I now use the FullTrustProcessLauncher overload that takes the ParameterGroupID as string:

if (ApiInformation.IsApiContractPresent("Windows.ApplicationModel.FullTrustAppContract", 1, 0))
{
 await FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync("Background");
}

If you own the code on both sides you can use the local AppData to share complex data from the calling app to the desktop process. Furthermore, from your desktop extension process you can launch other desktop process with arbitrary command line arguments, as those processes have the same integrity, as shown in the sample as scenario 2.

UWP side (MainPage.cs):

if (ApiInformation.IsApiContractPresent("Windows.ApplicationModel.FullTrustAppContract", 1, 0))
{
 // store command line parameters in local settings
 // so the Lancher can retrieve them and pass them on
 ApplicationData.Current.LocalSettings.Values["parameters"] = tbParameters.Text;

 await FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync("Parameters");
}

Win32 side (Program.cs)

string parameters = ApplicationData.Current.LocalSettings.Values["parameters"] as string;
Process newProcess = Process.Start(rootPath + @"WPF\WPF.exe", parameters);

screenshot7

This is it for part 2 of this series. Hopefully I answered most of the question around process launching and parameters in this sample. If not, please leave comments here, hit me up on Twitter or post your questions on Stackoverflow. If they are tagged with ‘UWP’ they will show on my radar.

Next up: Part 3 – Bi-directional communication between components

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 )

Google+ photo

You are commenting using your Google+ 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 )

w

Connecting to %s