Windows 8 Store Apps - What They Can and Can't Reference

16 October 2012

Summary:
In this post we look at the various options Windows Store apps have for referencing class libraries, Win32 dlls, databases, services, etc. Along the way we take in some theory about WinRT and the .NET Core version of the .NET framework designed for Windows Store apps.

Introduction

The other day I was playing around with some Windows Store app ideas and started looking at the different options for reusing code in libraries, referencing .NET assemblies, Win32 dlls, storing data, etc. The following table summarizes my findings:

Item to Reference/Use Works? Link to example Notes
.NET assembly Class Library (assembly) No N/A I was surprised by this. But it seems not to be allowed by design.
See Why Can't I Reference a .NET Assembly? below for details
.NET Core Class Library (Windows Store Apps) Yes Example A .NET Core class library uses the version of .NET designed for WinRT (which has certain 'unsafe' or invalid namespaces like System.Data removed)
Portable Class Library Yes Example Supports sharing code common to Windows Phone, Silverlight, WinRT, .NET and XBox
Windows Runtime Component Yes Example Can be used by any WinRT project (C#/VB/XAML, JS/HTML, C++)
Win32 DLL Yes Example Place the Win32 DLL in the project output directory and use [DllImport] attrib in code
External Database
(e.g. SQL Server)
No N/A Not allowed by design. The .NET System.Data namespace is removed (hidden) for WinRT apps
Embedded SQLite Yes Example See here for Tim Heuer's example on using SQLite with a WinRT app
Azure Mobile Services Yes Example Designed specifically for Windows Store apps to be able to store cloud-based data. See my recent article on the subject for more details
Local storage Yes Example Store preferences and other small data items in roaming settings
WCF and SOAP Services Yes Example Add Service Reference
HttpWebRequest for JSON Yes Example This is part of the new .NET Core framework for WinRT apps

Why Can't I Reference a .NET Assembly?

I was expecting to be able to reference a .NET assembly, provided it didn't use any of the "forbidden" APIs which were removed in the cut-down version of .NET (.NET Core) for Windows Store apps. However, this is what you get if you try to reference a .NET assembly in a Windows Store app:

When I discovered this was the case, it got me thinking as to why. Before we get to that, let's back-track just a bit to consider a broad architecture diagram for developing both Windows Store and Desktop apps in Windows 8:

Note that the above diagram is my understanding of how things are in Windows 8 - it may not be 100% correct, but I think it's close.

There seems to be (still) a lot of confusion around the relationship between WinRT, .NET/.NET Core and Win32. I researched various sources, including several articles in the Windows 8 special edition of MSDN magazine, a number of Build presentations on the subject, and the excellent podcast by Scott Hanselman and Immo Landwerth. In particular, there seems to be disagreement on whether WinRT sits directly on top of the OS's kernel services, or whether Win32 sits between WinRT and the kernel.

For the purposes of this article, I don't think it matters what's actually the truth of the situation. From my prespective, these are the key points concerning the general nature of the Windows 8 app development architecture:

  1. WinRT (short for Windows Runtime) is a native (C/C++) API based on COM

  2. WinRT APIs are defined in .winmd files that encode metadata using the ECMA 335 format, which is very similar to that of .NET assemblies. You can open WinRT .winmd files (look in C:\Windows\System32\WinMetadata) using ILDASM.exe, and Visual Studio uses the metadata to provide Intellisense, etc. The WinRT .winmd files do not contain code (this is part of the underlying Windows 8 OS), only metadata
  3. WinRT has its own type system, but Lauguage Projection enables transparent calling into WinRT APIs from supported languages using language-native types and idioms. For example, for managed app developers (C#/VB/XAML), this makes WinRT look and behave "like .NET", enabling you to pass (say) List<string>, even though WinRT actually requires its own equivalent type. Language projection works the other way around too, so that WinRT code can call into (say) managed code as though it were native WinRT code
  4. Language projection is the responsibility of each language that supports WinRT. So the .NET Core CLR implements language projection-specifics for managed code, etc.
  5. The overhead required with language projection is much less than when marshalling data and P/Invoke'ing Win32 APIs from managed code
  6. When writing Windows Store apps using managed code, developers use a mix of WinRT APIs (the namespaces all start Windows.) and the "special" version of .NET known as .NET Core

I believe (but I'm not sure!) that point 4. above is the reason why a Windows Store app can't reference a .NET assembly: only the special .NET Core CLR implements language projection, the standard CLR in the full version of the .NET framework isn't designed to support WinRT. Sounds logical, doesn't it?!

However, if you need to reuse exisiting .NET code, and you have the source to the assembly, and as long as your .NET class library doesn't use any of the "forbidden" APIs, you can change your .NET class library into a .NET Core class library by modifying (manually) the the project's .prj file and recompiling (just compare .prj files for a .NET class library and a .NET Core clss library, copy over the relevant new sections and comment-out references to .NET namespaces like System.Data, etc.)

Creating and using a .NET Core Class Library

Creating a Windows Store app class library (.NET Core library) is simple: just add a new project using the Visual Studio 2012 Class Library (Windows Store apps) template:

Then, in your Windows Store app project add a reference to the library (Project | Add Reference)

Creating and using a Portable Class Library

Unlike .NET class libraries, Portable Class Libaries (PCL) are designed to enable the sharing of code between Windows Phone, Silverlight, Windows Store, .NET and XBox (all .NET framework-based) projects. When you create a PCL you are prompted as to which environments you wish to target:

The only real restriction is that, clearly, the resulting library can only contain the APIs common to all the versions of the .NET framework for the selected environments. Adding a reference to a PCL is just the same as for .NET class libraries and Windows Store class libraries.

Creating and using a Windows Runtime Component

Windows Runtime Components (WRC) allow developers to create custom WinRT libraries that can be consumed in any language that supports WinRT. You can create WRC using either managed code (C#/VB) or C++.

Because a WRC can be consumed by any language that supports WinRT, there are a few rules that have to be observed:

  1. Private members and methods may use any .NET types
  2. Public properties and method parameters and returns must be WinRT types (or be managed code types that map automatically to WinRT types)
  3. Public classes and interfaces can't be generic, or implement non-WinRT interfaces
  4. Public classes can't be derived from WinRT types
  5. Public classes must be sealed
  6. The root namespace for a WRC must match the assembly name
  7. WinRT doesn't support polymorphism

A full list of WRC rules can be found here, and an excellent MSDN article on WRC is here.

To create a WRC, use the Windows Runtime Component template:

Let's look at a simple WRC example that demonstrates some of the issues you'll commonly run into.

Firstly, the following code won't compile because List<T> isn't a WinRT type:

using System.Collections.Generic;

namespace WinRTComponent
{
    public sealed class WinRTClass
    {
        private List<string> _fruits;

        public WinRTClass()
        {
            _fruits = new List<string> {"Apple", "Pear", "Strawberry", "Blueberry", "Plum"};
        }
        public static string SayHello(string customerName)
        {
            return "Hello " + customerName + ", from a WinRT component";
        }

        // Fails, because List<T> isn't a valid WinRT type
        public List<string> GetFruits()
        {
            return _fruits;
        }
    }
}

All uses of the .NET String type compile just fine, because there is a direct mapping to the WinRT HSTRING type.

What's interesting however, is that Visual Studio issues a really helpful error, basically telling you what you should do to get your code to compile:

Method 'WinRTComponent.WinRTClass.GetFruits()' has a parameter of type 'System.Collections.Generic.List<System.String>' in its signature. Although this generic type is not a valid Windows Runtime type, the type or its generic parameters implement interfaces that are valid Windows Runtime types. Consider changing the type 'System.Collections.Generic.List<T>' in the method signature to one of the following types instead: 'System.Collections.Generic.IList<T>, System.Collections.Generic.IReadOnlyList<T>, System.Collections.Generic.IEnumerable<T>'

So we simply have to update the return type like so:

// Compiles OK
public IEnumerable<string> GetFruits()
{
    return _fruits;
}

Another example shows that you can't use the normal .NET Task<T> when implementing an async method:

// Fails because you can't use Task<T>
public async Task<IEnumerable<string>> GetBerriesAsync()
{
    return await Task.Run(() => _fruits.Where(x => x.Contains("berry")));
}

Again, the compiler issues a genuinely helpful error message:

Method 'WinRTComponent.WinRTClass.GetBerriesAsync()' has a parameter of type 'System.Threading.Tasks.Task<System.Collections.Generic.IEnumerable<System.String>>' in its signature. Although this generic type is not a valid Windows Runtime type, the type or its generic parameters implement interfaces that are valid Windows Runtime types. Consider changing the type 'Task' in the method signature to one of the following types instead: Windows.Foundation.IAsyncAction, Windows.Foundation.IAsyncOperation, or one of the other Windows Runtime async interfaces. The standard .NET awaiter pattern also applies when consuming Windows Runtime async interfaces. Please see System.Runtime.InteropServices.WindowsRuntime.AsyncInfo for more information about converting managed task objects to Windows Runtime async interfaces.

So, all we have to do is make the async method private (so it compiles) and add an access method using the appropriate WinRT async types as follows:

// Compiles OK because it's private
private async Task<IEnumerable<string>> GetBerriesAsync()
{
    return await Task.Run(() => _fruits.Where(x => x.Contains("berry")));
}

// Add a new async access method for GetBerriesAsync() using WinRT types
public IAsyncOperation<IEnumerable<string>> GetBerriesAsyncOp()
{
    return GetBerriesAsync().AsAsyncOperation();  // AsAsyncOperation() is an extension method
}

The lesson here is that there's no real need to memorize all the rules concerning what .NET types you can/can't use in a WRC. Go ahead and code as you would normally, and let the compiler be your guide for making the necessary changes.

Using a Win32 DLL

Using a Win32 DLL from a Windows Store app is accomplished in the same way it has always been done in .NET applications. You simply place the DLL into the app's installed location and import the required methods.

If you want to create a Win32 DLL project as part of a Windows Store app, you can. Use the Visual C++ | Win32 project template in Visual Studio 2012:

Here's the C++ code for a very simple Win32 DLL:

// Win32.cpp : Defines the exported functions for the DLL application.
//

#include "stdafx.h"

extern "C" __declspec(dllexport) const char* SayHello()
{
    return "Hello from Win32";
}

The following shows how to import and call the SayHello() method in a Windows Store app:

using System;
using System.Runtime.InteropServices;

namespace RefWin8Demo
{
    public sealed partial class Home : Common.LayoutAwarePage
    {
        public string HelloMsgWin32 { get { return Win32Interface.DoSayHello(); } }
    }

    public static class Win32Interface
    {
        // Import the required method
        [DllImport(@"Win32.dll", EntryPoint = "SayHello", CallingConvention = CallingConvention.Cdecl)]
        public static extern IntPtr SayHello();  
      
        // Create a access method
        public static string DoSayHello()
        {
            // Marshall the pointer (char*) returned from the Win32 DLL into a managed code string
            return Marshal.PtrToStringAnsi(SayHello());
        }
    }
}

Using Azure Mobile Services

See my recent article on the subject for more details.

Using Local Storage

A good way to store small amounts of app data (e.g. user preferences) is to use Windows 8 cloud-based Roaming Settings. This means that app data can follow the user from device to device. As a fallback, if a network connection isn't available, local storage is used.

Data is actually stored in the \Users\{user}\AppData\Local\Packages\{packagename} directory, where packagename can be found in your app's Package.manifest file.

Here's an example of how to read from and write to roaming settings:

// Read pref from roaming settings (using: Windows.Storage)
bool showTooltips;
if (ApplicationData.Current.RoamingSettings.Values.ContainsKey("TooltipsOn"))
    showTooltips = (bool)ApplicationData.Current.RoamingSettings.Values["TooltipsOn"];

// Write to roaming settings
ApplicationData.Current.RoamingSettings.Values["TooltipsOn"] = true;

Using WCF and SOAP Services

Windows Store apps can reference WCF and SOAP-based services simply by adding a Service Reference (Project | Add Service Reference):

You then create an instance of the service's proxy object and call the required methods. Note that, in keeping with the Windows 8 practice for enforcing the use of asynchronous patterns, the proxy object for the service that's generated by Visual Studio will only include async versions of the service's web methods:

public Home()
{
    this.InitializeComponent();

    var data = GetServiceData();
}

private async Task<string> GetServiceData()
{
    // Create an instance of the WCF service's proxy object
    var wcfService = new MyServiceReference.MyServiceClient();

    // Call the service method asynchronously and return the data
    return await wcfService.SayHelloAsync();
}

Using a JSON Data Source

A common scenario is requesting remote data from a web server in JSON format. The following shows how to make a WebRequest for JSON data, and then convert that data into a collection of the required type:

// Here we have a Person class that knows how to return a collection of Person objects from a JSON source
[DataContract]
public class Person : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private string m_name;
    [DataMember]
    public string Name
    {
        get { return m_name; }
        set { m_name = value; NotifyPropertyChanged("Name"); }
    }

    private string m_address;
    [DataMember]
    public string Address
    {
        get { return m_address; }
        set { m_address = value; NotifyPropertyChanged("Address"); }
    }

    private void NotifyPropertyChanged(String info)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(info));
    }

    /// <summary>
    /// Get a collection of JSON data objects that represents a Person type
    /// Example usage:
    /// ObservableCollection<Person> data = await Person.GetDataAsync("http://myserver/mydata/jsonData.txt");
    /// </summary>
    /// <param name="uri">The URI (local or remote) of the data source</param>
    /// <returns>Returns a collection of Person objects</returns>
    public static async Task<ObservableCollection<Person>> GetDataAsync(string uri)
    {
        ObservableCollection<Person> data = null;

        // Create a WebRequest to get the data
        var request = (HttpWebRequest)WebRequest.Create(new Uri(uri));

        // Set the required HTTP headers
        request.Method = "GET";
        request.ContentType = "application/json";

        // Wait for the server to respond
        var response = (HttpWebResponse)(await request.GetResponseAsync());
        if (response.StatusCode == HttpStatusCode.OK)  // If the server responds that it found the data OK
        {
            // Here we use the DataContractJsonSerializer class to parse the JSON into the required collection. 
            // An alternative in the NewtsonSoft JSON deserializer
            var deserialize = new 
                System.Runtime.Serialization.Json.DataContractJsonSerializer(typeof(ObservableCollection<Person>));
            data = deserialize.ReadObject(response.GetResponseStream()) as ObservableCollection<Person>;
        }

        return data;
    }
}

Conclusion

In this post we looked at the various options Windows Store apps have for referencing class libraries, portable libraries, Win32 dlls, databases, services, etc.