UWP and the .NET Native tool chain compiler option

Compiler configuration

If you’re developing Windows Store Apps using UWP, you’ll be familiar with the new Compile with .NET Native tool chain build option, that is recommended for release builds (and, in fact, not only recommended but obligatory when building apps for the public App Store, as it will not accept app packages built with the “old” compiler option). This .NET Native tool chain is advertised as making apps run faster in production environments – while I can neither support nor disprove this, what I can confirm from my everyday work experience is that is makes builds a lot slower. This might be the reason why the Compile with .NET Native tool chain build option is activated for release builds of UWP projects, but deactivated for debug builds per default – this is a good idea in principle, because the long build time of the Native tool chain would be impossible to use for frequent test runs during development.

However, there is a serious downside of this separation: The old compiler and the .NET Native tool chain seem to produce slightly different IL code in certain scenarios, which every now and then leads to bug tickets stating that an app behaves not as specified which are not reproducable in debug mode! My previous experience with such discrepancy between debug and release behavior was limited to 3rd party components, but a few weeks ago I was able to witness a similar problem in the core UWP framework:

Boon and bane of regression testing

As part of the usual regression tests for a new minor app release, our testing department reported a bug within the PDF viewer component of a large UWP app: This component includes a button that allows the user to share the currently displayed PDF document with other applications (e.g. for sending it as e-mail attachment), implementing a simple UWP share contract.

According to the bug report, this share button did not work properly any more, as it did not invoke the chosen 3rd party app but failed with an exception of type ComTypeMarshalling_MissingInteropData. The strange thing about this bug was that we had not changed anything in the business logic of the PDF component (certainly not the share contract implementation), and that I could not reproduce it on my development machine during debugging the concerned section.

Trial and error

Only after confirming that the bug had really arised within this release version and never occurred before, and checking the detailed change logs of the release, I noticed that we had updated a bunch of NuGet dependencies, among them the Microsoft.NETCore.UniversalWindowsPlatform package provided by Microsoft as part of the .NET core platform. In some tedious trial-and-error sessions I could isolate the problem’s cause to this package, so I decided to invest some more time and check each version in detail. Long story short, in the end it turned out that everything worked fine in older versions up to 5.2, only when switching to 5.3 (or newer versions) of Microsoft.NETCore.UniversalWindowsPlatform the share contract failed.

Obviously, downgrading the dependency in question was not an option for the release, so I had to dig deeper into the code declaring the share contract:

var dataTransferManager = DataTransferManager.GetForCurrentView();
dataTransferManager.DataRequested += async (sender, args) => {
	var file = await StorageFile.GetFileFromApplicationUriAsync(...);
	DataPackage dataPackage = args.Request.Data;
	dataPackage.Properties.Title = "Title";
	dataPackage.Properties.Description = "Description";
	dataPackage.SetStorageItems(new [] { file });
};
private void ShareButton_OnTapped(object sender, TappedRoutedEventArgs e)
{
	DataTransferManager.ShowShareUI();
}

Now, this looks quite straightforward and can not be implemented in a lot of different ways (actually, this code snippet is quite similar to Microsoft’s official tutorial for implementing a share contract). However, let’s focus on the last line of the DataRequested event handler method (as highlighted in the code snippet above): The DataPackate.SetStorageItems(...) method expects a collection of items that implement the Windows.Storage.IStorageItem interface as parameter – the file object we’re passing is a Windows.Storage.StorageFile, which (according to the documentation) indeed implements IStorageItem. However, as of version 5.3 the implicit cast seems to fail (don’t ask me for the reason – after having gone so far, I had no intention of digging even deeper into the specifics of the .NET Native tool chain compiler). Replacing the respective code line with an explicit cast resolved the issue:

DataPackage dataPackage = args.Request.Data;
dataPackage.Properties.Title = "Title";
dataPackage.Properties.Description = "Description";
IEnumerable<IStorageItem> files = new IStorageItem[] {file};
args.Request.Data.SetStorageItems(files);

Lessons learned

If you ever face failing share contracts or encounter ComTypeMarshalling_MissingInteropData exceptions, it’s good to know that explicit type declarations might solve the problem. However, for me it was a lot more valueable to learn that it might be a good idea to test my projects even in release mode every now and then. In addition, another outcome of this case was a prove of the importance of regression testing!