Partager via


Win10 apps in .NET - projects, targets, libraries

This is Part 5 of my "VB Win10 Apps" series. (Everything applies to C# as well, of course).

 

Key conclusions:

  1. Universal Windows Apps simplify your project structure: you only need one project for all Win10 devices.
  2. PCLs targeting .NETCore5 - are the best answer if you can manage to stick to only .NET APIs. This will be easier now than before since there'll be a lot more .NET APIs in .NETCore5.
  3. PCLs targeting Win8.1+Phone8.1 - are the best answer if you want to work on UWP + Phone8.1. (or UWP + Phone8.1 + Win8.1).
  4. Class libraries targeting UWP - are the best answer (indeed the only answer) if you want to write adaptive code.
  5. Shared Projects - you can use these to share source code between projects, and they have lots of enhancements in VS2015.

 

What's outside the "Universe"?

When a Bond villain says "I am invincible" then he gets struck down. When you build a ship called "Titanic" it's going to sink. When you think that the galaxy is all there is, someone (Hubble 1929) teaches you that other galaxies exist. When you call something "Universal" then there's a whole range of things outside the universe. ..

Someone wrote to me saying, "I love idea of 'Write Once Right Everywhere', which was rebranded to 'Universal apps', but how does UWP help my app run on Android and iOS?" There's a lot of confusion in that question! Microsoft has been talking about “Universal Windows apps”. Not “universal apps”, at least not when the Microsoft speaker is paying attention to what they say (we had strong instructions from our branding department in this regard!) I think that “write once run anywhere” was only ever the slogan for Java, and it failed, because the only halfway decent UI stack that runs everywhere is HTML+JS, and even then it's no more than halfway decent…

 

The promise of Universal Windows Apps

The promise of “Universal Windows Apps” is that they work on all versions of Win10 (also called “UWP apps”). Emphasis on the word “Windows”. UWP is certainly NOT aiming to be a universal solution for client apps that run on all of Android + iOS + OSX + Linux + Windows.

What is the win with this strategy? It simplifies your VS solution for client apps:

Without UWP: With UWP:
 

 

The promise of .NET Core 5

For UWP apps, in VS2015 RC you could only use the same .NET APIs as were available to Win8.1 apps. But for VS2015 RTM, you will be able to use the full .NET Core 5 framework. This has a bunch more .NET APIs that you're familiar with from .NET on the desktop (e.g. WCF, Sockets, ...). It will be delivered via NuGet and packaged app-locally. It isn't tied to any underlying OS, and is portable across Windows, Mac and Linux.

 

What is the win with this stragey? - It will be easier to port the core logic from your existing Winforms/WPF apps into Win10 UWP apps. (Won't help with porting UI, of course).

Another win? - If you can stick to APIs that are with .NET Core 5, then you'll be able to author a single library DLL and have it work everywhere -- in .NET4.6 apps on the desktop (winforms, wpf, console), and in in UWP apps, and in ASP.NET vNext on all of its platforms, including console apps for Mac+Linux.

Another win? - the .NET framework is open-source, and made of NuGet packages, and deployed app-locally. So if there's a fix you need, it's easy for Microsoft (or any third party, or you) to update any part of the framework. And you don't need to wait for a new version of .NET to be installed on target devices: you can deploy an update to your app immediately, and it will include the update.

Another win? - The set of .NET APIs in .NETCore5 is growing considerably, large enough that for many purposes you don't need to venture outside (except for your UI layer of course). This means you can eliminate much more of the #ifdef or Platform Abstraction Layer code from your libraries.

Before .NETCore 5 After .NETCore 5
 
   

This was the code before .NETCore5:

And this is what it looks like after:

 

Sharing libraries between 8.1 and UWP

If you want a library which works in both 8.1 apps and UWP apps, the quick answer here is: "Make your library an 8.1 Universal PCL" (i.e. a PCL that targets both Win8.1 and Phone8.1). This PCL will also work fine in UWP apps.

The longer answer is that your library can only ever use the lowest common denominator. For WinRT APIs, what is the lowest common denominator between 8.1 and UWP apps? I tried to distill it into this diagram...

8.1 UWP
 

It's a complicated diagram! What I tried to show is that Win8.1 store apps could use one set of APIs, and Phone8.1 apps could use a slightly different APIs, and 8.1 Universal PCLs could use about 80% of each of them. Meanwhile, Win10 UWP is more or less a superset of 8.1 Universal PCLs. And the Win10 "Desktop Extension SDK" corresponds mostly to those things that were in Win8.1 but aren't in UWP. And likewise "Mobile Extension SDK". I also got the sizes approximately correct!

If those Venn Diagrams are too confusing, here are the conclusions:

  • If you have an 8.1 Universal PCL, then it can be referenced fine from Win8.1, Phone8.1 and UWP apps, and it can run fine on all Win10 devices.
    • That's not quite true... there's a tiny handful of APIs which are present in 8.1 Universal PCLs, but are absent from UWP, and can instead be found in both Win10 Desktop Extension SDK and Mobile Extension SDK. The only one you'll run into in practice is UserInformation.UserProfile. If your 8.1 Universal PCL mentions this type, it will run fine on Win10.Desktop and Win10.Mobile, but will crash at runtime on other Win10 devices.
       
  • If you have a Phone8.1 DLL, then it can be referenced fine from Phone8.1 and UWP apps. At it can run fine on Win8.1 and Win10.Mobile devices. But it has a fair chance of crashing when run on other Win10 devices, if it uses APIs that they lack.
    • If the DLL mentions any mobile-specific types in its public signatures, your UWP app will need a reference to Win10 Mobile Extension SDK to be able to use them.
    • The same thing goes for Win8.1 DLLs.

 

Adaptive code. Note that adaptive code (where you write "If ApiInformation.IsTypePresent(xyz)" and run different code branches accordingly) is only supported by the .NET runtime in UWP apps. And it is only ever able to adapt upon UWP APIs. Therefore you're only able to write adaptive code in a UWP Class Library: you simply can't write it in a PCL or a DLL that targets older platforms.

If you do need to write a library which works one way on UWP and a different way on other platforms, you have to fall back to using Shared Projects to build several different DLLs, and then package them up in a NuGet package.

 

Improvements in Shared Projects 

Shared Projects are a way of sharing source code itself between projects, rather than just sharing DLL binaries. Shared Projects let you use #If directives.

The way it works is that you create two or more "head" projects of any kind, and create a shared project, and add a "shared project reference" from each head to the shared project. Then any codefile or xaml file you write in the shared project, will be treated as if it's part of both head projects.

Personally I prefer PCLs. I'd rather write a "Platform Abstraction Layer" (also called "Inversion Of Control") than use #If. It makes my code cleaner. Nevertheless, there are times and places for shared projects. And if you use them, you'll be happy with two improvements:

  1. They now support for VB! When introduced in VS2013 Update2, they at first only supported C#.
  2. Intellisense in a shared file is now smart about what's available. Look at this screenshot:

The way it works, when you type the "dot", the compiler interrogates all head projects that use this shared file. It asks all of them what intellisense they'd like to show. It merges the list together, showing every single intellisense item available in any project, but showing a warning icon for anything that's not available in all projects.

It's pretty smart! If there's an instance method called "Foo" in one head project, say, but it's an extension method in another, intellisense knows that the method Foo is fine and doesn't need a warning triangle. And it works for all kinds of methods, no matter where they come from. (In VS2013 it only worked for WinRT APIs).

 

 

Some practical case studies

 

Example1: “I’m building few APIs, which wrap protocol independent communication with devices and IoT Hub services. Should I build it on top of .NETCore or on top of UWP WinRT APIs?”

  • If you’re writing VB or C#, and have a choice of whether to invoke a .NET or a UWP WinRT API, then I strongly suggest to use the .NET one. That makes your code easier to read, maintain and port.
  • If you can build your library sticking solely to the APIs provided by .NETCore, then by all means do so. Your task will be made easier because lots of useful APIs for networking, say, which used to be only in the full desktop .NET framework, are going to be moved into .NETCore. If you stick solely to .NETCore then your library can be consumed by .NET apps running on UWP, Win32, OSX, Linux. (And I hope in the future that Xamarin will also fully support .NETCore).
  • If you additionally want it available to C++ and JS apps on UWP, then write a “WinRT component” which wraps up your APS in a WinRT-friendly way.
  • It used to be that folks who wanted to write components that worked on C#/VB/C++/JS in Win8 would prefer to write their components in C++, to avoid the “native <-> managed interop”
    overhead. Thanks to .NET Native, even a managed component will get compiled into Native for release builds, so the impedance mismatch has mostly disappeared. More on that in a future post.
  • If you need to make use of OS APIs that aren’t available in .NETCore then you’ll have to fall back to invoking OS functionality directly. That means you’ll have to package your library as a NuGet, and have a UWP-specific platform abstraction layer, and one for Xamarin, and one for Linux, and one for Mac. No way around this.

 

Example2: should Microsoft offer AllJoyn support as part of UWP, or .NETCore?

  • This is an odd question. Let’s start with a putative third-party implementation of the AllJoyn protocol (which, I should say, I know nothing about…)
  • If someone writes an AllJoyn implementation in VB/C# that sits entirely on top of .NETCore APIs, then it will be widely portable across UWP and all other .NETCore platforms. It can be
    made available also to C++/JS UWP apps so long as someone writes a “WinRT component” wrapper around it.
  • As for Microsoft implementing this library itself, I think the .NET team generally haven’t been in the business for writing new components of .NET for every single new protocols. Even json.net is happily left to a third party. But the Windows division have more often been in the business of doing this themselves, and it looks like they are going to add AllJoyn to UWP? (I don’t know: I’m just going by what I read on internet searches).
  • If Microsoft Windows division have made an implementation of AllJoyn that’s in a UWP library, then by all means invoke it from your UWP apps. But if you want your app/library to work on all different platforms, you’ll have to do the grunt work yourself of finding an AllJoyn library for every other platform. I know that folks would like it if Microsoft did the grunt work of implementing AllJoyn on top of .NETCore. True, yes, we all want someone else to do as much of the work as possible! :)