记录IL2Cpp的编译过程

in 编程
关注公众号【好便宜】( ID:haopianyi222 ),领红包啦~
阿里云,国内最大的云服务商,注册就送数千元优惠券:https://t.cn/AiQe5A0g
腾讯云,良心云,价格优惠: https://t.cn/AieHwwKl
搬瓦工,CN2 GIA 优质线路,搭梯子、海外建站推荐: https://t.cn/AieHwfX9

编者注

由于Windows 10重启,丢失部分文档,下面内容能补则补吧

需求

由于需要发布并简单处理,则使用IL2Cpp进行,编译过程和运行过程碰到很多问题,特此记录

编译过程

找不到类内部的枚举

部分代码如下

namespace Business.UI
{
    public class GUIManager : SingletonTemplate<GUIManager>
    {

        public enum GUIDisplayType
        {
            None,
            Normal,
            Free,
        }

修正为如下内容,并修改其他程序的命名空间

namespace Business.UI.Entity
{
    public enum GUIDisplayType
    {
        None,
        Normal,
        Free,
    }
}

UnityEditor引用

请看如下todo部分,AssetDatabase是UnityEditor空间下,无法在UnityEngine使用

        public GameObject Create(Vector2Int screenSize, int targetDisplay, GameObject gameObject)
        {
            if (gameObject == null)
            {
// todo: 在UnityEngine空间下使用UnityEditor的类库
                gameObject
                    = AssetDatabase.LoadAssetAtPath<GameObject>(AssetPath);
                gameObject = GameObject.Instantiate(gameObject);
                gameObject.SetHidenFlagEx(HideFlags.HideAndDontSave, true, true);
           }

修复如下

// todo: 在UnityEngine空间下使用UnityEditor的类库
#if UNITY_EDITOR
        gameObject = AssetDatabase.LoadAssetAtPath<GameObject>(AssetPath);
#endif

Mina.Net的Common.Log引用

由于我们期望默认环境下使用.net standard 2.0。则在编译IL2Cpp的环境下发现我们并不需要的类库Common.Log
解决:
fork mina的源代码,移除Common.Log的所有引用,删除不需要的代码,并重新编译dll

详细请看:https://gitee.com/haishenshizi/Mina.NET

log4net的System.Configruation引用

由于项目进度,延缓处理该问题。修改项目的运行时为.Net 4.x跳过该问题
备注:NLog实现了自定义的Comfigruation,有时间则考虑使用NLog代替log4net

SharpDX

IL2Cpp未实现该方法

IL2CPP error for method 'System.Void SharpDX.Utilities::Read(System.IntPtr,T&)' in assembly 'C:\Workspace\Unity\aicfve-previz\PrevizUnityEditor\Temp\StagingArea\Data\Managed\SharpDX.dll'
Additional information: Build a development build for more information. 未实现该方法或操作。

使用ILSpy查找SharpDX的关键代码

	public unsafe static T Read<T>(IntPtr source) where T : struct
	{
		return *(T*)(void*)source;
	}

    // 问题发生在这里!
	public unsafe static void Read<T>(IntPtr source, ref T data) where T : struct
	{
		data = *(T*)(void*)source;
	}

该问题说明某些情况下IL2Cpp没有实现。理论上应当移除该方法。但是通过rider反编译,并查看引用情况

Usages Of 'Read<T>(...)'

Get<T>(int):T (in SharpDX.DataBuffer)
GetDataAs<T>():T (in SharpDX.Multimedia.RiffChunk)
GetCurrentState(ref T):void (in SharpDX.DirectInput.CustomDevice)

SharpDX.DirectInput使用了该方法

public unsafe void GetCurrentState(ref T data)
    {
      int num = Utilities.SizeOf<TRaw>();
      byte* numPtr = stackalloc byte[num * 2];
      TRaw data1 = default (TRaw);
      this.GetDeviceState(num, (IntPtr) ((void*) numPtr));
      // 在这里!
      Utilities.Read<TRaw>((IntPtr) ((void*) numPtr), ref data1);
      data.MarshalFrom(ref data1);
    }

设定方案:移除SharpDX当中该方法,在SharpDX.DirectInput当中,移除泛型调用与虚函数,直接实现各个调用类
只能安装完整版本的VS2017,第一次还自动给我升级成了商业版本,害的我重新删除安装VS2017社区版。最终Rider能够正确识别到SharpDX项目。

构建过程简单描述

由于SharpDX是一个很早的项目,项目自己编写构建过程,并没有托管给VS。导致必须使用VS2017进行编译,但是由于项目组自行定义构建范围,则必须变更csproj的构建范围。这里我通过Rider进行变更,并在Rider当中进行编译,才能顺利通过。
https://gitee.com/haishenshizi/SharpDX

Acquire

这个问题,首先怀疑由于添加了link.xml导致所有的内容都发生了展开。有些类型展开发成错误

IL2CPP error for method 'System.Void SharpDX.DirectInput.Device::Acquire()' in assembly 'C:\Workspace\Unity\aicfve-previz\PrevizUnityEditor\Temp\StagingArea\Data\Managed\OptiTrackInterface.dll'
Additional information: Build a development build for more information. 无法将类型为“Mono.Cecil.CallSite”的对象强制转换为类型“Mono.Cecil.MethodReference”。
il2cpp.exe didn't catch exception: System.InvalidCastException: 无法将类型为“Mono.Cecil.CallSite”的对象强制转换为类型“Mono.Cecil.MethodReference”。
   在 Unity.IL2CPP.StackAnalysis.StackStateBuilder.Build(IEnumerable`1 instructions)
   在 Unity.IL2CPP.StackAnalysis.StackStateBuilder.StackStateFor(IEnumerable`1 instructions, StackState initialState, MethodDefinition methodDefinition, TypeResolver typeResolver)
   在 Unity.IL2CPP.StackAnalysis.StackAnalysis.Analyze()
   在 Unity.IL2CPP.StackAnalysis.StackAnalysis.Analyze(MethodDefinition methodDefinition, ControlFlowGraph cfg, TypeResolver typeResolver)
   在 Unity.IL2CPP.MethodBodyWriter..ctor(IGeneratedMethodCodeWriter writer, MethodReference methodReference, TypeResolver typeResolver, IRuntimeMetadataAccess metadataAccess, VTableBuilder vTableBuilder, MethodBodyWriterDebugOptions options, ISourceAnnotationWriter sourceAnnotationWriter)
   在 Unity.IL2CPP.MethodWriter.WriteMethodBody(MethodReference method, IGeneratedMethodCodeWriter methodBodyWriter, IRuntimeMetadataAccess metadataAccess, ISourceAnnotationWriter sourceAnnotationWriter, IIcallMappingService icallMapping, VTableBuilder vtableBuilder, Action saveSequencePoints)
   在 Unity.IL2CPP.MethodWriter.<WriteMethodDefinition>c__AnonStorey0.<>m__0(IGeneratedMethodCodeWriter bodyWriter, IRuntimeMetadataAccess metadataAccess)
   在 Unity.IL2CPP.CodeWriterExtensions.WriteMethodWithMetadataInitialization(IGeneratedMethodCodeWriter writer, String methodSignature, String methodFullName, Action`2 writeMethodBody, String uniqueIdentifier, MethodReference methodRef)
   在 Unity.IL2CPP.MethodWriter.WriteMethodDefinition(IGeneratedMethodCodeWriter writer, MethodReference method, IMethodCollector methodCollector, IMethodVerifier methodVerifier, ISourceAnnotationWriter sourceAnnotationWriter, IIcallMappingService icallMapping, VTableBuilder vtableBuilder)
   在 Unity.IL2CPP.SourceWriter.<WriteMethodSourceFiles>c__AnonStorey3.<>m__0(IGeneratedMethodCodeWriter writer, TypeReference type)
   在 Unity.IL2CPP.SourceWriter.WriteEqualSizedChunks[T](NPath outputDir, IEnumerable`1 items, String fileName, Int64 chunkSize, Action`2 writeItemAction, SourceWritingContext sourceWritingContext)
   在 Unity.IL2CPP.SourceWriter.WriteMethodSourceFiles(NPath outputDirectory, SourceWritingContext sourceWritingContext, String fileName, IEnumerable`1 typeList, IMethodCollector methodCollector, Boolean writeMarshalingDefinitions)
   在 Unity.IL2CPP.SourceWriter.Write(NPath outputDir, SourceWritingContext sourceWritingContext, ReadOnlyCollection`1 assemblyDefinitions, IMethodCollector methodCollector, IInteropDataCollector interopDataCollector)
   在 Unity.IL2CPP.AssemblyConverter.Apply()
   在 Unity.IL2CPP.AssemblyConverter.ConvertAssemblies(IEnumerable`1 assemblyDirectories, IEnumerable`1 explicitAssemblies, NPath outputDir, NPath dataFolder, NPath symbolsFolder, NPath executableAssembiesFolder, NPath monoLibFolder, NPath monoEtcFolder, NPath[] searchDirectories, String entryAssemblyName, NPath[] extraTypesFiles)
   在 il2cpp.Program.DoRun(String[] args)
   在 il2cpp.Program.Run(String[] args)
   在 il2cpp.Program.Main(String[] args)

未经处理的异常:  System.InvalidCastException: 无法将类型为“Mono.Cecil.CallSite”的对象强制转换为类型“Mono.Cecil.MethodReference”。
   在 Unity.IL2CPP.StackAnalysis.StackStateBuilder.Build(IEnumerable`1 instructions)
   在 Unity.IL2CPP.StackAnalysis.StackStateBuilder.StackStateFor(IEnumerable`1 instructions, StackState initialState, MethodDefinition methodDefinition, TypeResolver typeResolver)
   在 Unity.IL2CPP.StackAnalysis.StackAnalysis.Analyze()
   在 Unity.IL2CPP.StackAnalysis.StackAnalysis.Analyze(MethodDefinition methodDefinition, ControlFlowGraph cfg, TypeResolver typeResolver)
   在 Unity.IL2CPP.MethodBodyWriter..ctor(IGeneratedMethodCodeWriter writer, MethodReference methodReference, TypeResolver typeResolver, IRuntimeMetadataAccess metadataAccess, VTableBuilder vTableBuilder, MethodBodyWriterDebugOptions options, ISourceAnnotationWriter sourceAnnotationWriter)
   在 Unity.IL2CPP.MethodWriter.WriteMethodBody(MethodReference method, IGeneratedMethodCodeWriter methodBodyWriter, IRuntimeMetadataAccess metadataAccess, ISourceAnnotationWriter sourceAnnotationWriter, IIcallMappingService icallMapping, VTableBuilder vtableBuilder, Action saveSequencePoints)
   在 Unity.IL2CPP.MethodWriter.<WriteMethodDefinition>c__AnonStorey0.<>m__0(IGeneratedMethodCodeWriter bodyWriter, IRuntimeMetadataAccess metadataAccess)
   在 Unity.IL2CPP.CodeWriterExtensions.WriteMethodWithMetadataInitialization(IGeneratedMethodCodeWriter writer, String methodSignature, String methodFullName, Action`2 writeMethodBody, String uniqueIdentifier, MethodReference methodRef)
   在 Unity.IL2CPP.MethodWriter.WriteMethodDefinition(IGeneratedMethodCodeWriter writer, MethodReference method, IMethodCollector methodCollector, IMethodVerifier methodVerifier, ISourceAnnotationWriter sourceAnnotationWriter, IIcallMappingService icallMapping, VTableBuilder vtableBuilder)
   在 Unity.IL2CPP.SourceWriter.<WriteMethodSourceFiles>c__AnonStorey3.<>m__0(IGeneratedMethodCodeWriter writer, TypeReference type)
   在 Unity.IL2CPP.SourceWriter.WriteEqualSizedChunks[T](NPath outputDir, IEnumerable`1 items, String fileName, Int64 chunkSize, Action`2 writeItemAction, SourceWritingContext sourceWritingContext)
   在 Unity.IL2CPP.SourceWriter.WriteMethodSourceFiles(NPath outputDirectory, SourceWritingContext sourceWritingContext, String fileName, IEnumerable`1 typeList, IMethodCollector methodCollector, Boolean writeMarshalingDefinitions)
   在 Unity.IL2CPP.SourceWriter.Write(NPath outputDir, SourceWritingContext sourceWritingContext, ReadOnlyCollection`1 assemblyDefinitions, IMethodCollector methodCollector, IInteropDataCollector interopDataCollector)
   在 Unity.IL2CPP.AssemblyConverter.Apply()
   在 Unity.IL2CPP.AssemblyConverter.ConvertAssemblies(IEnumerable`1 assemblyDirectories, IEnumerable`1 explicitAssemblies, NPath outputDir, NPath dataFolder, NPath symbolsFolder, NPath executableAssembiesFolder, NPath monoLibFolder, NPath monoEtcFolder, NPath[] searchDirectories, String entryAssemblyName, NPath[] extraTypesFiles)
   在 il2cpp.Program.DoRun(String[] args)
   在 il2cpp.Program.Run(String[] args)
   在 il2cpp.Program.Main(String[] args)

首先还是想的移除Acquire方法,但是提示Unacquire方法也出现同样的问题。
通过查询发现如下连接:https://issuetracker.unity3d.com/issues/unable-to-cast-object-of-type-mono-dot-cecil-dot-callsite-to-type-mono-dot-cecil-dot-methodreference-with-raw-calli-instruction
我们当前的版本为Unity 2018.1.9f,则在2018.3修复了。但是因为这个情况就变更成本很大。我们尝试通过ENABLE_IL2CPP的宏定义去解决。解决成功

#if (UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN) && !ENABLE_IL2CPP
using SharpDX;
#endif

第5次编译奇怪的错误

由于Unity闪退,一直在调成Unity构建参数,在Build页面当中勾选了Script Debug发生如下问题:

Additional information: Build a development build for more information. 未将对象引用设置到对象的实例。
il2cpp.exe didn't catch exception: System.NullReferenceException: 未将对象引用设置到对象的实例。
   在 Unity.IL2CPP.Naming.NamingExtensions.TypeMember(INamingService naming, TypeReference type, String memberName)
   在 Unity.IL2CPP.Naming.NamingExtensions.ForTypeInfo(INamingService naming, TypeReference typeReference)
   在 Unity.IL2CPP.Naming.NamingExtensions.ForRuntimeTypeInfo(INamingService naming, TypeReference type)
   在 Unity.IL2CPP.SourceWriter.WriteSequencePointMap(NPath _outputDir, SourceWritingContext sourceWritingContext)
   在 Unity.IL2CPP.AssemblyConverter.Apply()
   在 Unity.IL2CPP.AssemblyConverter.ConvertAssemblies(IEnumerable`1 assemblyDirectories, IEnumerable`1 explicitAssemblies, NPath outputDir, NPath dataFolder, NPath symbolsFolder, NPath executableAssembiesFolder, NPath monoLibFolder, NPath monoEtcFolder, NPath[] searchDirectories, String entryAssemblyName, NPath[] extraTypesFiles)
   在 il2cpp.Program.DoRun(String[] args)
   在 il2cpp.Program.Run(String[] args)
   在 il2cpp.Program.Main(String[] args)

未经处理的异常:  System.NullReferenceException: 未将对象引用设置到对象的实例。
   在 Unity.IL2CPP.Naming.NamingExtensions.TypeMember(INamingService naming, TypeReference type, String memberName)
   在 Unity.IL2CPP.Naming.NamingExtensions.ForTypeInfo(INamingService naming, TypeReference typeReference)
   在 Unity.IL2CPP.Naming.NamingExtensions.ForRuntimeTypeInfo(INamingService naming, TypeReference type)
   在 Unity.IL2CPP.SourceWriter.WriteSequencePointMap(NPath _outputDir, SourceWritingContext sourceWritingContext)
   在 Unity.IL2CPP.AssemblyConverter.Apply()
   在 Unity.IL2CPP.AssemblyConverter.ConvertAssemblies(IEnumerable`1 assemblyDirectories, IEnumerable`1 explicitAssemblies, NPath outputDir, NPath dataFolder, NPath symbolsFolder, NPath executableAssembiesFolder, NPath monoLibFolder, NPath monoEtcFolder, NPath[] searchDirectories, String entryAssemblyName, NPath[] extraTypesFiles)
   在 il2cpp.Program.DoRun(String[] args)
   在 il2cpp.Program.Run(String[] args)
   在 il2cpp.Program.Main(String[] args)

解决:删除Unity项目下Temp文件下的内容,注意Temp文件夹与Asset文件夹同级别

运行过程

注意:在Unity2018.1.9f编译的IL2Cpp运行失败,这里转到Unity2018.3.2f,强烈建议使用稳定版本

log4net - MissingMethodException

运行后,Unity正常运行,并在Debug当中报错,以忽略无用信息

MissingMethodException: Default constructor not found for type log4net.Repository.Hierarchy.Hierarchy
  at System.RuntimeType.CreateInstanceMono (System.Boolean nonPublic)
  at System.RuntimeType.CreateInstanceDefaultCtor
  at System.Activator.CreateInstance
  at System.Activator.CreateInstance
  at log4net.Core.DefaultRepositorySelector.CreateRepository
  at log4net.Core.DefaultRepositorySelector.CreateRepository
  at log4net.Core.DefaultRepositorySelector.GetRepository
  at log4net.LogManager.GetLogger
  at log4net.LogManager.GetLogger
  at Utils.UnityUtils.Log4Unity.LoggerFactory.getLogger
  at Core.SceneTree.SceneSerializer.SceneManager..cctor ()
  at Core.SceneTree.SceneSerializer.Filters.TagFilter.RegisterFilter ()
Rethrow as TypeInitializationException: The type initializer for 'Core.SceneTree.SceneSerializer.SceneManager' threw an exception.
  at Core.SceneTree.SceneSerializer.Filters.TagFilter.RegisterFilter ()

问题分析:由于log4net机制是通过配置文件的设置来调用泛型内容,在AOT的泛型展开过程不会展开任何未调用的代码。
解决方法:1.在Log4net或者其他部分代码处进行虚假调用解决;2.通过Unity的Link.xml机制解决
使用方法2解决

<linker>
       <assembly fullname="log4net" preserve="all"/>
</linker>

Newtonsoft.Json - System.NotSupportedException

由于Newtonsoft.Json当中使用Unity在AOT编译中不会实现的System.Reflection.Emit(详细内容参考本人上一篇关于IL2Cpp内容)

System.NotSupportedException: System.Reflection.Emit.DynamicMethod::.ctor
  at System.Reflection.Emit.DynamicMethod..ctor
  at Newtonsoft.Json.Utilities.DynamicReflectionDelegateFactory.CreateDynamicMethod
  at Newtonsoft.Json.Utilities.DynamicReflectionDelegateFactory.CreateDefaultConstructor[T]
  at Newtonsoft.Json.Serialization.DefaultContractResolver.InitializeContract
  at Newtonsoft.Json.Serialization.DefaultContractResolver.CreateObjectContract
  at Newtonsoft.Json.Serialization.DefaultContractResolver.CreateContract
  at Driver.SteamVR.SDK._1._0._17.IVRDriverManager+_GetDriverHandle.Invoke
  at Newtonsoft.Json.Utilities.ThreadSafeStore`2[TKey,TValue].AddValue
  at Newtonsoft.Json.Utilities.ThreadSafeStore`2[TKey,TValue].Get
  at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize
  at Newtonsoft.Json.JsonSerializer.DeserializeInternal
  at Newtonsoft.Json.JsonConvert.DeserializeObject
  at Newtonsoft.Json.JsonConvert.DeserializeObject[T]
  at Network.Update.Socket.Filter.ReceiveJsonFilter.MessageReceived
  at Mina.Core.Filterchain.DefaultIoFilterChain+<>c__DisplayClass23_0.<CallNextMessageReceived>b__0
  at System.Xml.Serialization.XmlNodeEventHandler.Invoke
  at Mina.Core.Filterchain.DefaultIoFilterChain.CallNext
  at Mina.Core.Filterchain.DefaultIoFilterChain+NextFilter.MessageReceived
  at Mina.Core.Filterchain.DefaultIoFilterChain+<>c__DisplayClass23_0.<CallNextMessageReceived>b__0
  at System.Xml.Serialization.XmlNodeEventHandler.Invoke
  at Mina.Core.Filterchain.DefaultIoFilterChain.CallNext
  at Mina.Core.Filterchain.DefaultIoFilterChain+NextFilter.MessageReceived
  at Mina.Core.Filterchain.DefaultIoFilterChain+<>c__DisplayClass23_0.<CallNextMessageReceived>b__0
  at System.Xml.Serialization.XmlNodeEventHandler.Invoke
  at Mina.Core.Filterchain.DefaultIoFilterChain.CallNext
  at Mina.Core.Filterchain.DefaultIoFilterChain+NextFilter.MessageReceived
  at Mina.Filter.Codec.ProtocolCodecFilter+ProtocolDecoderOutputImpl.Flush
  at Mina.Filter.Codec.ProtocolCodecFilter.MessageReceived
  at Mina.Core.Filterchain.DefaultIoFilterChain+<>c__DisplayClass23_0.<CallNextMessageReceived>b__0
  at System.Xml.Serialization.XmlNodeEventHandler.Invoke
  at Mina.Core.Filterchain.DefaultIoFilterChain.CallNext
  at Mina.Core.Filterchain.DefaultIoFilterChain+NextFilter.MessageReceived
  at Mina.Core.Filterchain.DefaultIoFilterChain+<>c__DisplayClass23_0.<CallNextMessageReceived>b__0
  at System.Xml.Serialization.XmlNodeEventHandler.Invoke
  at Mina.Core.Filterchain.DefaultIoFilterChain.CallNext
  at Mina.Core.Filterchain.DefaultIoFilterChain.FireMessageReceived
  at Mina.Transport.Socket.AsyncDatagramAcceptor.ReceiveCallback
  at System.Threading.ThreadPoolWorkQueue.Dispatch ()

查询Newton.Json的官方git的issue,获取如下https://github.com/JamesNK/Newtonsoft.Json/issues/1440
其中发现有个人已经为Unity编辑https://github.com/SaladLab/Json.Net.Unity3D
使用Rider直接编译并测试

SharpDX - NotSupportedException

由于需求还是需要继续使用SharpDX,则必须解放IL2Cpp的编译工作。升级Unity2018.3.x后,能够正常编译通过,但是在运行时过程发生如下问题

NotSupportedException: IL2CPP does not support marshaling delegates that point to instance methods to native code.
  at SharpDX.DirectInput.DirectInput.GetDevices (SharpDX.DirectInput.DeviceClass deviceClass, SharpDX.DirectInput.DeviceEnumerationFlags deviceEnumFlags) [0x00000] in <00000000000000000000000000000000>:0 
  at Driver.SharpDX.DirectInputUtils.Driver.DirectInputGamepadDriver.findDevice () [0x00000] in <00000000000000000000000000000000>:0 
  at Driver.SharpDX.DirectInputUtils.Driver.DirectInputGamepadDriver.Update () [0x00000] in <00000000000000000000000000000000>:0 

参考资料

通过参考资料,了解到Callback必须使用static修饰,不能使用动态方法
https://answers.unity.com/questions/1229036/callbacks-from-c-to-c-are-not-working-in-540f3.html
https://garry.tv/2018/02/15/steamworks-and-il2cpp/

SharpDX源代码分析

根据文档描述Callback应当为静态方法,

public IList<DeviceInstance> GetDevices(DeviceClass deviceClass, DeviceEnumerationFlags deviceEnumFlags)
        {
            var enumDevicesCallback = new EnumDevicesCallback();
            EnumDevices((int)deviceClass, enumDevicesCallback.NativePointer, IntPtr.Zero, deviceEnumFlags);
            return enumDevicesCallback.DeviceInstances;
        }

附录

UnityIL2Cpp泛型展开

https://docs.unity3d.com/Manual/IL2CPP-BytecodeStripping.html

关注公众号【好便宜】( ID:haopianyi222 ),领红包啦~
阿里云,国内最大的云服务商,注册就送数千元优惠券:https://t.cn/AiQe5A0g
腾讯云,良心云,价格优惠: https://t.cn/AieHwwKl
搬瓦工,CN2 GIA 优质线路,搭梯子、海外建站推荐: https://t.cn/AieHwfX9
扫一扫关注公众号添加购物返利助手,领红包
Comments are closed.

推荐使用阿里云服务器

超多优惠券

服务器最低一折,一年不到100!

朕已阅去看看