Experimenting with .NET 5 Target Framework Names and the Windows platform

Firstly, if you haven’t been following the development of .NET 5 then you should definitely download the latest Visual Studio preview and .NET 5 preview SDK today. Next, you should follow the blogs from the dotnet team and specifically the post by Immo that discusses the future of .NET Standard. The post doesn’t just cover … Continue reading “Experimenting with .NET 5 Target Framework Names and the Windows platform”

Firstly, if you haven’t been following the development of .NET 5 then you should definitely download the latest Visual Studio preview and .NET 5 preview SDK today. Next, you should follow the blogs from the dotnet team and specifically the post by Immo that discusses the future of .NET Standard. The post doesn’t just cover .NET Standard, it also covers the basics of how target framework names (TFMs) work in the .NET 5+ era. In this post we’re going to play around with this and take a look at some examples of different TFMs in action.

NetCoreApp

Let’s start by creating a new project based on the Console Application project template (formerly the Console App (.NET Core) template).

When prompted we’ll select the .NET 5 (Preview) target framework.

Out of the box, this gives use the following project, which as you’d expect targets the net5.0 target framework.

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>
</Project>

What does this actually mean? Well, let’s add some debugging output to this project file.

<Project Sdk="Microsoft.NET.Sdk"  InitialTargets="Init">
	<PropertyGroup>
		<OutputType>Exe</OutputType>
		<TargetFramework>net5.0</TargetFramework>
	</PropertyGroup>
	<Target Name="Init">
		<Warning Text="$(TargetFrameworkMoniker)" />
		<Warning Text="$(TargetPlatformMoniker)" />
	</Target>
</Project>

When we build the project we’ll see two additional lines in the output that will show the actual TargetFrameworkMoniker and the TargetPlatformMoniker.

1>------ Rebuild All started: Project: TFMSample, Configuration: Debug Any CPU ------
1>c:\temp\TFMSample\TFMSample\TFMSample.csproj(7,3): warning : .NETCoreApp,Version=v5.0
1>c:\temp\TFMSample\TFMSample\TFMSample.csproj(8,3): warning : (No message specified)
1>You are using a preview version of .NET. See: https://aka.ms/dotnet-core-preview
1>TFMSample -> c:\temp\TFMSample\TFMSample\bin\Debug\net5.0\TFMSample.dll
1>Done building project "TFMSample.csproj".
========== Rebuild All: 1 succeeded, 0 failed, 0 skipped ==========

What’s interesting here is that the TargetFrameworkMoniker is actually .NETCoreApp. This means that net5.0, is actually the same as writing netcoreapp5.0 (e.g. <TargetFramework>netcoreapp5.0</TargetFramework >).

Windows 7: WindowsForms and WPF

According to the design documents for Target Framework Names in .NET 5 if we want to target Windows specific apis then we should be able to add the -windows suffix to the TFM. If we change the TFM to .net5.0-windows, this is what we see when we build the project (I’m just going to show the two output lines for brevity).

1>c:\temp\TFMSample\TFMSample\TFMSample.csproj(7,3): warning : .NETCoreApp,Version=v5.0
1>c:\temp\TFMSample\TFMSample\TFMSample.csproj(8,3): warning : Windows,Version=7.0

With this change we’re now targeting the Windows platform and specifically version 7. As you’d imagine this aligns with the API set that was available for Windows 7….. it does not mean that the output can be run on a Windows 7 device. In this case, we should be able to access the APIs from Windows Forms and WPF. Let’s try this out by attempting to reference a WinForms API

static void Main(string[] args)
{
    Console.WriteLine("Hello .NET 5!");
    System.Windows.Forms.Application.SetHighDpiMode(System.Windows.Forms.HighDpiMode.SystemAware);
    Console.ReadLine();
}

This doesn’t compile, yielding the following error.

At this point I started to scratch my head – Surely since .net5 is just .netcoreapp5 then we should be able to reference WinForms and WPF in the same way as we did when targeting netcoreapp3.1. If you go back and create a WinForms or WPF app using the .NET Core project templates, you’ll see that the project file is basically the same as what we have for net5.0 with the exception that it includes an additional project UseWindowsForms or UseWPF.

Adding UseWindowsForms to our net5.0 project fixes our build issue. If you look at the Dependencies, you’ll see that adding UseWindowsForms adds a dependency on the Microsoft.WindowsDekstop.App.WindowsForms package.

It’s no surprise that we can do the same with UseWPF, which adds Microsoft.WindowsDesktop.App.WPF as a dependency.

Windows 10: UWP

