最近完成一款UWP应用,在手机端测试发布版(Release)的时候应用莫名奇妙的强行关闭,而同样的应用包在PC端一点问题都没有,而且Debug版在两个平台都没有问题,唯独手机的Release版有问题。实在没办法只能记录每个步骤的Log,通过查看Log发现是SuspensionManager的DataContractSerializer序列化抛出了ArgumentNullException异常。
常见.NET Native引发异常:
例1:
System.InvalidCastException: InvalidCast_Com
at SharedLibrary!<BaseAddress>+0x429e9d
…
例2:
HResult : -2146233088 TypeName : Newtonsoft.Json.JsonException, Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed Message : Newtonsoft.Json.JsonException: Error creating ‘NestedObjectJsonConverter‘. ---> System.NullReferenceException: Object reference not set to an instance of an object. at Newtonsoft.Json.Serialization.JsonTypeReflector.<>c.b__18_1(Object param) at
…
例3:
Exception thrown: ‘System.AggregateException‘ in System.Private.Threading.dll
Exception thrown: ‘System.ArgumentException‘ in System.Linq.Expressions.dll
Exception thrown: ‘System.ArgumentException‘ in System.Linq.Expressions.dll
Exception thrown: ‘System.ArgumentException‘ in System.Linq.Expressions.dll
The thread 0x2a30 has exited with code 0 (0x0).
Exception thrown: ‘System.Reflection.MissingMetadataException‘ in System.Private.Reflection.Core.dll
Additional information: ‘Microsoft.Extensions.Caching.Memory.MemoryCacheOptions‘ is missing
原因:
因为手机Release版使用了.Net Native编译。.Net Native编译时将源代码转换为本机代码不再有中间语言(IL)。这也是为什么.NET Native编译的代码中发生的异常不会出现在 JIT 编译的代码中。
.NET Native编译做了下面几件事:
详细介绍参照MSDN:https://msdn.microsoft.com/zh-cn/library/dn807190(v=vs.110).aspx
- 对于某些代码路径,它将依靠反射和元数据的代码替换为静态本机代码。
- 如果可能,它会尝试消除所有元数据。
- 它只将实际由应用程序调用的实现代码包含在最终应用程序集中。这尤其会对第三方库和 .NET Framework 类库中的代码产生影响。因此,应用程序不再依赖第三方库或完整的 .NET Framework 类库;相反,对应用程序而言,当前第三方和 .NET Framework 类库中的代码都是本地的。
- 它将完整的 CLR 替换为主要包含垃圾回收器的重构运行时。重构运行时位于应用程序中名为 mrt100_app.dll 本地库,且其大小仅为几百千字节。这可能是因为静态链接不再需要公共语言运行时执行多个服务。
常见问题操作:
因为 .NET Native只在获知应用程序实际调用了实现代码时才会将它链接到应用程序中,所以应用程序中可能不包含以下操作中所需的元数据或实现代码。如果在运行时缺少必需的元数据或实现代码,应用程序运行时将引发MissingMetadataException、MissingRuntimeArtifactException 或 MissingInteropDataException 异常。
- 反射。
- 动态或后期绑定调用。
- 序列化和反序列化。
- COM 互操作。
解决办法:
为了让.NET Native编译时不删除我们需要的元数据或者实现代码,这时需要配置Default.rd.xml(运行时指令)文件,指定程序需要的元数据。
文件地址如下图:
默认的配置为:
<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata"> <Application> <!-- An Assembly element with Name="*Application*" applies to all assemblies in the application package. The asterisks are not wildcards. --> <Assembly Name="*Application*" Dynamic="Required All"/> <!-- Add your application specific runtime directives here. --> </Application> </Directives>
为了能正确配置建议使用这个工具:MissingMetadataException troubleshooter (http://dotnet.github.io/native/troubleshooter/type.html#)
比如配置SuspensionManager挂起中断处理(DataContractSerializer序列化)
<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata"> <Application> <!-- An Assembly element with Name="*Application*" applies to all assemblies in the application package. The asterisks are not wildcards. --> <Assembly Name="*Application*" Dynamic="Required All"/> <Type Name="TestDemo.StudentModel" DataContractSerializer="Required Public" /> </Application> </Directives>
MSDN介绍配置
<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata"> <Application> <Namespace Name="Contoso.Cloud.AppServices" Serialize="Required Public" /> <Namespace Name="ContosoClient.ViewModels" Serialize="Required Public" /> <Namespace Name="ContosoClient.DataModel" Serialize="Required Public" /> <Namespace Name="Contoso.Reader.UtilityLib" Serialize="Required Public" /> <Namespace Name="System.Collections.ObjectModel" > <TypeInstantiation Name="ObservableCollection" Arguments="ContosoClient.DataModel.ProductItem" Serialize="Public" /> <TypeInstantiation Name="ReadOnlyObservableCollection" Arguments="ContosoClient.DataModel.ProductGroup" Serialize="Public" /> </Namespace> </Application> </Directives>
详细节点介绍与配置参照MSDN:运行时指令 (rd.xml) 配置 https://msdn.microsoft.com/zh-cn/library/dn600639(v=vs.110).aspx
注意点:
DataContractSerializer、DataContractJsonSerializer 和 XmlSerializer 类有些特殊,有些情况需要配置有些情况不需要配置Default.rd.xml文件。
不需要配置情况:(构造函数中使用TypeOf指定类型时.Net Native编译器会自动处理)
DataContractSerializer dataSer = new DataContractSerializer(typeof(T));
需要配置情况:(构造函数外部使用Typeof指定类型时必须在Default.rd.xml文件配置)
Type t = typeof(DataSet); XmlSerializer ser = new XmlSerializer(t);
或者SuspensionManager挂起中断处理的
DataContractSerializer serializer = new DataContractSerializer(typeof(Dictionary<string, object>), _knownTypes);
或者
XmlSerializer xSerializer = new XmlSerializer(typeof(Teacher), new Type[] { typeof(Student), typeof(Course), typeof(Location) });
备注: