Reflection.emit 系统.InvalidProgramException:公共语言运行时检测到一个无效程序。

5

我是reflection.emit的新手,一直在尝试生成以下c#代码:

public class RepositoryWrapper
{
    public void CallRepositoryMethod(IAddressRepository repository, Address address)
    {
        repository.NODE_I_NodeExtendedDetails_Address3(address.NodeId);
    }
}

以下是它的中间代码表示:

    IL_0000: nop
    IL_0001: ldarg.1
    IL_0002: ldarg.2
    IL_0003: callvirt instance int32 ReflectionServices.Node::get_NodeId()
    IL_0008: callvirt instance void ReflectionServices.IAddressRepository::NODE_I_NodeExtendedDetails_Address3(int32)
    IL_000d: nop
    IL_000e: ret

以下是我用来创建它的代码:

 internal static void Generate(this System.Reflection.Emit.ILGenerator @this, Type target,string method,Type instance)
        {

        var methodToCall = target.GetMethod(method);
        var methodParams = methodToCall.GetParameters();
        var instanceProperties = instance.GetProperties(BindingFlags.Public | BindingFlags.Instance);

        var orderedProperties = (from mp in methodParams
                              join p in instanceProperties
                              on mp.Name.ToLower() equals p.Name.ToLower()
                              select p).ToArray();

        //add properties to the string builder

        //load the object reference onto the stack sothat we can access its methods
        @this.Emit(OpCodes.Nop);
        @this.Emit(OpCodes.Ldarg_1);
        @this.Emit(OpCodes.Ldarg_2);

            var property = orderedProperties.FirstOrDefault(x => x.Name == "NodeId");
            if (property != null)
            {
                var getMethod = property.GetGetMethod();
                @this.Emit(getMethod.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, getMethod);

            }


        //call method 
            @this.Emit(methodToCall.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, methodToCall);
            @this.Emit(OpCodes.Nop);
        //return from function
        @this.Emit(OpCodes.Ret);
        }

我遇到的错误如下:

System.InvalidProgramException: Common Language Runtime detected an invalid program.
Result StackTrace:  
at ReflectionServices.Repository.NODE_I_NodeExtendedDetails3_Address40807399(Repository      target, Address )

以下是生成的IL代码:

nop
ldarg.1
ldarg.2
call instance int32 ReflectionServices.Node::get_NodeId()
callvirt instance void            
ReflectionServices.Repository::
                               NODE_I_NodeExtendedDetails3_Address(int32)
nop

ret

有人能看出我卡在哪里了吗?

谢谢。

这是我所请求的dll和方法:

 public sealed class ReflectionEmitWithDebuggingMethodGenerator
{
    private AssemblyBuilder Assembly { get; set; }
    private ModuleBuilder Module { get; set; }
    private AssemblyName Name { get; set; }

    public ReflectionEmitWithDebuggingMethodGenerator()
        : base()
    {
        this.Name = new AssemblyName() { Name = Guid.NewGuid().ToString("N") };
        this.Assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(
            this.Name, AssemblyBuilderAccess.RunAndSave,@"C:\Users\darren\Documents\Visual Studio 2012\Projects\UnityInjection");
        this.AddDebuggingAttribute(this.Assembly);
        this.Module = this.Assembly.DefineDynamicModule(this.Name.Name + ".dll", true);
    }
   public Action<TObject, TInstance> Generate<TObject, TInstance>(Type target, string methodN, Type instanceType)
    {
        var type = this.Module.DefineType(target.Namespace + "." + target.Name);
        var methodName = methodN + target.GetHashCode().ToString();
        var method = type.DefineMethod(methodName, MethodAttributes.Static | MethodAttributes.Public, typeof(void), new Type[] { target, instanceType });
        method.DefineParameter(1, ParameterAttributes.In, "target");
        method.DefineParameter(2, ParameterAttributes.In, "instance");

        ILGenerator.Generate(method.GetILGenerator(), target,methodN,instanceType);

        var createdType = type.CreateType();

        var createdMethod = createdType.GetMethod(methodName);
        return (Action<TObject, TInstance>)Delegate.CreateDelegate(typeof(Action<TObject, TInstance>), createdMethod);
    }


}

你在调用生成的程序集时出现了错误,对吗?你是否将生成的 IL 与你建模的 IL 进行了比较? - Michael Gunter
是的,当执行该函数时我会收到错误。我已经更新了帖子并附上了生成的Il代码。 - Code Junkie
嗨,svick,我似乎无法找到创建的dll文件。 - Code Junkie
在运行PEVerify程序集后,我解决了问题。错误是未识别的参数,所以我将@this.Emit(OpCodes.Ldarg_1);和@this.Emit(OpCodes.Ldarg_2);更改为@this.Emit(OpCodes.Ldarg_0); @this.Emit(OpCodes.Ldarg_1); 我猜这是因为它不是成员调用?有人能详细说明吗?如果svick将您的建议作为答案,我会将其标记为正确。 - Code Junkie
@Code Junkie,我的回答解释了为什么你的原始代码不能正确工作。 - Adam Maras
显示剩余3条评论
1个回答

3
与编译和发射的输出进行比较,只有一个区别:
编译后的输出:
callvirt instance int32 ReflectionServices.Node::get_NodeId()

发出:

call instance int32 ReflectionServices.Node::get_NodeId()

你正在调用int32 get_NodeId()的类型是ReflectionServices.Node,但你试图复制的方法中传入的对象类型是Address。这让我相信在ReflectionServices.Node上定义的属性访问器必须被虚拟调用,可能是因为它继承自另一个类(或实现了一个在ReflectionServices.Node实现之前声明该属性的接口)。
当你发出那行代码时,只需要以虚拟方式调用即可。
@this.Emit(OpCodes.Callvirt, getMethod);

编辑:根据提供的更多代码,这里是真正的解决方案。

因此,您在实现接口的基本原理上遇到了问题:

var method = type.DefineMethod(methodName, MethodAttributes.Static | Method...
//                                                          ^^^^^^

接口方法不是静态的,它们是实例成员。因此,在创建MethodBuilder时,您需要首先从属性标志中删除MethodAttributes.Static

其次,当您要返回此函数时,您将需要包括一个目标对象,该对象是调用该方法的实例。为此,您可以使用Activator.CreateInstance调用默认生成的构造函数并提供一个已实例化的对象以用作目标。用这些行替换您的Generate方法的最后一行即可实现这一点。

var activatedObject = Activator.CreateInstance(type);

return (Action<TObject, TInstance>)Delegate.CreateDelegate(
    typeof(Action<TObject, TInstance>), activatedObject, createdMethod);

你是对的,Address类继承自具有NodeId属性的Node类。我尝试了你的更改并只调用了OpCodes.Callvirt,但它仍然有相同的错误... - Code Junkie
第二个参数呢? - Code Junkie
在那行代码返回后,我调用了这个: - Code Junkie
((Action<TObject, TInstance>)Delegate.CreateDelegate(typeof(Action<TObject, TInstance>), createdMethod))(repository,address); - Code Junkie
这意味着我可以缓存该函数,如果我按照你所提到的去掉 static 关键字,它就无法正常工作了? - Code Junkie
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接