NullReferenceException与MSIL

塞瓦·阿列克谢耶夫(Seva Alekseyev)

我正在解释来自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_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

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

文档提到调试信息可能会妨碍偏移量,但这是一个发行版本。

我正在使用ILDASM检查的DLL正是我作为XAP一部分提供给Windows Phone Store的DLL。

布赖恩·赖希勒

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

正如你所看到的ldarg.0ldfldcallvirt指令都是由相同的映射覆盖,所以如果这些触发的例外,他们都将映射回相同的IL偏移(0×13)。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章