Combining .NET 5, WinUI, UWP – FullTrust, PartialTrust, WindowsAppContainer

As Microsoft progresses towards the first release of WinUI3 and in parallel invests into Project Reunion, the gap between Win32 based applications (eg WinForms, WPF) and UWP applications is being eroded. In this post we’re going to look at the role of trust, identity, capabilities and the Windows App Container, and how they relate to both UWP and .NET 5 based applications.

Before we get started, one of the thing that’s becoming apparent is that “UWP” as a singular definition (take this definition for example) for a style of applications doesn’t cut it. In this post, when we refer to a UWP application, we’re simply referring to an application that’s been created using the UWP project template in Visual Studio.

The way we’re going to explore these topics is with a series of sample applications. With each application we’ll change some attributes and look at how it impacts the application.

Vanilla UWP

Let’s kick off with a vanilla UWP application, one that’s created using the Blank App (Universal Windows) project template in Visual Studio. On the MainPage we’re going to drop a couple of buttons.

<Grid>
    <StackPanel VerticalAlignment="Center"
                HorizontalAlignment="Center">
        <Button Content="Write to Temp folder"
                HorizontalAlignment="Stretch"
                Click="TempWriteButtonClick"
                Margin="12" />
        <Button Content="Write to AppData folder"
                HorizontalAlignment="Stretch"
                Margin="12"
                Click="AppDataWriteButtonClick" />
    </StackPanel>
</Grid>

In the code-behind file we’ll add some code to write some text to a file.

private void TempWriteButtonClick(object sender, RoutedEventArgs e)
{
    File.WriteAllText(@"c:\temp\dummyoutput.txt", "File contents");
}

private void AppDataWriteButtonClick(object sender, RoutedEventArgs e)
{
    var file = System.IO.Path.Combine(ApplicationData.Current.LocalFolder.Path, $"dummyoutput.txt");

    File.WriteAllText(file, "File contents");
}

The two buttons write the same text to two different files: the first is located in the c:\temp folder, the second is located in the LocalFolder of ApplicationData.

When we run this application and click the buttons, the code for writing to ApplicationData works. However, writing to c:\temp generates an exception: System.UnauthorizedAccessException: ‘Access to the path ‘c:\temp\dummyoutput.txt’ is denied.’. Anyone familiar with UWP will know that this is to be expected since the sandbox that UWP applications run in prevent them from writing to random files without the user’s consent. The ApplicationData folder is a pre-approved location that a UWP application can write to and is cleaned up when the application is deleted.

Before we move on, let’s take a quick look at the context that this application is running using Process Explorer. For a bit of background on processes and appcontainers I would highly recommend the following two posts:

Demystifying AppContainers in Windows 8 (Part I)

Demystifying AppContainers (Part II)

Opening Process Explorer and searching for our application, VanillaUWPApp.exe (and yes, even though it’s a UWP app, the entry point is still an exe), we find it located not as its own process but as a child of wininit.exe, services.exe and svchost.exe. If we take a look at the Security tab of the Properties dialog for the application we can see a couple of interesting things:

  • The Integrity of the application is Low Mandatory Level
  • It’s running in the context of an AppContainer
  • There are a number of Capabilities associated with the application

Vanilla WPF

Let’s jump to the other end of the spectrum and look at a similar application based on the WPF Application project template. We’re going to be targeting .NET5 and we need to be able to access the WinRT APIs (i.e. for ApplicationData), so our csproj looks like:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net5.0-windows10.0.17763.0</TargetFramework>
    <UseWPF>true</UseWPF>
  </PropertyGroup>
</Project>

The XAML and code-behind is going to be identical to what we had for the vanilla UWP application. However, this time when we run the application we see the opposite: writing to c:\temp works fine but writing to ApplicationData fails (with an obscure exception: System.InvalidOperationException: ‘Exception_WasThrown’). Again this result shouldn’t be suprising – as a Win32 application it should have the same permissions as the user, so there shouldn’t be any issue writing to either folder. The issue with ApplicationData is that the application itself has no identity, meaning that there’s no way for the runtime to resolve where ApplicationData should be mapped to.