Currently we’re using net5.0-windows and as we saw this maps to version 7 of the Windows platform. If we change the tfm to net5.0-windows7, it’s not surprise that we see exactly the same output (i.e. we’re still targeting version 7 of the Windows platform). Now let’s change it to net5.0-windows10.

1>------ Rebuild All started: Project: TFMSample, Configuration: Debug Any CPU ------
1>c:\temp\TFMSample\TFMSample\TFMSample.csproj(9,3): warning : .NETCoreApp,Version=v5.0
1>c:\temp\TFMSample\TFMSample\TFMSample.csproj(10,3): warning : Windows,Version=10.0.0.0
1>C:\Program Files\dotnet\sdk\5.0.100-rc.1.20452.10\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.TargetFrameworkInference.targets(222,5): error NETSDK1140: 10.0.0.0 is not a valid TargetPlatformVersion for Windows. Valid versions include:
1>C:\Program Files\dotnet\sdk\5.0.100-rc.1.20452.10\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.TargetFrameworkInference.targets(222,5): error NETSDK1140: 10.0.19041.0
1>C:\Program Files\dotnet\sdk\5.0.100-rc.1.20452.10\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.TargetFrameworkInference.targets(222,5): error NETSDK1140: 10.0.18362.0
1>C:\Program Files\dotnet\sdk\5.0.100-rc.1.20452.10\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.TargetFrameworkInference.targets(222,5): error NETSDK1140: 10.0.17763.0
1>C:\Program Files\dotnet\sdk\5.0.100-rc.1.20452.10\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.TargetFrameworkInference.targets(222,5): error NETSDK1140: 8.0
1>C:\Program Files\dotnet\sdk\5.0.100-rc.1.20452.10\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.TargetFrameworkInference.targets(222,5): error NETSDK1140: 7.0
1>Done building project "TFMSample.csproj" -- FAILED.
========== Rebuild All: 0 succeeded, 1 failed, 0 skipped ==========

As you’d expect, the windows10 maps to a version number of 10.0.0.0, which, as the error explains, is not a valid version number. There are a couple of interesting things to point out here. Firstly, that the only supported versions of Windows 10 are currently 10.0.17763.0, 10.0.18362.0 and 10.0.19041.0, which limits the backward compatibility of .net5 to only those versions of Windows that are still being supported. Whilst it’s not the official documentation on Windows 10 support, Wikipedia has a nice visual representation of supported versions of Windows 10.

The other thing to note from the error messages is that there is a version 8.0 that’s supported. I’m not sure what APIs are included in version 8.0 but I would imagine that they’re the WinRT APIs that align with Windows 8.0/8.1. I’m not sure why you’d necessarily want to target version 8.0, and not Windows 10, so I’m not going to skip over version 8.0 in this post.

Let’s update our TFM to target version 10.0.18362.0 (i.e. net5.0-windows10.0.18362.0). Now we see the addition of Microsoft.Windows.SDK.NET.Ref as a dependency.

When we build the project we now see the following output, which is what we’d expect.

1>c:\temp\TFMSample\TFMSample\TFMSample.csproj(9,3): warning : .NETCoreApp,Version=v5.0
1>c:\temp\TFMSample\TFMSample\TFMSample.csproj(10,3): warning : Windows,Version=10.0.18362.0

Let’s update our code to access a WinRT api – in this case the Storage API.

static void Main(string[] args)
{
    Console.WriteLine("Hello .NET 5!");
    var tempFolder = ApplicationData.Current.TemporaryFolder;
    Console.WriteLine("Folder " + tempFolder.DisplayName);
    Console.ReadLine();
}

This compiles but throws an InvalidOperationException when we attempt to run it.

Typically when you’re accessing WinRT apis you’re doing so from within the confines of say a UWP application. Here we’re attempting to access the WinRT apis from a Win32 application, so you can imagine there’s some extra work we need to do in order for our application to be permitted to access those apis.

The easiest way to grant access to the apis, is to add a Windows Packaging Project to our solution.

Right-click on the Applications folder in the packaging project and select Add Reference. Select the TFMSample project.

If you set the packaging project as the startup project you can attempt to build and run the application. However, in the current Visual Studio preview you’ll see an error in the debug output

The target process exited without raising a CoreCLR started event. Ensure that the target process is configured to use .NET Core. This may be expected if the target process did not run on .NET Core.

Right-click on the packaging project and select Publish, Create App Package. Follow the prompts (you’ll need to select or create a signing certificate) to create the app package. If you then right-click on the packaging project again and select Deploy, this should install the generated package onto your computer. Then you can run the application from the Start menu – it’ll have the name of the packaging project, not your net5 application.

Hopefully this post has given you some insight into how the target framework names will work for .net5+.

Leave a Reply

Your email address will not be published. Required fields are marked *