Special Windows 10 issue 2015
Volume 30 Number 11
Microsoft .NET - .NET and Universal Windows Platform Development
By Daniel Jacobson | Windows 2015
With Visual Studio 2015, you can now use the latest .NET technology to build Universal Windows Platform (UWP) applications that run on all Windows 10 devices—including the phone in your pocket, the laptop or tablet in your bag, the Surface Hub in your office, the Xbox console in your home, HoloLens, and any other devices you can imagine on the Internet of Things (IoT). It’s truly an exciting time to be a Windows developer.
What’s New with the UWP?
As a .NET developer, you’ll appreciate all that the UWP has to offer. UWP apps will run in “Windowed” mode on the huge number of desktops that have been, and will continue to be, upgraded to Windows 10. UWP apps will be able to reach all Windows 10 devices with one application package and one code base. In addition, UWP apps take advantage of the new Microsoft .NET Core Framework (explained in detail later in this article). Your .NET business logic can run on other platforms that support .NET Core, such as ASP.NET 5. UWP apps deploy a small copy of the .NET Core with the app, so the app will always run against the .NET version you tested it against. All .NET UWP apps take full advantage of .NET Native, which generates highly optimized native machine code, resulting in performance gains (also explained in this article).
.NET Core Framework
The .NET Core Framework is a new version of .NET for modern device and cloud workloads. It’s a general-purpose and modular implementation of the Microsoft .NET Framework that can be ported and used in many different environments for a variety of workloads. In addition, the .NET Core Framework is open source and available on GitHub (github.com/dotnet/corefx) and is supported by Microsoft on Windows, Linux, and Mac OS X. If you’re a UWP developer utilizing the latest .NET technology, this brings you huge advantages. In Visual Studio 2015, you can utilize .NET Core portable class libraries (PCLs) to target any UWP app, .NET 4.6 app or ASP.NET 5 app—even those that are cross-platform.
In addition, the .NET Core Framework is a superset of the .NET APIs that were previously available in Windows Store app development. This means UWP developers now have several additional namespaces available in their API arsenal. One such namespace is System.Net.Sockets, which is used for UDP communication. This was previously unavailable in Windows Runtime (WinRT) apps, and the workaround was to use the WinRT-specific UDP APIs. Now that Sockets is available in the .NET Core, you can utilize the same Socket code in your UWP apps and other .NET apps.
Another advantage is that the System.Net.Http.HttpClient API is built on the WinRT HTTP stacks. This provides the ability to use HTTP/2 by default if the server supports it, resulting in lower latency and fewer round-trip communications.
The Windows Communication Foundation (WCF) client (and the associated Add Service Reference dialog) was previously unavailable in Windows Phone .appx projects, but because it’s a part of .NET Core, it can be used by all .NET UWP apps.
Finally, .NET Core is the underlying framework on which .NET Native depends. When .NET Native was designed, it was clear that the .NET Framework wouldn’t be suitable as the foundation for the framework class libraries. That’s because .NET Native statically links the framework with the application, and then removes the extra stuff that isn’t needed by the application. (This is a gross simplification, but you get the idea. For more details, take a look at “Inside .NET Native” at bit.ly/1UR7ChW.)
The traditional .NET Framework implementation isn’t factored, which makes it a challenge for a linker to reduce the amount of framework that gets compiled into the application. The .NET Core is, after all, essentially a fork of the .NET Framework whose implementation is optimized around factoring concerns. Another benefit of this implementation is the ability to ship the .NET Core Framework as a set of NuGet packages, such that you can update individual classes out-of-band from the .NET Framework. Before going any further, though, let’s talk about the changes in NuGet.
What’s New in NuGet?
With the UWP, NuGet 3.1 support is built-in. Included in this release are features that improve package dependency management and a local caching of your packages for reuse in multiple projects.
With NuGet 3.1, the package-dependency declaration model has been updated. Beginning with ASP.NET 5, NuGet introduced support for the project.json file, and this is the same model the UWP supports. Project.json enables you to describe the dependencies of a project with a clear definition of the packages on which you immediately depend. Because the format is the same for ASP.NET 5 and the UWP, you can use the same file to define package references for both platforms, as well as PCLs.
Changing from packages.config to project.json enables you to “reboot” the references in your projects, and there’s now a new transitive dependency capability of NuGet. If you reference a package that references another NuGet package, it used to be difficult to manage package versioning. For example, NHibernate is a package that depends on lesi.Collections. In the packages.config, you’d have two references, one for each package. When you want to update NHibernate, do you also update lesi.collections? Or, if there’s an update for lesi.collections, do you also have to update NHibernate to support the new features? It turned into a messy cycle, and package version management was difficult. The transitive dependencies feature of NuGet abstracts this decision to update package references with improved semantic versioning in the package definition files (nuspecs).
In addition, NuGet now downloads and stores a copy of the packages you use in a global packages folder located in your %userprofile%\.nuget\packages folder. Not only will this improve performance in package referencing because you’ll only need to download each package once, but it should also reduce the disk space used on your workstation as you can share the same package binaries from project to project.
NuGet and .NET Core
What happens when you take the factoring-focused .NET Core and combine it with the package-dependency management of NuGet 3.1? You get the ability to update individual .NET Framework packages out-of-band from the rest of the .NET Framework. With the UWP, the .NET Core is included as a set of NuGet packages in your app. When you create a new project, you’ll see only the general Microsoft.NETCore.UniversalWindowsPlatform package dependency, but if you look at this package on NuGet, you’ll see all of the .NET Framework libraries that are included, as shown in Figure 1.
Figure 1 Viewing .NET Framework Libraries in NuGet
For example, say there’s an update to the System.Net.Sockets class that introduces a new API you’d like to use in your application. With traditional .NET, your application would need to take a dependency on a new build of the entire .NET Framework. With the UWP and .NET Core with NuGet, you can update your NuGet dependencies to include the latest version of just that package. Then, when your application is compiled and packaged, that version of the framework library will get included in your application. This gives you the flexibility to use the latest and greatest .NET technology, without forcing your users to always have the latest framework installed on their devices.
In addition to being able to update your .NET classes at your own cadence, the componentized nature of .NET Core also enables .NET Native, providing performance benefits for all Windows 10 C# and Visual Basic applications when delivered to consumer devices.
What Is .NET Native?
Now that you know the .NET Core Framework enables .NET Native, I’ll explain in more detail what it is and what it does for you as a UWP developer.
.NET Native is an ahead-of-time (AOT) compilation process—it turns your managed .NET code into native machine code at compile time. In contrast, traditional .NET uses just-in-time (JIT) compilation, which defers the native compilation of a method until its first execution at run time. .NET Native is more similar to a C++ compiler. In fact, it uses the Visual Studio C++ compiler as part of its tool chain. Every managed (C# or Visual Basic) Universal Windows app will utilize this new technology. The applications are automatically compiled to native code before they reach consumer devices. If you’d like to dive deeper into how it works, I highly recommend reading the MSDN Library article, “Compiling Apps with .NET Native,” at bit.ly/1QcTGxm.
How Does .NET Native Impact You and Your App?
Your mileage likely will vary, but for most cases your app will start up faster, perform better, and consume fewer system resources. You can expect to see up to a 60 percent performance improvement on your applications the first time they start, and up to a 40 percent improvement on subsequent startup times (“warm” startup). Your applications will consume less memory when compiled natively. All dependencies on the .NET runtime are removed, so your end users will never need to break out of their setup experience to acquire the specific version of the .NET Framework your app references. In fact, all the .NET dependencies are packaged within your application, so the behavior of your app shouldn’t change just because there’s a change in the .NET Framework installed on the machine.
Even though your application is being compiled to native binaries, you still get to take advantage of the .NET languages you’re familiar with (C# or Visual Basic), and the excellent tools associated with them. Finally, you can continue to use the comprehensive and consistent programming model available with the .NET Framework, with extensive APIs for business logic, built-in memory management and exception handling.
With .NET Native, you get the best of both worlds: managed development with C++ performance. How cool is that?
Debug Versus Release-Compile Configuration
.NET Native compilation is a complex process, and that makes it a little slower than classic .NET compilation. The benefits mentioned earlier come at the cost of compilation time. You could choose to compile natively every time you want to run your app, but you’d be spending extra time waiting for the build to finish. The Visual Studio tooling is designed to address this and create the smoothest possible developer experience.
When you build and run in “Debug” configuration, you’re running Intermediate Language code against the CoreCLR packaged within your application. The .NET system assemblies are packaged alongside your application code, and your application takes a dependency on the Microsoft.NET.CoreRuntime (CoreCLR) package. If the CoreCLR framework is missing from the device you’re testing on, Visual Studio will automatically detect that and install it before deploying your application.
This means you get the best development experience possible—fast compilation and deployment, rich debugging and diagnostics, and all of the tools you’re accustomed to with .NET development.
When you switch to “Release” mode, by default your app utilizes the .NET Native toolchain. Because the package is compiled to native binaries, the package doesn’t need to contain the .NET Framework libraries. Moreover, the package is dependent on the latest installed .NET Native runtime as opposed to the CoreCLR package. The .NET Native runtime on the device will always be compatible with your application package.
Local native compilation via the “Release” configuration will enable testing your application in an environment that’s similar to what your customers will experience. It’s important to test this on a regular basis as you proceed with development! By testing your application using the code generation and runtime technology your customers will experience, you’ll make sure you’ve addressed all possible bugs (such as potential race conditions that will result from different performance characteristics).
A good rule of thumb is to test your app this way periodically throughout development to make sure you identify and correct any issues that might come from the .NET Native compiler. There should be no issues in the majority of cases; however, there are still a few things that don’t play so nicely with .NET Native. Four-plus dimensional arrays are one example. Ultimately, your customers will be getting the .NET Native compiled version of your application, so it’s always a good idea to test that version throughout development and before shipping.
In addition to testing with .NET Native compilation, you might also notice that the AnyCPU build configuration has disappeared. With .NET Native, AnyCPU is no longer a valid build configuration because native compilation is architecture-dependent. An additional consequence of this is that when you package your application, you should select all three architecture configurations (x86, x64 and ARM) to make sure your application is applicable to as many devices as possible. This is the Universal Windows Platform, after all.
With that said, you can still build AnyCPU libraries and DLLs to be referenced in your UWP app. These components will be compiled to architecture-specific binaries based on the configuration of the project (.appx) consuming it.
.NET Native in the Cloud
One great feature of .NET Native is that the compiler can be hosted in the cloud. That means when improvements are made to the compiler that might have a beneficial impact on your application, the Store’s cloud-hosted .NET Native compiler can recompile your application package to reap the benefits. Any time this compilation is done, it will be transparent to you as the developer, but it will ultimately mean happier consumers of your application.
However, this can have some impact on your workflow. For example, it’s a good idea to make sure you always have the latest tools installed so you can be testing your .NET Native compilation against the most recent local version of the compiler. In addition, when you build your Store package in Visual Studio, two packages are created—one .appxupload and one “test” .appx for sideloading. The .appxupload contains the MSIL binaries, as well as an explicit reference to the version of the .NET Native toolchain your app consumes (referenced in the AppxManifest.xml as “ilc.exe”). This package then goes to the Store and is compiled using the exact same version of the .NET Native toolchain. Because the compiler is cloud-hosted, it can iterate to fix bugs without you having to recompile your app locally.
With .NET Native, you have to be careful about which package you upload to the Store. Because the Store does the native compilation for you, you can’t upload the native binaries generated by the local .NET Native compiler. The Visual Studio workflow will guide you through this process so you select the right package. For full guidance on creating a Store package, take a look at the MSDN Library article, “Packaging Universal Windows Apps for Windows 10,” at bit.ly/1OQTTG0. This will guide you through the package creation process to make sure you generate and choose the right package to upload to the store.
Debugging with .NET Native
If you find issues in your application you suspect are caused by .NET Native, there’s a technique you can use to help debug the issue. Release configurations fully optimize code (for example, code inlining is applied in many places) by default, which loses some debugging artifacts. As a result, trying to debug a Release configuration app can be difficult; you might experience unpredictable stepping and breakpoint behavior, as well as the inability to inspect variables due to memory optimization. Because the default behavior of Release configurations is to use the .NET Native compiler with code optimization, it’s difficult to debug any issues that might be a result of the .NET Native compilation process.
A good way to get around this is to create a custom-build configuration for your project that utilizes the .NET Native compiler but doesn’t fully optimize code. To create a custom-build configuration, open the Configuration Manager from the build configuration dropdown, as shown in Figure 2.
Figure 2 Opening the Configuration Manager
In the Active solution configuration dropdown, choose <New…> to create a new configuration, as shown in Figure 3.
Figure 3 Creating a New Configuration
Give the new configuration a name that will be useful to you later. I like to use “Debug .NET Native.” Copy settings from the “Release” build configuration and then click OK.
Close the Configuration Manager and open the project’s property page by right-clicking on the project in Solution Explorer, and clicking Properties. Navigate to the Build tab and make sure that Compile with .NET Native tool chain is checked, and Optimize code is unchecked, as shown in Figure 4.
Figure 4 Creating a Build Configuration for Debugging .NET Native
You now have a build configuration you can use for debugging .NET Native-specific issues.
For more information about debugging with .NET Native, refer to the MSDN Library article, “Debugging .NET Native Windows Universal Apps,” at bit.ly/1Ixd07v.
.NET Native Analyzer
Of course it’s good to know how to debug issues, but wouldn’t it be better if you could avoid them from the get-go? The Microsoft.NETNative.Analyzer (bit.ly/1LugGnO) can be installed in your application via NuGet. From the Package Manager Console, you can install the package via the following command: Install-Package Microsoft.NETNative.Analyzer. At development time, this analyzer will give you warnings if your code isn’t compatible with the .NET Native compiler. There’s a small section of the .NET surface that’s not compatible, but for the majority of apps this will never be a problem.
As you can see, it’s an exciting time to be a .NET Windows developer. With the UWP, .NET Native and changes to NuGet, it has never been easier to create apps across so many different devices that your customers will love. For the first time, you can take advantage of the latest advances in any .NET class and still expect your application to run on all Windows 10 devices.
Daniel Jacobson is a program manager for Visual Studio, working on tools for Windows platform developers. Reach him at email@example.com.
Thanks to the following technical experts for reviewing this article: Kino Aguilar, Adam Denning, Yishai Galatzer, Jenny Hayes, Jeremy Meng, Harikrishna Menon, Jessica Prince, Unni Ravindranathan, Navit Saxena, Michael Strehovsky, Debbie Thorn, Matthew Whilde, Lucian Wischik