This time in Process Explorer the VanillaWPFApp.exe is located as a child of wininit.exe, services.exe, svchost.exe and explorer.exe. Again, looking at the Security tab, we can see:

  • The Integrity of the application is Medium Mandatory Level
  • It’s not running in the context of an AppContainer
  • There are no Capabilities associated with the application

Full Trust Packaged WPF

In the vanilla WPF example we weren’t able to write a file to ApplicationData because the application doesn’t have an identity. One of the easiest ways to address this is by using a Windows Application Packaging Project to package the WPF application. Our next application, FullTrustWPFApp, is going to be exactly the same as the vanilla WPF application, except it will have a matching packaging project, FullTrustWPFAppPAckaged. After creating the packaging project, you simply need to add a reference to the WPF application to the Applications node in Solution Explorer. You can either publish, and then install, the packaging project, or you can simply set the packaging project as the startup project. Either option will launch the WPF application with an identity.

What does this change do for the running context of the WPF application? Let’s again turn to Process Explorer – this time the application, FullTrustWPFApp.exe, appears as a direct child of explorer.exe. What’s interesting is that it has made very little difference to the Security context of the application – the Integrity is still Medium Mandatory Level and we’re not running in an app container.

Partial Trust Packaged WPF

In the previous example we went with the defaults for the packaging project, which resulted in our application being run as a full trust application. If we want to distribute our application via the Store, the installer would indicate to the user that the application is requesting the “Uses all system resources” capability, which for most users would be a bit alarming.

The alternative to this is to specify that the application should be run with partial trust. To do this, we just need to adjust the Trust Level property of the application (in this case our example app is called PartialTrustWPFApp) in the Applications list in the packaging project.

You’ll also need to make the following changes to the Package.appxmanifest:

  • Remove the runFullTrust capability
<Capabilities>
<Capability Name="internetClient" />
<!--<rescap:Capability Name="runFullTrust" />-->
</Capabilities>
  • Add a PhoneIdentity element directly under the Package element. The PhoneProductId should match the Name attribute of the Identity element.
<Package
  xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
  xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
  xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
  xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
  IgnorableNamespaces="uap rescap">

  <Identity
    Name="7feb3db5-5d87-468e-850e-1ac01a29a3da"
    Publisher="CN=NickRandolph"
    Version="1.0.0.0" />

	<mp:PhoneIdentity PhoneProductId="7feb3db5-5d87-468e-850e-1ac01a29a3da" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>

When we run this application it still appears as a direct child to explorer.exe in Process Explorer. However, the Security context of the application has changed significantly.

Now our application has these attributes:

  • It has Integrity set to Low Mandatory Level
  • It’s running in an AppContainer
  • It has a number of capabilities associated with it.

This is looking very similar to our vanilla UWP example from earlier. In fact, the behaviour is quite similar too – writing to ApplicationData works but writing to c:\temp throws the System.UnauthorizedAccessException.

Full Trust UWP

The last thing we’re going to do is to add the runFullTrust capability to a UWP application and see what effect this has.

The upshot is that this makes absolutely no difference to a UWP application – this capability only impacts packaged Win32 applications and extensions. However, it does add the “Uses all system resources” permission to the installation dialog, so you should not include this capability.

There’s an interesting thread discussing this, with a great response from the Windows Terminal team.

Summary

Hopefully in this post you’ve seen a couple of examples of different trust levels and how they interact/control the use of app containers. The code for all these examples is available here on GitHub

Side note: I didn’t call this out during the post, but the relationship with WinUI3 is that you can build a WinUI3 for desktop application and have it run in an app container using the partial trust option.

4 thoughts on “Combining .NET 5, WinUI, UWP – FullTrust, PartialTrust, WindowsAppContainer”

  1. Hi Nick, thanks for this very interesting post!

    For the landlubbers among us, can you provide a more explicit step-by-step description that will lead to a project having the combination advertised in the title — UWP, .NET 5, and WinUI (3, I hope) ? I think a lot of people would love that!

    The example you did in your post started from WPF. I am starting from the “Blank App, Packaged (WinUI in Desktop)” project template (preview 3). One difference being, of course, WinUI3 vs WPF. In addition I am not clear whether your example allows calling the various UWP support API such as FileOpenPicker which calls through the broker process to provide access to files outside of the AppContainer directory.

    Steve

    Reply

Leave a comment