MissingManifestResourceException when using Portable Class Libraries within WinRT

Our team recently ran into a strange issue. Our Windows Phone application targets Silverlight 8.1 but we have several WinRT 8.1 based background tasks. Recently, we refactored our codebase into several Portable Class Libraries (PCLs) in order to better share code between the application and background tasks. Since then, however, we’ve started seeing MissingManifestResourceExceptions thrown from the background tasks from the ResourceManager.GetString(String, CultureInfo) method. The odd thing was that this occurred only in the background tasks, only when running on a real phone, and only in the application’s Release configuration. The exceptions were not seen in the Silverlight application, or in the Windows Phone emulator, or when using the application’s Debug configuration.

We originally thought the issue must be due to some overlooked error in our build or packaging processes, but all of our analysis indicated that the there were no significant differences in the built assemblies nor in the packages generated for Debug and Release configurations; the issue must be in the runtime. After a lot of digging around, we finally found an active Microsoft Connect report for the issue:

https://connect.microsoft.com/VisualStudio/feedback/details/991028/issue-using-resx-files-on-winrt-apps-windows-phone-and-windows

That others were running into the same problem, under the same circumstances, seemed to confirm our conclusion. However, we still needed a workaround. Our options were (1) to avoid using PCLs (that use their own resources) from our background task or (2) redirect use of the ResourceManager within the PCL to the WinRT ResourceLoader.

Option #1 would mean undoing much of our refactoring work and obviously was a non-starter.

Option #2 works on the assumption that, in a WinRT library, all of the resources of the referenced PCLs are extracted and placed in the package’s resources.pri file in addition to being embedded within the PCL assembly itself. That means you can get to the duplicate resources using the WinRT ResourceLoader even if you cannot using the .NET ResourceManager. Because we need to dynamically switch between using the ResourceManager and ResourceLoader when in Silverlight and WinRT, respectively, we need some sort of dependency injection (DI) scheme.

In a typical .NET library, resources are added to a Resources file (.resx). The file is then used to embed the resources into the built assembly. The file is also used to generate a class that exposes each resource as a strongly-typed property, making it easy to consume resources within the library. This generated class uses a ResourceManager instance to retrieve the resource mapped to each property. Redirection means either (1) not using the generated class at all and using DI scheme directly, (2) alter the generation of the class to use the DI scheme, or (3) try to alter the behavior of the class to conform to the DI scheme.

Option #1 was least desirable as it meant a lot of churn within the PCLs. Option #2 was not particularly desirable either as it was a significant amount of work to write and test a generator that matched the built-in one except for this one minor change. The question was how to implement option #3.

Looking at the generated class for a Resources file, each contains a static ResourceManager field named resourceMan. This field (if null) is set on the first retrieval of a resource via one of the generated properties. The ResourceManager.GetString(String, CultureInfo) also happens to be a virtual method, which means we cancreate a derived ResourceManager that retrieves resources from, say, a ResourceLoader. The trick then, is to initialize that static ResourceManager field to an instance of our derived ResourceManager. That turns out to be a simple act of reflection as shown in the following sample:

public class WindowsRuntimeResourceManager : ResourceManager
{
    private readonly ResourceLoader resourceLoader;

    private WindowsRuntimeResourceManager(string baseName, Assembly assembly) : base(baseName, assembly)
    {
        this.resourceLoader = ResourceLoader.GetForViewIndependentUse(baseName);
    }

    public static void InjectIntoResxGeneratedApplicationResourcesClass(Type resxGeneratedApplicationResourcesClass)
    {
        resxGeneratedApplicationResourcesClass
            .GetRuntimeFields()
            .First(m => m.Name == "resourceMan")
            .SetValue(null, new WindowsRuntimeResourceManager(resxGeneratedApplicationResourcesClass.FullName, resxGeneratedApplicationResourcesClass.GetTypeInfo().Assembly));
    }

    public override string GetString(string name, CultureInfo culture)
    {
        return this.resourceLoader.GetString(name);
    }
}

In the WinRT component, we then call the static injection method for each generated Resources class within the PCL:

WindowsRuntimeResourceManager.InjectIntoResxGeneratedApplicationResourcesClass(typeof(PortableLibrary.Resources.AppResources));

With that, our WinRT background tasks and referenced PCLs were able to access all of their resources without issue and we did not have to significantly refactor any code.

Caveats:

  • Using reflection to set private fields for types you do not own completely (e.g. are generated) is highly fragile and could break with the next version of the Resources class generator. Do this only if you have no other choice and ensure you have checks in place to detect such breaks.

  • In our case we owned all of the PCLs referenced by our WinRT component. I cannot say whether this workaround will work for all (e.g. third-party) PCLs, and may not work for PCLs which have internal resource types that cannot be as easily injected.

This post was migrated from my MSDN blog, Visual Studio Tools and Anything Else I Can Think Of, and written as a Microsoft employee.

2014

Back to Top ↑

2012

Back to Top ↑

2008

Finding DASL Property Names

1 minute read

The LINQ-to-DASL provider of the Office Interop API Extensions provides a very limited set of mappings between its query types and their associated DASL prop...

Debugging LINQ-to-DASL Queries

3 minute read

When your LINQ-to-DASL queries do not return the results you expect, how do you determine where the problem is?  The issue could be that the query simpl...

LINQ to DASL Walkthrough

3 minute read

Now that the Office Interop API Extensions have been released, I thought I would post a complete walkthrough of a simple LINQ to DASL application. Let's star...

Office Interop API Extensions Now Available!

less than 1 minute read

As announced in Andrew Whitechapel’s post, version 1.0 of the VSTO Power Tools have been released! One of those tools is the Office Interop API Extensions, a...

Using LINQ with the Office Object Model

3 minute read

In my last post I talked about LINQ to DASL, a LINQ provider that converts query expressions into their DASL equivalent in order to efficiently filter item c...

Back to Top ↑

2007

VSTO at Portland Code Camp v3.0

less than 1 minute read

Yesterday I gave a presentation on VSTO at the third annual Portland Code Camp. I demonstrated an Outlook 2007 add-in that used Outlook Form Regions, WCF, an...

Back to Top ↑