NullReferenceException与MSIL

Sev*_*yev 7 c# cil .net-assembly windows-phone

我正在解释来自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();
}
Run Code Online (Sandbox Code Playgroud)

它与m_Fieldnull 一致- 那里没有任何其他可能为null的东西.但这是神秘的部分.

GetILOffset()StackFrameStackTrace该异常对象返回0×13.如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
Run Code Online (Sandbox Code Playgroud)

这是我不明白的.如果偏移确实是0x13,则意味着该ldarg行导致异常.但该命令记录为不会抛出任何异常.这callvirt应该扔,不是吗?或者相对于方法开始以外的其他方式的偏移量?ldfld也可以抛出,但仅当this对象为null时; 这在C#AFAIK中是不可能的.

文档提到调试信息可能会妨碍偏移,但它是一个发布版本.

我正在使用ILDASM检查的DLL正是我作为XAP的一部分发送到Windows Phone Store的DLL.

Bri*_*hle 5

当 JIT 生成机器代码时,它也会生成 MSIL <--> 机器代码映射。当您在生成的代码中遇到异常时,运行时将使用映射来识别 IL 偏移量。

允许 JIT 重新排序机器指令作为其优化的一部分(当它们被启用时),这可能导致映射变得更加近似和细化。如果字段访问被提前(内存访问相对较慢,有时在你需要它之​​前就开始加载它会很好)那么异常可能看起来是由早期的 IL 指令抛出的。


我屠杀了我的调试实用程序之一来执行以下操作:

  • 启动一个目标进程并运行直到出现异常
  • 捕获 IL 字节和 IL 到本机的映射
  • (粗略地)用指示符显示哪些 IL 指令与相同的映射组合在一起,反汇编 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
Run Code Online (Sandbox Code Playgroud)

如您所见,ldarg.0,ldfldcallvirt指令都包含在同一个映射中,因此如果其中任何一个触发了异常,它们都将映射回相同的 IL 偏移量 (0x13)。