2

为了能够测试依赖于 SharePoint 的遗留代码,我需要模拟 SharePoint 的一些对象。我通过篡改 SharePoint 程序集来做到这一点,并在运行中用我的方法替换它们的方法。

这适用于某些情况,但不适用于其他情况。我遇到的一个奇怪的情况就是这个。

我想SPContext.Current用我自己的实现替换getter;为了简单起见,我的实现只是抛出了一个异常:

.property class Microsoft.SharePoint.SPContext Current()
{
  .get class Microsoft.SharePoint.SPContext Proxy.SPContextProxy::get_Current()
}

.method public hidebysig specialname static 
  class Microsoft.SharePoint.SPContext get_Current () cil managed 
{
  // Method begins at RVA 0x877e68
  // Code size 12 (0xc)
  .maxstack 8

  IL_0000: nop
  IL_0001: ldstr "Proxy don't have an effective implementation of this property."
  IL_0006: newobj instance void [mscorlib]System.NotImplementedException::.ctor(string)
  IL_000b: throw
} // end of method SPContextProxy::get_Current

在篡改原程序集的时候,如果我替换了getter对应的IL代码SPContext.Current,这个属性就不能再使用了。我什至无法在 ILSpy 中可视化它的内容,因为这是显示的内容:

System.NullReferenceException: Object reference not set to an instance of an object.
   at Mono.Cecil.Cil.CodeReader.ReadExceptionHandlers(Int32 count, Func`1 read_entry, Func`1 read_length)
   at Mono.Cecil.Cil.CodeReader.ReadSection()
   at Mono.Cecil.Cil.CodeReader.ReadFatMethod()
   at Mono.Cecil.Cil.CodeReader.ReadMethodBody()
   at Mono.Cecil.Cil.CodeReader.ReadMethodBody(MethodDefinition method)
   at Mono.Cecil.MethodDefinition.<get_Body>b__2(MethodDefinition method, MetadataReader reader)
   at Mono.Cecil.ModuleDefinition.Read[TItem,TRet](TRet& variable, TItem item, Func`3 read)
   at Mono.Cecil.MethodDefinition.get_Body()
   at ICSharpCode.Decompiler.Disassembler.ReflectionDisassembler.DisassembleMethodInternal(MethodDefinition method)
   at ICSharpCode.ILSpy.ILLanguage.DecompileProperty(PropertyDefinition property, ITextOutput output, DecompilationOptions options)
   at ICSharpCode.ILSpy.TextView.DecompilerTextView.DecompileNodes(DecompilationContext context, ITextOutput textOutput)
   at ICSharpCode.ILSpy.TextView.DecompilerTextView.<>c__DisplayClass16.<DecompileAsync>b__15()

另一方面,当我在原始指令之前插入指令时,我可以成功调用 getter,并在 ILSpy 中查看其内容:

.property class Microsoft.SharePoint.SPContext Current()
{
  .custom instance void [Microsoft.SharePoint.Client.ServerRuntime]Microsoft.SharePoint.Client.ClientCallableAttribute::.ctor() = (
    01 00 00 00
  )
  .get class Microsoft.SharePoint.SPContext Microsoft.SharePoint.SPContext::get_Current()
}

.method public hidebysig specialname static 
  class Microsoft.SharePoint.SPContext get_Current () cil managed 
{
  // Method begins at RVA 0x33e2d8
  // Code size 61 (0x3d)
  .maxstack 1
  .locals init (
    [0] class Microsoft.SharePoint.SPContext,
    [1] class [System.Web]System.Web.HttpContext,
    [2] class Microsoft.SharePoint.SPContext
  )

...按照我插入的说明进行操作:

  IL_0000: nop
  IL_0001: ldstr "Proxy doesn't implement this property yet."
  IL_0006: newobj instance void [mscorlib]System.NotImplementedException::.ctor(string)
  IL_000b: throw

...遵循原始说明:

  IL_000c: ldnull
  IL_000d: stloc.0
  IL_000e: call class [System.Web]System.Web.HttpContext [System.Web]System.Web.HttpContext::get_Current()
  IL_0013: stloc.1
  IL_0014: ldloc.1
  IL_0015: brfalse.s IL_0039
  .try
  {
    IL_0017: ldloc.1
    IL_0018: call class Microsoft.SharePoint.SPWeb Microsoft.SharePoint.WebControls.SPControl::GetContextWeb(class [System.Web]System.Web.HttpContext)
    IL_001d: brtrue.s IL_0023

    IL_001f: ldnull
    IL_0020: stloc.2
    IL_0021: leave.s IL_003b

    IL_0023: leave.s IL_002a
  } // end .try
  catch [mscorlib]System.InvalidOperationException
  {
    IL_0025: pop
    IL_0026: ldnull
    IL_0027: stloc.2
    IL_0028: leave.s IL_003b
  } // end handler
  .try
  {
    IL_002a: ldloc.1
    IL_002b: call class Microsoft.SharePoint.SPContext Microsoft.SharePoint.SPContext::GetContext(class [System.Web]System.Web.HttpContext)
    IL_0030: stloc.0
    IL_0031: leave.s IL_0039
  } // end .try
  catch [mscorlib]System.IO.FileNotFoundException
  {
    IL_0033: pop
    IL_0034: leave.s IL_0039
  } // end handler
  catch [mscorlib]System.InvalidOperationException
  {
    IL_0036: pop
    IL_0037: leave.s IL_0039
  } // end handler

  IL_0039: ldloc.0
  IL_003a: ret

  IL_003b: ldloc.2
  IL_003c: ret
} // end of method SPContext::get_Current

在插入新指令之前删除原始指令时,是什么阻止了 ILSpy 加载代码?

笔记:

  • 篡改是通过使用MethodDefinition.Body.Instructions集合(以及相应的InsertRemove方法)使用 Mono.Cecil 完成的。

  • 其他一些组装方法和属性Microsoft.SharePoint被成功篡改: ILSpy 显示生成的 IL 代码。

  • 我认为该.maxstack指令可能是一个问题(原始属性中有 1 个,代理属性中有 8 个,结果中有 1 个)。在对一个单独的项目进行了几次测试后,它似乎没有任何效果。

  • 我还怀疑异常可能是原因(原始代码引发的异常与新代码不同)。在对一个单独的项目进行了几次测试之后,它似乎也没有效果。

4

1 回答 1

4

When IL is shown in textual form, exception handling blocks (.try, catch, etc.) appear as actual blocks of IL instructions, just like they do in C#.

But in the binary form, exception handling blocks are stored separately (see §II.25.4.6 Exception handling clauses of ECMA-335) and reference the IL instructions using offsets. In Cecil, exception handlers are represented using the MethodBody.ExceptionHandlers property.

So, if you replaced the old MethodBody.Instructions with your own instructions, it's very likely that the offsets of the old exception handlers are now invalid, which is causing the issues. (The fact that Cecil throws NullReferenceException sounds like a bug to me, consider reporting it.)

The other example that you linked to which doesn't exhibit this problem is different because there the original method doesn't contain exception handlers, it throws an exception. And throw is just a normal IL instruction, it doesn't have a special representation like e.g. .try/catch does.

于 2015-08-12T20:35:49.750 回答