【发布时间】:2012-03-31 20:30:42
【问题描述】:
在使用匿名托管的动态方法时,任何人都可以解释为什么我在公共类上的公共虚拟方法中得到 ldvirtftn 无法验证的异常?我还设置了以下程序集级别属性:
[assembly: SecurityTransparent]
[assembly: SecurityRules(SecurityRuleSet.Level2,SkipVerificationInFullTrust=true)]
示例代码如下:
public class Program
{
public virtual void Foo() {}
public static void Main(string[] args)
{
Action<ILGenerator> genfunc = il => il
.newobj<Program>()
.ldvirtftn(typeof(Program).GetMethod("Foo"))
.ret();
try
{
Console.WriteLine(CodeGen.CreateDelegate<Func<IntPtr>>(genfunc).Invoke());
}
catch (System.Security.VerificationException) { }
Console.WriteLine(CodeGen.CreateDelegate<Func<IntPtr>>(genfunc,owner:typeof(Program)).Invoke());
}
}
如果方法是拥有的,那么它不会抛出异常。
更奇怪的是,如果我像这样更改代码,那么这两种方法都可以编译并运行而不会出现问题:
public class Program
{
public virtual void Foo() {}
public static void Main(string[] args)
{
Action<ILGenerator> genfunc = il => il
.newobj<Program>()
.dup()
.ldvirtftn(typeof(Program).GetMethod("Foo"))
.newobj<Action>(typeof(object),typeof(IntPtr))
.ret();
try
{
Console.WriteLine(CodeGen.CreateDelegate<Func<Action>>(genfunc).Invoke());
}
catch (System.Security.VerificationException) { }
Console.WriteLine(CodeGen.CreateDelegate<Func<Action>>(genfunc,owner:typeof(Program)).Invoke());
}
}
这段代码是用反射库编写的。
CodeGen.CreateDelegate 只是使用类型参数来确定动态方法的签名。方法如下::
public static TDelegate CreateDelegate<TDelegate>(
Action<ILGenerator> genfunc, string name = "", object target = null, Type owner = null, bool skipVisibility = false)
where TDelegate : class
{
var invokeMethod = typeof(TDelegate).GetMethod("Invoke");
var parameters = invokeMethod.GetParameters();
var paramTypes = new Type[parameters.Length + 1];
paramTypes[0] = typeof(object);
parameters.Select(p => p.ParameterType).ToArray().CopyTo(paramTypes, 1);
var method = owner != null ?
new DynamicMethod(name, invokeMethod.ReturnType, paramTypes, owner, skipVisibility) :
new DynamicMethod(name, invokeMethod.ReturnType, paramTypes, skipVisibility);
genfunc(method.GetILGenerator());
return method.CreateDelegate(typeof(TDelegate), target) as TDelegate;
}
【问题讨论】:
-
MSIL 不仅适用于托管代码。您也可以将原始本机 C++ 代码编译为 IL。由 C++/CLI 编译器完成。 Opcodes.Ldvirtfn 很重要的那种代码,它从 v-table 中挖掘出一个函数指针。当抖动验证器被击中时,可以使用原始地址 IntPtr,而不是大量验证。
-
是的,但是为什么给方法一个拥有类型会使验证问题消失呢?
标签: c# reflection.emit dynamicmethod