none
dynamicmethod在release下报错 RRS feed

  • 问题

  • 创建一个dynamicmethod,利用emit构造方法体,在debug下调用正常,但是生成release版本运行时报错,dynamicmethod在调用完毕后出错,

    错误信息大意是 试图读取或写入受保护的内存。。。。,试过dynamicmethod两种调用方式,一样的问题。

    dynamicmtehod  method = new dynamicmethod(。。。。);

    1 (T)method.createdelegate(typeof(T))(arg);

    2 method.invoke(null,arg);

    求教高人指点!


    asp.net developer

    2012年11月14日 8:35

答案

  • 从你的代码看 你是在一个web项目里测试的。

    不过我在一个console项目里测试的,以下是全部测试代码,在debug/release模式下都没问题, platform x86。

        class Program
        {
            static void Main(string[] args)
            {
                DataMapManager dmm = new DataMapManager();
                dmm.LoadTestSub();
            }
        }
    
        public class DataMapManager
        {
            public void LoadTestSub()
            {
                Action action = (Action)Build().CreateDelegate(typeof(Action));
                action();
                int k = 5;
                Console.WriteLine(k);
            }
            public DynamicMethod Build()
            {
                DynamicMethod method = new DynamicMethod("DynMethod", typeof(void), null);
                ILGenerator generator = method.GetILGenerator();
                generator.Emit(OpCodes.Nop);
                generator.Emit(OpCodes.Ret);
                return method;
    
            }
        }

    希望你能在一个新的工程里重现这个问题。

    这是按你要求的要有返回值的时候:

            public void LoadTestSub()
            {
                //Action action = (Action)Build().CreateDelegate(typeof(Action));
    
                int k = (int)Build().Invoke(null, null);
                Console.WriteLine(k);
            }
            public DynamicMethod Build()
            {
                DynamicMethod method = new DynamicMethod("DynMethod", typeof(int), null);
                ILGenerator generator = method.GetILGenerator();
                generator.Emit(OpCodes.Nop);
                generator.Emit(OpCodes.Ldc_I4, 5);
                generator.Emit(OpCodes.Ret);
                return method;
    
            }

    你不能简单地将你原来的代码改为int k=action();  http://msdn.microsoft.com/en-us/library/system.action.aspx  

    这里的action的没有返回值,没有入参.


    Mike Feng
    MSDN Community Support | Feedback to us
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.


    2012年11月20日 7:02
    版主

