我正在解释来自C#Windows Phone应用的异常报告。方法抛出NullReferenceException
。方法是:
public void OnDelete(object o, EventArgs a)
{
if (MessageBox.Show(Res.IDS_AREYOUSURE, Res.IDS_APPTITLE, MessageBoxButton.OKCancel) == MessageBoxResult.OK)
m_Field.RequestDelete();
}
这与m_Field
为空是一致的 - 那里没有任何其他可能为null的东西。但这是神秘的部分。
异常对象的GetILOffset()
StackFrame
中的StackTrace
返回0x13。如ILDASM所示,该方法的MSIL为:
IL_0000: call string App.Res::get_IDS_AREYOUSURE()
IL_0005: call string App.Res::get_IDS_APPTITLE()
IL_000a: ldc.i4.1
IL_000b: call valuetype (...) System.Windows.MessageBox::Show(...)
IL_0010: ldc.i4.1
IL_0011: bne.un.s IL_001e
IL_0013: ldarg.0
IL_0014: ldfld class App.Class2 App.Class1::m_Field
IL_0019: callvirt instance void App.Class2::RequestDelete()
IL_001e: ret
这是我不明白的地方。如果偏移确实是0x13,则意味着ldarg
行导致异常。但该命令记录为不会抛出任何异常。这应该是callvirt
,不是吗?或者相对于方法开始以外的其他方式的偏移量? ldfld
也可以抛出,但前提是this
对象为空;这在C#AFAIK中是不可能的。
文档提到调试信息可能会妨碍偏移,但它是一个发布版本。
我正在使用ILDASM检查的DLL正是我作为XAP的一部分发送到Windows Phone Store的那个。
答案 0 :(得分:3)
当JIT生成机器代码时,它还生成MSIL< - >机器代码映射。当您在生成的代码中出现异常时,运行时将使用映射来标识IL偏移量。
允许JIT重新订购机器指令作为其优化的一部分(当它们被启用时),这可能导致映射变得更加近似和细化。如果提前进行了字段访问(内存访问相对较慢,有时可以在需要之前开始加载它),那么异常可能看起来已被早期的IL指令抛出。
我屠杀了我的一个调试实用程序来执行以下操作:
然后我在一个虚拟进程上运行该工具,该进程大致与您在问题中显示的内容相同,并得到以下内容(发布版本):
IL_0000: call 0600000B
IL_0005: call 0600000A
IL_000A: ldc.i4.1
IL_000B: call 0A000014
IL_0010: ldc.i4.1
IL_0011: bne.un.s 30
----
IL_0013: ldarg.0
IL_0014: ldfld 04000001
IL_0019: callvirt 06000004
----
IL_001E: ret
正如您所看到的,ldarg.0
,ldfld
和callvirt
指令都包含在同一个映射中,因此如果其中任何一个触发了异常,它们都将映射回相同的IL偏移量(0x13)。