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

Advertisements

23 thoughts on “UWP with Desktop Extension – Part 2

  1. Thanks for the posts.

    I wonder if it is also possible to force started full trust application into “as administrator” mode? Both UAC prompt and launching the app as administrator at all times (as opposed to selective) would be acceptable.

    I am afraid this is not possible? I found this SO post https://stackoverflow.com/a/50829593/868014 where you seem to write exactly this, and maybe RS5 will bring this option.

    Like

    1. Hi Roman,

      Yes this is coming in RS5 (Fall 2018 update). We will enable both scenarios (requesting elevation at launch as well as launching a process as elevated on demand). You should be able to start playing with this on the latest Windows Insider OS build, with the latest insider SDK build (17692 or later). To enable this, add the “allowElevation” capability to your appxmanifest.

      Thanks,
      Stefan

      Liked by 1 person

      1. Hi Stefan,

        Regarding the “allowElevation” capability you mention above, would you have a working example?

        i have read the document at https://docs.microsoft.com/en-us/windows/uwp/packaging/app-capability-declarations and played around but couldn’t get it to work. In my case, I have a UWP and WPF app, which is an set to a desktop extension and launched from the UWP by following your example. I wish the WPF app is running in elevated mode but it is not.

        Regards

        John

        Like

  2. Hi Stefan,

    Thank you for the quick reply and the instructions on how to get an elevated app running.

    Unfortunately, I don’t have a Windows 10 with build 17744 so I wouldn’t be able to exercise for now. I shall try in the future.

    Regards

    John

    Like

  3. Hi

    First of all thanks for your valuable examples. I have a related problem I would like your opinion about it. Hope you find it interesting and has the time to give it a comment. It already costed me a week of work without successfully result.:-(

    Very simplified I have an old WPF LOB application, and then a new UWP one under development. Both runs on the same machine on Windows 10 LTSB version (Anniversary Update 143939). While the UWP version is under development I want to be able to switch easily between the two applications. I have added a switch button in both applications.

    It was simple to get the WPF to launch the UWP versions using protocol activation. The other way is the one giving me gray hair. Using desktop extension is there any way to easily let the UWP bring an already running instance of the WPF application into front.

    I should say that my Windows 10 runs in tablet mode and it seems like that it prevents the wpf application from being restored/maximized. The wpf application will remain stucked in the taskbar (= blinking in the taskbar).

    So to summarize my question. “Can my UWP app send an already running WPF application into front running on Windows 10 LTSB (14393) in tablet mode?”.

    Like

  4. Hi, this does not seem to work, i have:
    await FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync(“Parameters”);

    and this trusted console apps task to launch a dotnet.exe with
    ProcessStartInfo startInfo = new ProcessStartInfo
    {
    FileName = @”NBXplorer.exe”,
    Arguments = args[1],//@”–datadir application:////NBXplorer –chains btc –network testnet”,
    RedirectStandardError = true,
    RedirectStandardOutput = true,
    UseShellExecute = false,
    CreateNoWindow = true
    };
    Process process = new Process { StartInfo = startInfo };
    process.Start();

    but when it launches NBXplorer.exe it just crashes so quickly cannot see output.

    UWP frustrates me with it’s restrictiveness! Grrrrr

    Like

    1. I am just directly using the exe now, not a middleman app, so just launching with:

      await FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync(“Parameters”);

      and appXManifest

      Like

  5. Tried your example starting a .NET Core console fulltrust process with parameters and it launches with error, starting with cmd prompt in the AppX folder manually starts fine, so frustrating.

    Like

      1. Yes the .exe is in the WindowsPackagingProject, I included it in the root node of the project, the .NET Core console app relies on dll, xml and json files they are also included in the project root node and set to content->copy always.

        Using like:

        OnLaunched -> App.xaml.cs
        await FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync(“Parameters”);

        AppXManifest:

        Like

      2. The console app needs to be in a subfolder of the package, and it needs to be compiled as self-contained EXE. If this doesn’t help, please post your question on stackoverflow and include an actual repro so we can help you there.

        Like

  6. Thanks but unfortunately no change, not sure if I need to include all the dotnet core dlls from the publish folder or not but UWP VS2017 crashes when I change content to copyalways for all the included core dlls in the package.

    Like

  7. Hey Stefan, great examples!
    Is there any way to pass through dynamic arguments to the app you’re launching? My use case is that my arguments cannot be hard coded in the app manifest, they are generated on the fly in code and passed across to a console app.
    I’ve tried the below
    string cmdArg = @”Install” + @”$” + installPath;
    ApplicationData.Current.LocalSettings.Values[“Install”] = cmdArg;
    await FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync(“Install”);
    Which creates an entry in my local settings fine, however the value being passed across to my console app is the hard coded value defined in my app manifest only. And not the value I’ve added. So is there a way to do this? Or at the very least a way around this?

    Like

    1. You need to change the code in your console app to read the arguments from the LocalSettings instead of the command line. If you can’t change your console app you can add an extra helper EXE that you launch from the UWP, which reads the LocalSettings and then invokes your actual console EXE with the desired command line parameters.

      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 )

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 )

Connecting to %s