2

SharePoint に依存するレガシ コードをテストできるようにするために、SharePoint のオブジェクトのいくつかをモックする必要があります。これを行うには、SharePoint アセンブリを改ざんし、そのメソッドをその場で自分のものに置き換えます。

これは、場合によっては機能しますが、他の場合には機能しません。私が遭遇した奇妙な状況はこれです。

SPContext.Currentのゲッターを独自の実装に置き換えたい。簡単にするために、私の実装では例外をスローするだけです。

.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

元のアセンブリを改ざんする場合、ゲッターに対応する 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改ざんは、コレクション (および対応するInsertandRemoveメソッド)を使用して、Mono.Cecil で行われます。

  • アセンブリの他のいくつかのメソッドとプロパティは、Microsoft.SharePoint正常に改ざんされています。ILSpy は結果の IL コードを表示します。

  • そのディレクティブが問題になる可能性があると思い.maxstackました (元のプロパティで 1、プロキシされたプロパティで 8、結果で 1)。別のプロジェクトでいくつかのテストを行った後、効果がないように見えます。

  • また、例外が原因である可能性も考えました (元のコードは新しいコードとは異なる例外をスローします)。別のプロジェクトでいくつかのテストを行った後、効果がないように見えます。

4

1 に答える 1

4

IL がテキスト形式で表示される場合、例外処理ブロック ( .trycatchなど) は、C# の場合と同様に、IL 命令の実際のブロックとして表示されます。

ただし、バイナリ形式では、例外処理ブロックは個別に格納され ( §II.25.4.6 ECMA-335 の例外処理条項を参照)、オフセットを使用して IL 命令を参照します。Cecil では、例外ハンドラはMethodBody.ExceptionHandlersプロパティを使用して表されます。

そのため、古いものを独自の命令に置き換えた場合MethodBody.Instructions、古い例外ハンドラーのオフセットが無効になり、問題が発生する可能性が非常に高くなります。(Cecil がスローするという事実はNullReferenceException、私にはバグのように聞こえます。報告することを検討してください。)

この問題を示さないリンク先の他の例は、元のメソッドに例外ハンドラーが含まれておらず、例外がスローされるため、異なります。Andthrowは単なる通常の IL 命令であり、eg .try/のような特別な表現はありませんcatch

于 2015-08-12T20:35:49.750 に答える