HoloLens, Unity

IL2CPP + HoloLens

Following the Unity announcement about deprecating the .NET backend I have been slowly turning my attention towards using IL2CPP instead which some time in the future will be the only option for Unity HoloLens development. Of course, there are supported LTS versions but I guess it will often be the case that as frameworks and SDKs move forwards they would tend towards supporting newer features and functionality. Either way, as a HoloLens dev it wouldn’t help to at least be prepared.

Just to give a very high-level description of what this means; using the .NET backend generates a .NET UWP project when building my Unity project for HoloLens. This means debugging C# code in Visual Studio and deploying a .NET (or .NET native) app to a HoloLens device. When I build an IL2CPP project in Unity it creates a native C++ Visual Studio project which is generated from the C# that you write in your Unity scripts. So effectively converts .NET code into native C++.

There is a managed debugger so you can continue to work with C# in a debugging experience. In the Unity build settings if you check ‘Wait for managed Debugger’ then

managed debugger

when you run the resulting app on the HoloLens it will put up a dialog which will wait giving you a chance to hook up the managed debugger.

20190117_144756_HoloLens

I usually open two instances of Visual Studio; one with the native code and from the other choose the menu option Debug > Attach Unity Debugger and use that to debug C# code

Attach Unity Debugger

I can then set breakpoints in my C# scripts as expected. Over the last few Unity versions I have been using this experience has been steadily improving. It seemed initially to be slow and sometimes the debugger wouldn’t catch my first-chance exceptions. This works well in 2018.3.0f2 though which I am currently using.

MSAL Sample

I was working with a sample that I had previously written using the Microsoft Auth Library which was originally used as an example of delegated auth on HoloLens but I recently extended to also show ‘device code flow’ which allows the auth to happen on a second device which may be more convenient if typing passwords or codes is required.

In order to use the MSAL library I downloaded the Nuget package directly from the Nuget website and then chose the relevant dll to include directly into my Unity project. The MSAL library is a .NET library so you may be wondering how this works with IL2CPP. So, the .NET assembly will get converted into C++ which is included in the resulting project.

The device code auth flow works ok in the Unity editor since it doesn’t have the complication of requiring a browser to be present in the app. It also worked using the .NET backend but when I switched over to IL2CPP things stopped working and I was hit with a Exception in the managed debugger.

Error on deserializing read-only members in the class: No set method for property ‘ErrorDesription’ in type ‘Microsoft.Identity.Core.Oauth2.Oauth2ResponseBase’

Decompiling the original assembly revealed that there was a setter for that property so where’d it go? As it turns out, the IL2CPP process will strip out any code that it detects to be unused, i.e. not referenced elsewhere. This results in less code, faster build times, etc. Detecting unused code that may actually be used by reflection is tricky though and this code can get stripped which is exactly why I got the exception above. No worries though, since it’s not a huge leap to diagnose and even easier to fix. The Unity forum staff pointed me at the link.xml file, which if you create one in your Assets folder can enable you to take some control over which code gets stripped. Adding the following xml prevents all code stripping from the auth library and fixes my first issue:

Now, my sample still didn’t work as this time I was getting a NullReferenceException in the managed debugger. This didn’t reveal any clues as to what the problem was so I was forced to turn towards the native debugger and the generated C++.  There are some great tips here on navigating the generated code, catching exceptions and viewing strings, etc. So, I spent a bit of time stepping through the call stack and I wouldn’t recommend it as an experience as it is fairly verbose and easy to get lost. I then turned on first chance exceptions and discovered where the exception was being thrown.

exception1 and stepping a few frames back up the call stack reveals the following:

exception2

Now, this is C++ code generated from System.Runtime.Serialization and more specifically this function System.Runtime.Serialization.Json.JsonFormatWriterInterpreter::TryWritePrimitive and it is using reflection itself to find a method that will be used in the implementation. Of course, that method has been stripped out and so this code fails. Adding an entry to the link.xml file for System.Runtime.Serialization fixes this but leads me to think that as a consumer of this code I shouldn’t really be concerned with it’s internals in this way.

And finally I have a working sample which you can find here.

Tagged

4 thoughts on “IL2CPP + HoloLens

  1. What are the build times you are getting with IL2CPP (using the Unity Editor to build the UWP Visual Studio project, then building the Visual Studio project to then deploy on the HoloLens)? I have been encountering insanely long build times (~10-15 minutes total), which is making IL2CPP very difficult for me to use during development compared with .NET. Wondering if there are things that can be done to make this less painful.

    1. Hi Dan,

      Yep, have been suffering with long build times for a clean project. Not so bad for incremental builds and I haven’t explored other options for speeding this up yet beyond the standard tips from Unity https://docs.unity3d.com/Manual/IL2CPP-OptimizingBuildTimes.html I think we’ll see improvements in this scenario for things like pre-built binaries as I guess Unity will implement caching for those somehow. Currently libraries that would never change need to be rebuilt every time a clean build is needed. I’ll post back if I find alternative ways to speed up the builds.

      Pete D

  2. Hi Pete,

    could you describe permissions and other settings that are required to set up in Azure Active Directory – App registration (Preview)? I’ve created one with redirect URI set to urn:ietf:wg:oauth:2.0:oob, enabled Access tokens, ID tokens and gave Files Read All / Sites Read All / User Read API permissions in order to access OneDrive with Microsoft Graph but I failed to retrieve the list of all the items in my drive (https://graph.microsoft.com/v1.0/me/drive/root/children). At least the persmissions that you’ve set on your project regarding last 5 messages – e90a5e05-a177-468a-9f6e-eee32b946f86 as I can’t register an authorizing app on my Azure Portal.

    1. Firstly, if you are using ‘device code flow’ this is only supported with a work account currently – so using a personal MSA won’t work right now. Can you retrieve the files if you use a delegated auth flow? When I look at my app registration (preview) I only have set User.Read (not sure why I don’t need Mail.Read there) for Microsoft Graph. Let me know if you want to check other settings?

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.