全部回复

  • 不好意思,因为牵涉的代码太多,我大致再描述一下,该动态方法是对datareader和实体的映射。就是根据实体类型动态生成一个由datareader转换为实体的方法。

    首先构造动态方法

      static DynamicMethod  BuildMethod<T>()  {根据T的类型构造一个方法体}
      存储于dictionary<string,dynamicmethod>里(methodName,method)

    读取动态方法 

     GetMethod(methodname)
    {
       从  dictionary<string,dynamicmethod>把method拿出来
       并返回方法的委托实例 (type)method.createdelegate(type)
    }

    然后 调用之

    GetMethod(method)(arg);

    具体代码如下

     public class DataMapManager
        {
            public static Dictionary<string, DynamicMethod> DataReaderMapMehotds
            {
                get
                {
                    Dictionary<string, DynamicMethod> dataReaderMapMehotds = (Dictionary<string, DynamicMethod>)EntityCache.Cache.Get(CacheKeys.DataReaderMapMehotds.ToString());
                    if (dataReaderMapMehotds == null)
                    {
                        dataReaderMapMehotds = new Dictionary<string, DynamicMethod>();
                        EntityCache.Cache.Insert(CacheKeys.DataReaderMapMehotds.ToString(), dataReaderMapMehotds);
                    }
                    return dataReaderMapMehotds;
                }
            }

            public static Func<IDataReader,T> GetDataReaderMapMethod<T>()
            {
                DynamicMethod result;
                string key = typeof(T).Name;
                if (!DataReaderMapMehotds.TryGetValue(key, out result))
                {
                    result = BuildMapMethod<T>();
                    DataReaderMapMehotds.Add(key, result);
                }
                return (Func<IDataReader, T>)result.CreateDelegate(typeof(Func<IDataReader, T>));
            }

            private static readonly Dictionary<Type, MethodInfo> ConvertMethodInfos = new Dictionary<Type, MethodInfo>()
           {     
               {typeof(int),typeof(Convert).GetMethod("ToInt32",new Type[]{typeof(object)})},
               {typeof(Int16),typeof(Convert).GetMethod("ToInt16",new Type[]{typeof(object)})},
               {typeof(Int64),typeof(Convert).GetMethod("ToInt64",new Type[]{typeof(object)})},
               {typeof(DateTime),typeof(Convert).GetMethod("ToDateTime",new Type[]{typeof(object)})},
               {typeof(decimal),typeof(Convert).GetMethod("ToDecimal",new Type[]{typeof(object)})},
               {typeof(Double),typeof(Convert).GetMethod("ToDouble",new Type[]{typeof(object)})},
               {typeof(Boolean),typeof(Convert).GetMethod("ToBoolean",new Type[]{typeof(object)})},
               {typeof(string),typeof(Convert).GetMethod("ToString",new Type[]{typeof(object)})}     
           };

            public static bool CanSetted(IDataReader dr, string name)
            {
                bool result = false;
                for (int i = 0; i < dr.FieldCount; i++)
                {
                    if (dr.GetName(i).Equals(name, StringComparison.CurrentCultureIgnoreCase) && !dr[i].Equals(DBNull.Value))
                    {
                        result = true;
                        break;
                    }
                }
                return result;
            }

            private static MethodInfo _canSetedMethod;
            private static MethodInfo CanSettedMethod
            {
                get
                {
                    if (_canSetedMethod == null)
                    {
                        _canSetedMethod = typeof(DataMapManager).GetMethod("CanSetted", new Type[] { typeof(IDataReader), typeof(string) });
                    }
                    return _canSetedMethod;
                }
            }

            private static MethodInfo _getValueMethod;
            private static MethodInfo GetValueMethod
            {
                get
                {
                    if (_getValueMethod == null)
                    {
                        _getValueMethod = typeof(IDataRecord).GetMethod("get_Item", new Type[] { typeof(string) });
                    }
                    return _getValueMethod;
                }
            }

            private static DynamicMethod  BuildMapMethod<T>()
            {
                DynamicMethod method = new DynamicMethod(typeof(T).Name+"_RowMap", typeof(T),
                        new Type[] { typeof(IDataReader) }, typeof(EntityContext).Module,true);        
                ILGenerator generator = method.GetILGenerator();
                LocalBuilder result = generator.DeclareLocal(typeof(T));
                generator.Emit(OpCodes.Newobj, typeof(T).GetConstructor(Type.EmptyTypes));
                generator.Emit(OpCodes.Stloc, result);

                foreach (PropertyInfo property in typeof(T).GetProperties())
                {
                    Label endIfLabel = generator.DefineLabel();
                    generator.Emit(OpCodes.Ldarg_0);
                    generator.Emit(OpCodes.Ldstr, property.Name);
                    generator.Emit(OpCodes.Callvirt, CanSettedMethod);
                    generator.Emit(OpCodes.Brfalse, endIfLabel);
                    generator.Emit(OpCodes.Ldloc, result);
                    generator.Emit(OpCodes.Ldarg_0);
                    generator.Emit(OpCodes.Ldstr, property.Name);
                    generator.Emit(OpCodes.Callvirt, GetValueMethod);
                    if (property.PropertyType.IsValueType || property.PropertyType== typeof(string))
                        generator.Emit(OpCodes.Call, ConvertMethodInfos[property.PropertyType]);
                    else
                        generator.Emit(OpCodes.Castclass, property.PropertyType);
                    generator.Emit(OpCodes.Callvirt, property.GetSetMethod());
                    generator.MarkLabel(endIfLabel);
                }

                generator.Emit(OpCodes.Ldloc, result);
                generator.Emit(OpCodes.Ret);
                return method;
            }


            public static  List<TEntity> Convert<TEntity>(IDataReader dr)
            {
                List<TEntity> result = new List<TEntity>();
                Func<IDataReader, TEntity> rowMap = DataMapManager.GetDataReaderMapMethod<TEntity>();
                while (dr.Read())
                {
                    result.Add(rowMap(dr));
                }
                return result;
            }

        }


    asp.net developer

    2012年11月14日 9:00
  • Hi  Linping,

    How can I reference the type EntityCache and CacheKeys, and EntityContext?

    thank you.


    Mike Feng
    MSDN Community Support | Feedback to us
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    2012年11月15日 12:00
    版主
  • 您好,因涉及代码太多,我把问题简化了,代码如下。

      protected void Page_Load(object sender, EventArgs e)
            {
                Action action = (Action)Build().CreateDelegate(typeof(Action));
                action();
                int k = 5;         
            }

            public  DynamicMethod Build()
            {
                DynamicMethod method = new DynamicMethod("DynMethod", typeof(void), null);
                ILGenerator generator = method.GetILGenerator();
                generator.Emit(OpCodes.Nop);
                generator.Emit(OpCodes.Ret);
                return method;

            }

    创建的动态方法是一个无参数,无返回值的空方法,当程序在debug下可以正常运行,但是在release下运行action()就会自动跳出终止代码执行。


    asp.net developer

    2012年11月17日 8:12
  • 从你的代码看 你是在一个web项目里测试的。

    不过我在一个console项目里测试的,以下是全部测试代码,在debug/release模式下都没问题, platform x86。

        class Program
        {
            static void Main(string[] args)
            {
                DataMapManager dmm = new DataMapManager();
                dmm.LoadTestSub();
            }
        }
    
        public class DataMapManager
        {
            public void LoadTestSub()
            {
                Action action = (Action)Build().CreateDelegate(typeof(Action));
                action();
                int k = 5;
                Console.WriteLine(k);
            }
            public DynamicMethod Build()
            {
                DynamicMethod method = new DynamicMethod("DynMethod", typeof(void), null);
                ILGenerator generator = method.GetILGenerator();
                generator.Emit(OpCodes.Nop);
                generator.Emit(OpCodes.Ret);
                return method;
    
            }
        }

    希望你能在一个新的工程里重现这个问题。

    这是按你要求的要有返回值的时候:

            public void LoadTestSub()
            {
                //Action action = (Action)Build().CreateDelegate(typeof(Action));
    
                int k = (int)Build().Invoke(null, null);
                Console.WriteLine(k);
            }
            public DynamicMethod Build()
            {
                DynamicMethod method = new DynamicMethod("DynMethod", typeof(int), null);
                ILGenerator generator = method.GetILGenerator();
                generator.Emit(OpCodes.Nop);
                generator.Emit(OpCodes.Ldc_I4, 5);
                generator.Emit(OpCodes.Ret);
                return method;
    
            }

    你不能简单地将你原来的代码改为int k=action();  http://msdn.microsoft.com/en-us/library/system.action.aspx  

    这里的action的没有返回值,没有入参.


    Mike Feng
    MSDN Community Support | Feedback to us
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.


    2012年11月20日 7:02
    版主
  • 首先感谢你的解答,我测试了你的代码,确实能在release下正常运行,但是经过我仔细比较,发现一个很诡异的现象,

    就是你的代码里多了             Console.WriteLine(k);

    这一行代码会导致  action()执行完毕以后不会终止程序,而是继续执行 int k=5...,

    如果你把  Console.WriteLine(k);去掉可能会发现问题。


    asp.net developer

    2012年11月20日 12:31
  • 如果我不加  Console.WriteLine(k);;那我应该怎么判定 int k=5没有被执行?

    Mike Feng
    MSDN Community Support | Feedback to us
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    2012年11月21日 8:53
    版主
  • 你可以使用F5进行调试 然后单步执行观察的(vs上的解决方案配置设定为release)。

    另外 即使加了console。write,使得代码可以继续执行,但其实 action()也没有正常运行,

    假设你把动态方法换成一个有返回值的的方法  如 

        int n = action();

        int k = 5;
        
    Console.WriteLine(k);

    单步执行到 int k=5时, 观察n的值,发现n根本没有被定义。


    asp.net developer

    2012年11月21日 15:06
  • 你好,

    这是Build方法的IL代码:

        .maxstack 2
        .locals init (
            [0] class [mscorlib]System.Action action,
            [1] int32 k)
        L_0000: ldarg.0 
        L_0001: call instance class [mscorlib]System.Reflection.Emit.DynamicMethod ConApp_Dynamic.DataMapManager::Build()
        L_0006: ldtoken [mscorlib]System.Action
        L_000b: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
        L_0010: callvirt instance class [mscorlib]System.Delegate [mscorlib]System.Reflection.Emit.DynamicMethod::CreateDelegate(class [mscorlib]System.Type)
        L_0015: castclass [mscorlib]System.Action
        L_001a: stloc.0 
        L_001b: ldloc.0 
        L_001c: callvirt instance void [mscorlib]System.Action::Invoke()
        L_0021: ldc.i4.5 
        L_0022: stloc.1 
        L_0023: ldloc.1 
        L_0024: call void [mscorlib]System.Console::WriteLine(int32)
        L_0029: ret 
    

    你可以看到, 是有invoke语句的:

    L_001c: callvirt instance void [mscorlib]System.Action::Invoke() 所以这个方法被执行了.

    Mike Feng
    MSDN Community Support | Feedback to us
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    2012年11月23日 4:16
    版主