Custom Content Pipeline/serialization issues in Windows 10

I have a MonoGame solution with a Windows 10 project. I am using my own custom content libraries. Everything works fine until I try to build with the .NET Native Toolchain to release on the Windows Store. Once that setting is set I get all sorts of errors depending on what type of content I try to use. When I load an array of MapLevelMetaData I get an exception:

Exception thrown: ‘System.Reflection.MissingMetadataException’ in System.Private.CoreLib.dll
Additional information: ‘Microsoft.Xna.Framework.Content.ReflectiveReader<TileEngine.KioskLocation>’ is missing metadata. For more information, please visit http://go.microsoft.com/fwlink/?LinkID=392859

MapLevelMetaData has a property KioskLocation which is a custom struct. If I change the type from struct to class it works, but then I get an exception on the KioskLocation because it has a KioskType property that is an enum.

Exception thrown: ‘System.Reflection.MissingMetadataException’ in System.Private.CoreLib.dll
Additional information: ‘Microsoft.Xna.Framework.Content.EnumReader<TileEngine.KioskType>’ is missing metadata. For more information, please visit http://go.microsoft.com/fwlink/?LinkID=392859

OK, so it seems like it hates custom value types for some reason. If I change that (and a ton of other enums) to int, it eventually works, but then I get an ArgumentOutOfRange exception deep in some internals somewhere.
If I load up another custom class, Character.cs, before MapLevelMetaData I don’t get an exception but it loads up with every property set to the default value, null or 0 or whathaveyou.

Weirdly, I have a custom image processor that works fine.

In all cases, no matter what I add to default.rd.xml it doesn’t seem to have any affect. It doesn’t even seem to mind if there’s junk in there. I’m not sure how to tell if the compiler is even seeing that file, let alone using it in any meaningful way.

It is most likely that the missing code is being stripped because the linker can’t see any calls to it. This is because the content loading is done via reflection. We get around this on some platforms by forcing a reference to the type, therefore the linker won’t strip the code. Have a look in https://github.com/mono/MonoGame/blob/develop/MonoGame.Framework/Content/ContentTypeReaderManager.cs for if (false flag).

Thanks @KonajuGames, it did seem to be a version of that. Unfortunately I can’t instantiate EnumReader or ReflectionReader since they are protected. But that did lead me down a path where I finally found the right values for the default.rd.xml file. Here’s the entire contents (for now):

<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">

  <Application>
    <Assembly Dynamic="Required All" Name="*Application*" />
  </Application>

  <Library Name="MonoGame.Framework">
    <Namespace Name="Microsoft.Xna.Framework.Content">
      <TypeInstantiation Name="ReflectiveReader" Arguments="TileEngine.KioskLocation" Dynamic="Required All"></TypeInstantiation>
      <TypeInstantiation Name="EnumReader" Arguments="TileEngine.KioskType" Dynamic="Required All"></TypeInstantiation>
      <TypeInstantiation Name="EnumReader" Arguments="TileEngine.DoorDirection" Dynamic="Required All"></TypeInstantiation>
      <TypeInstantiation Name="EnumReader" Arguments="TileEngine.MapEnvironment" Dynamic="Required All"></TypeInstantiation>
    </Namespace>
  </Library>
</Directives>

So that seemed to solve the immediate problem, but now I'm getting a new exception on the same Content.Load call:

An exception of type ‘System.InvalidCastException’ occurred in MonoGame.Framework.dll but was not handled in user code
Additional information: Specified cast is not valid.
If there is a handler for this exception, the program may be safely continued.

With no info on where it’s coming from. Awesome.

Also, I seem to be able to do reflection on a type that I’m not using without anything special in the default.rd.xml file. For example this code works fine:

        var testType = Type.GetType("TileEngine.TempNotUsed, TileEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
        var testInstance = Activator.CreateInstance(testType);

Nothing really seems to make sense to me and I am questioning the concept of life itself. I wonder if my setup is borked in some weird way.

Like any good problem there are multiple things going on here.

The rd.xml thing was throwing me off. Most MonoGame samples I saw had nothing regarding MonoGame.Framework in the rd.xml file, so I was confused why I needed anything there. Turns out you only need the ReflectiveReader in rd.xml if your argument is a value type (in this case, a struct). Same for Enum reader (of course enums are always value types). I’m not sure why that distinction exists, but that seems to be the rule.

I found my answer for the 2nd issue (ArgumentOutOfRangeException/objects loading up with all default/null values) after stepping through the MonoGame source. It seems there’s an issue in the content pipeline around some code that is trying to determine if a property is hiding a property in the base class. It only fails for Windows 10 Universal apps built against the .NET Native Runtime. Since .NET Native is a requirement for the Windows Store, it seems like this should have come up before. Am I the first person doing this?

Anyway I was able to comment out the offending code (who uses property hiding anyway?) and build and run my game consistently. I will submit an issue about that to the GitHub repo.