Search Unity

IL2CPP Compability

Discussion in 'Scripting' started by HakanOzcan, Sep 26, 2019.

  1. HakanOzcan

    HakanOzcan

    Joined:
    May 14, 2013
    Posts:
    41
    Hey to all,
    My game uses a interlayer plugin(dll files in Plugins folder, coded with .NET3.5) which we use to communicate with our backend. Everythings works pretty much well in Editor. However, when I build it with IL2CPP(SRV: .NET 4.x equivalent, ACL: .NET Standart 2.0), I get NullReferenceException. I started to think that our interlayer plugin is not compatible with IL2CPP. Then, I go through the code try to detect some classes which I suspect(by taking into consideration Scripting Restrictions). Can someone(@JoshPeterson) help me who knows IL2CPP, AOT, etc concepts. Sorry for including that much scripts, but I think that this thread may also help most plugin developers.

    Here is the error:
    Code (CSharp):
    1. 09-26 20:52:17.049  4036  4115 E Unity   : NullReferenceException: Object reference not set to an instance of an object.09-26 20:52:17.049  4036  4115 E Unity   :   at GameManager+<CheckServerStatus>d__14.MoveNext () [0x00000] in <00000000000000000000000000000000>:0
    2. 09-26 20:52:17.049  4036  4115 E Unity   :   at UnityEngine.SetupCoroutine.InvokeMoveNext (System.Collections.IEnumerator enumerator, System.IntPtr returnValueAddress) [0x00000] in <00000000000000000000000000000000>:0
    3. 09-26 20:52:17.049  4036  4115 E Unity   :   at GameManager+<InitializeGame>d__9.MoveNext () [0x00000] in <00000000000000000000000000000000>:0
    4. 09-26 20:52:17.049  4036  4115 E Unity   :   at UnityEngine.SetupCoroutine.InvokeMoveNext (System.Collections.IEnumerator enumerator, System.IntPtr returnValueAddress) [0x00000] in <00000000000000000000000000000000>:0
    5. 09-26 20:52:17.049  4036  4115 E Unity   :   at GameManager.Start () [0x00000] in <00000000000000000000000000000000>:0
    6. 09-26 20:52:17.049  4036  4115 E Unity   : <InitializeGame>d__9:MoveNext()
    7. 09-26 20:52:17.049  4036  4115 E Unity   : UnityEngine.SetupCoroutine:InvokeMoveNext(IEnumerator, IntPtr)
    8. 09-26 20:52:17.049  4036  4115 E Unity   : GameManager:Start()
    9. 09-26 20:52:17.049  4036  4115 E Unity   :
    10. 09-26 20:52:17.049  4036  4115 E Unity   : (Filename: currently not available on il2cpp Line: -1)
    Here is the ChechServerStatus() function of GameManager where i get the error exactly at Container.Resolve<IUserManager>()
    Code (CSharp):
    1. public IEnumerator CheckServerStatus()
    2.     {
    3.         // I get NullReferenceException at Resolve function
    4.         Container.Resolve<IUserManager>().CheckServerStatus((ServerStatusResultModel model) =>
    5.         {
    6.              // Some code here
    7.         }  
    8.     }
    Here is the Container script
    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using System.Reflection;
    5. using System.Text;
    6.  
    7. namespace RedApple.GameFramework.contanier
    8. {
    9.  
    10.     /// <summary>
    11.     /// ref:https://www.codeproject.com/Articles/1075189/Lets-write-a-Tiny-IoC-Container-to-learn-and-for-f?msg=5194025
    12.     /// </summary>
    13.     public class Container : IContainer
    14.     {
    15.         Dictionary<Type, RegistrationModel> instanceRegistry = new Dictionary<Type, RegistrationModel>();
    16.  
    17.         public void RegisterInstanceType<I, C>()
    18.             where I : class
    19.             where C : class
    20.         {
    21.             RegisterType<I, C>(REG_TYPE.INSTANCE);
    22.         }
    23.  
    24.         public void RegisterSingletonType<I, C>()
    25.             where I : class
    26.             where C : class
    27.         {
    28.             RegisterType<I, C>(REG_TYPE.SINGLETON);
    29.         }
    30.  
    31.         private void RegisterType<I, C>(REG_TYPE type)
    32.         {
    33.             if (instanceRegistry.ContainsKey(typeof(I)) == true)
    34.             {
    35.                 instanceRegistry.Remove(typeof(I));
    36.             }
    37.  
    38.             instanceRegistry.Add(
    39.                 typeof(I),
    40.                     new RegistrationModel
    41.                     {
    42.                         RegType = type,
    43.                         ObjectType = typeof(C)
    44.                     }
    45.                 );
    46.         }
    47.  
    48.         public I Resolve<I>()
    49.         {
    50.             return (I)Resolve(typeof(I));
    51.         }
    52.  
    53.         private object Resolve(Type t)
    54.         {
    55.             object obj = null;
    56.  
    57.             if (instanceRegistry.ContainsKey(t) == true)
    58.             {
    59.                 RegistrationModel model = instanceRegistry[t];
    60.  
    61.                 if (model != null)
    62.                 {
    63.                     Type typeToCreate = model.ObjectType;
    64.                     ConstructorInfo[] consInfo = typeToCreate.GetConstructors();
    65.  
    66.                     var dependentCtor = consInfo.FirstOrDefault(item => item.GetCustomAttributes(true).FirstOrDefault(att => att.GetType() == typeof(TinyDependencyAttribute)) != null);
    67.  
    68.                     if (dependentCtor == null)
    69.                     {
    70.                         // use the default constructor to create
    71.                         obj = CreateInstance(model);
    72.                     }
    73.                     else
    74.                     {
    75.                         // We found a constructor with dependency attribute
    76.                         ParameterInfo[] parameters = dependentCtor.GetParameters();
    77.  
    78.                         if (parameters.Count() == 0)
    79.                         {
    80.                             // Futile dependency attribute, use the default constructor only
    81.                             obj = CreateInstance(model);
    82.                         }
    83.                         else
    84.                         {
    85.                             // valid dependency attribute, lets create the dependencies first and pass them in constructor
    86.                             List<object> arguments = new List<object>();
    87.  
    88.                             foreach (var param in parameters)
    89.                             {
    90.                                 Type type = param.ParameterType;
    91.                                 arguments.Add(this.Resolve(type));
    92.                             }
    93.  
    94.                             obj = CreateInstance(model, arguments.ToArray());
    95.                         }
    96.                     }
    97.                 }
    98.             }
    99.  
    100.             return obj;
    101.         }
    102.  
    103.         private object CreateInstance(RegistrationModel model, object[] arguments = null)
    104.         {
    105.             object returnedObj = null;
    106.             Type typeToCreate = model.ObjectType;
    107.  
    108.             if (model.RegType == REG_TYPE.INSTANCE)
    109.             {
    110.                 returnedObj = InstanceCreationService.GetInstance().GetNewObject(typeToCreate, arguments);
    111.             }
    112.             else if (model.RegType == REG_TYPE.SINGLETON)
    113.             {
    114.                 returnedObj = SingletonCreationService.GetInstance().GetSingleton(typeToCreate, arguments);
    115.             }
    116.  
    117.             return returnedObj;
    118.         }
    119.     }
    120. }
     
    Last edited: Sep 27, 2019
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,745
    Is it mis-binding because "Container" is misspelled as "Contanier" in some places? I'm not sure what the cross-binding mechanism uses to find entrypoints.
     
  3. HakanOzcan

    HakanOzcan

    Joined:
    May 14, 2013
    Posts:
    41
    it is totaly because of my friends perfect English:) But anyway it is working in editor.
     
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,745
    I'll be honest, I didn't study the code all that closely... but one thing that comes to mind is if you are expecting anything "object-y" to go through that call boundary, because AFAIK it will not.

    If you're passing and/or returning primitives such as int, string, bool, float, that will work fine. (Strings have to be marshaled through from a System.IntPtr however).

    But pretty sure if you're slanging around your own class instances it won't work across the interop boundary with IL2CPP.
     
  5. jilleJr

    jilleJr

    Joined:
    Jan 21, 2015
    Posts:
    63
    What is your building target? Android? WebGL? Windows?
     
  6. HakanOzcan

    HakanOzcan

    Joined:
    May 14, 2013
    Posts:
    41
    Android(Testing on Bluestacks Emulator)
     
  7. doctorpangloss

    doctorpangloss

    Joined:
    Feb 20, 2013
    Posts:
    270
    You can't easily use dependency injection with IL2CPP.

    In a MonoBehaviour's Start, try instantiating your concrete implementation once.

    But better to just not use dependency injection.
     
  8. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,938
    I suspect that the problem is with managed code stripping. Some code in the interlayer plugin likely uses reflection, so the managed code stripper that runs as part of the IL2CPP build process is probably removing IL code that is required.

    You can prevent this from happening by adding a link.xml file to the project. See the documentation about this feature here: https://docs.unity3d.com/Manual/IL2CPP-BytecodeStripping.html