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

64 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

  8. Hi StefanWick, at first thanks for your great article. That’s very useful to me.
    I download your part2-demo from MS Store, and tested according to this link:
    https://docs.microsoft.com/en-us/windows/uwp/porting/desktop-to-uwp-test-windows-s
    It works fine, so that mean we can use “Process.Start” on Windows 10 S mode.
    So if the desktop “.exe” are in Windows app package, then we can do inter-process communication between them.
    My question is that, if we require elevated security privileges in desktop “.exe”. Does it still work on Windows 10 S mode?

    Like

  9. Hi, cannot figure this out, fullTrustProcess and app working fine until I download it from the store… does not work at all, also I get the following warning after the package has been analysed in the dev portal:
    “Package acceptance validation warning: The following restricted capabilities require approval before you can use them in your app: runFullTrust, broadFileSystemAccess”

    I am dumbfounded as both the capabilities are in the Package.appmanifest…..

    Like

    1. Please clarify what you mean by “does not work at all”. Do you mean (a) the app doesn’t actually get published to the Store, or (b) the app is published, but crashes when you launch it, or something else? Btw, the warning is expected on first submission of a package with restricted capabilities, until it is actually certified.

      Like

      1. Hi Stephan, the app is published but the store version will not launch the .NETCore bridge app which is part of the package and is launched by the full trust process, works fine via Visual Studio locally. Certification and publishing happened already, I heard though it needs to be manually checked and approved which could take up to 1 week….

        Like

      2. Can you tell whether you get a failure from the FullTrustProcessLauncher.LaunchAsync() call – or if the .NET core app is crashing on launch? If the latter, you may be missing a dependency in your package manifest.

        Like

      3. Hi Stephan, thanks for the reply, I cannot tell about a failure of crash from the Store download, It does seem however that the .NET core app window never even launches, the app is set for the console to hide upon launch but is should very briefly be visible, but it is not and does not show up in the package hierarchy in task manager….

        Like

  10. I just tries submitting the appxbundle instead of the bundle.appxupload and I got the following errors:

    “You must fix all package validation errors before submitting.

    appname_x64.appxbundle19.2 MB
    Package acceptance validation error: You cannot submit pre-compiled .NET Native packages. Please upload the Microsoft Store appxupload file and try again. Learn more”

    So I guess before the .NETCore app was getting parsed out or not included…. weird

    Like

    1. That’s not what this error means. You can check if your .NETCore app was included in the Store package by inspecting the .appxupload file (it’s just a nested .zip file), and/or by looking at the store-installed app on disk. Can you share the store-install link?

      Like

    1. Hmm, didn’t work let me try again. Replacing the brackets:

      [PackageDependency Name=”Microsoft.VCLibs.140.00.UWPDesktop”
      Publisher=”CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US”
      MinVersion=”14.0.26905.0″ /]

      Liked by 1 person

      1. It’s actually a bug. The .NET Core executable should be completely self-contained, but currently it’s not. Once the bug is fixed, declaring this dependency will no longer be necessary. But for now you have to put it in the manifest.

        Like

  11. [PackageDependency Name=”Microsoft.VCLibs.140.00.UWPDesktop” MinVersion=”14.0.24217.0″ Publisher=”CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US” ]

    Like

      1. Just checking something 1st, it seems that a previous version was getting downloaded as I only updated the main submission not the test-flight, I was not sure where the store was pulling the app from anyway, will test again soon. Thanks

        Like

      2. Hi Stefan, still no luck, there is a crash in the .NET Core app but having trouble finding it, the funny thing is this only happens when the app is grabbed from the store, not compiled and unhooked from debugger locally….

        Debug->Other debug targets->Debug installed app package yields the following
        [Unhandled exception at 0x00007FF9F3A1A388 (KernelBase.dll) in NBXplorer.exe: 0xE0434352 (parameters: 0xFFFFFFFF80070005, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x00007FF97E260000). occurred]

        Like

    1. Hard to diagnose without knowing your code. Unfortunately, I won’t be able to provide individual support through my personal blog. This is just a spare time project for me, not my day job. I don’t work in the Windows Application space anymore (in Azure now), but if you send me an email I will connect you with someone from that department to work with you through your Store submission problems. It will have to wait until Monday though as I am currently travelling on vacation.

      Like

      1. Hi Stefan, ok so it seems like I found the issue, basically NBXplorer (.NETCore app) created a folder on startup for database creation, this was fine when running locally but the downloaded store version was throwing a System.IO exception because of the hardcoded ms-appx path I designated was not in the StorageApplicationPermissions.FutureAccessList . So I hardcoded the parameter in the fullTrust to the folder I knew I would pick using the Folder Picker. This now poses an issue because I have no idea what folder the user will want to pick and cannot hardcode the path. Is there a way to set the fullTrustProcess parameter list in C# before the fullTrustProcess is launched? My guess is no 😉 Thanks

        Like

      2. This doesn’t sound right. Your .NETCore app is running as fulltrust process, so it can already write anywhere the user has write access. The futureaccesslist doesn’t apply here at all. My guess is that you are trying to write to the install folder which is write-protected when the app package is installed (it’s writeable when deploying/debugging with VS, that’s why you probably missed it). Why not write the folder to the app’s AppData location? If you do need to pass parameters dynamically from the UWP to the fulltrustprocess, this blog post explains how to do it.

        Like

  12. “My guess is that you are trying to write to the install folder which is write-protected when the app package is installed (it’s writeable when deploying/debugging with VS, that’s why you probably missed it).” You nailed it, I didn’t know that before, thank you.

    Like

  13. “If you do need to pass parameters dynamically from the UWP to the fulltrustprocess, this blog post explains how to do it.”

    Win32 side (Program.cs)
    string parameters = ApplicationData.Current.LocalSettings.Values[“parameters”]….

    .NETCore 2.1 does not seem to support ApplicationData

    Like

      1. Our app has some logic in a c++ dll that reads and update flat files in a specific location in the C drive (i.e. C:\\). That path is where we put flat files that is used as our offline database or cache. It was being FTP’ed to that location so that the app can go after those files whenever there’s no internet connection.

        Is it possible for a UWP app, with the help of UWP Desktop extension or DllImport in UWP, to go after those files? I’m thinking if we can reuse the C++ dlls because there’s a lot of logic in there that would be hard to rewrite.

        Like

      2. Yes, it’s possible. The best approach depends on your details. If you can link your DLL with the /APPCONTAINER flag and it only uses UWP-compliant APIs then you can load into your UWP process and call your library. If not (which is quite likely) then you can use a desktop extension process and call your library from there.

        Liked by 1 person

  14. Hi Stefan,

    Can you point me to a resource where the limitations of a desktop extension is documented? Our app has a lot of C++ dlls and some oft those dlls perform some low level functionalities like using the COM ports.

    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