最近学习Emit,在使用Emit动态生成对象时碰到一些“蛋疼”的问题,如下:
1、安全透明方法“XXX.XX()”尝试访问安全关键方法“YYY.YY()”失败。
2、方法“XXX(System.Object[])”尝试访问方法“YYY.ctor()”失败。
    上面两个都是System.MethodAccessException异常。刚开始一看,懵B了。这提示信息也太少了吧。没办法就这么残酷,只好硬着头皮,顺藤摸瓜找原因。功夫不负有心人,终于找到原因了,阿弥陀佛!
说道造成System.MethodAccessException异常的原因,就得先说一下反射安全
    从 .NET Framework 4 版开始,仅受信任的代码才能使用反射访问安全关键成员。而且,也只有受信任的代码才能使用反射访问非公共成员。最后,使用反射访问安全关键成员的代码必须具有安全关键成员要求的任何权限。
那什么是安全关键成员呢?满足下面一下条件之一的:具有 SecurityCriticalAttribute;属于一个具有 SecurityCriticalAttribute 的类型;或者位于一个安全关键程序集中;
    访问安全关键成员的规则如下所示:

  • 透明代码不能使用反射来访问安全关键成员,即使代码是完全受信任的也是如此。 将引发 MethodAccessException、FieldAccessException 或 TypeAccessException。
  • 以部分信任方式运行的代码将被视为透明的。

    无论安全关键成员是由已编辑代码直接访问,还是通过使用反射访问,这些规则都是相同的。 
    公共语言运行时将根据多个因素来确定类型或成员的透明度级别,这些因素包括程序集的信任级别和应用程序域的信任级别。 反射提供 IsSecurityCritical、IsSecuritySafeCritical 和 IsSecurityTransparent 属性以使您能够发现类型的透明度级别。 下表显示了这些属性的有效组合。

安全级别 IsSecurityCritical IsSecuritySafeCritical IsSecurityTransparent
安全级别高的 true false false
安全关键的 true true false
透明的 false false true

MethodBase 、FieldInfo、TypeBuilder、MethodBuilder 和 DynamicMethod 类都有相似的属性。
    若要使用反射来调用根据公共语言运行时的可访问性规则不可访问的成员,代码中必须授予带有 ReflectionPermissionFlag.MemberAccess 标志的 ReflectionPermission。需要注意的是默认情况下,.NET安全策略拒绝向源自 Internet 的代码授予此权限。 所以也不要向源自 Internet 的代码授予此权限。
    理解了上面的概念,System.MethodAccessException异常也就迎刃而解了,解决方案:
1、要Emit的代码中有非公开的成员。我查看一下代码,果然有一个类是internal的。这个简单把它改成public就可以了。
2、本来想加上ReflectionPermissionFlag.MemberAccess ,但总感觉不太好,破坏了程序集的安全性。经过不断的测试,找到了一个完美的解决方案,下面举例说明:
首先,我原先动态生成对象的方法时这样写的

  private static CreateOjectHandler CreateHandler(Type type, Type[] paramsTypes)
{ ConstructorInfo constructor = type.GetConstructor(paramsTypes);
DynamicMethod method = new DynamicMethod("DynamicCreateOject", typeof(object),
new Type[] { typeof(object[]) }, typeof(FastObjectCreater).Module); ILGenerator il = method.GetILGenerator(); for (int i = 0; i < paramsTypes.Length; i++)
{
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldc_I4, i);
il.Emit(OpCodes.Ldelem_Ref);
if (paramsTypes[i].IsValueType)
{
il.Emit(OpCodes.Unbox_Any, paramsTypes[i]);
}
else
{
il.Emit(OpCodes.Castclass, paramsTypes[i]);
}
}
il.Emit(OpCodes.Newobj, constructor);
il.Emit(OpCodes.Ret); return (CreateOjectHandler)method.CreateDelegate(typeof(CreateOjectHandler));
}

上面的代码放到项目A中,然后在项目B中调用

    class Program
{
static void Main(string[] args)
{
var chinese = FastObjectCreater.CreateInstance<Chinese>();
chinese.SayHello(); Console.ReadKey();
} public class Chinese
{
public Chinese()
{
} public Chinese(string name)
{
} public void SayHello()
{
Console.WriteLine("你好!!!");
} public override string ToString()
{
return "中国人";
}
} }

    上面代码在项目B中定义。接着,运行项目B就会出现问题2的异常。

    请注意这段代码 DynamicMethod method = new DynamicMethod("DynamicCreateOject", typeof(object),new Type[] { typeof(object[]) }, typeof(FastObjectCreater).Module); 它就是罪魁祸首。因为DynamicCreateOject方法生成在项目A中,而且项目B的Program方法是私有的,Chinese类是它的内部类,所以在Main方法中访问Chinese类是私有的级别。.NET 4不允许非受信任的代码用反射访问非公共成员,所以在执行FastObjectCreater.CreateInstance<Chinese>();代码时就引发了异常。所以解决的方法很简单,只要把DynamicMethod类的module参数改为constructor.DeclaringType.Module。这就把原来的属于项目A的DynamicCreateOject方法变成项目B的DynamicCreateOject方法,也就不存在非信任代码的问题。

    OK,终于写完了,回家喽!

【Emit】关于System.MethodAccessException解决方案的更多相关文章

  1. [C#] 解决Silverlight反射安全关键(SecuritySafeCritical)时报“System.MethodAccessException: 安全透明方法 XXX 无法使用反射访问”的问题

    作者: zyl910 一.缘由 在Silverlight中使用反射动态访问时,经常遇到"System.MethodAccessException: 安全透明方法 XXX 无法使用反射访问-- ...

  2. MySQL Unable to convert MySQL datetime value to System.DateTime 解决方案

    Unable to convert MySQL date/time value to System.DateTime 解决方案 这个问题发生在MySQL数据里面有Date类型数据,在C#中查询出来时候 ...

  3. Android 开发中遇到Read-only file system问题解决方案

    问题描述: 在往scdcard中复制mp3文件时,复制不成功.查看了一下sdcard里面没有内容,且无法直接在里面创建文件会出现--    read only file system类似的内容提示. ...

  4. Sql server 打不开了,无法识别的配置节 system.serviceModel 解决方案

    异常描述: System.Configuration.ConfigurationErrorsException: 配置系统未能初始化 ---> System.Configuration.Conf ...

  5. .netcore中无法使用System.Drawing --解决方案

    问题重现: 无法正常使用  解决方法: 安装System.Drawing.Common的NuGet就能正常使用了 操作之后: 这个是.netcoe中的解决办法,.net framework解决方案中添 ...

  6. Rvm 进行gem安装时必须输入密码Your user account isn't allowed to install to the system RubyGems 解决方案

    今天开发过程中,从master拉下代码后重启项目,想用控制台时,却发现需要密码??并且三次密码确认后还是疯狂报错. 当时第一想到是rvm版本不一致,随即则检查了版本跟gem生成,当确认rvm版本无误时 ...

  7. log4j:WARN Please initialize the log4j system properly.解决方案

    在使用quarz任务调度框架时的错误,实际上这个问题很常见,并不影响程序的使用,只是缺少日志输出,完整错误信息: log4j:WARN No appenders could be found for ...

  8. 关于read only file system问题解决方案

    切换到超级用户sudo -sadb kill-serveradb rebootadb remount

  9. 开机出现loading Operating System的解决方案

    今天清理机箱之后开机发现电脑屏幕出现以下界面,提示的内容是"正在加载操作系统,磁盘启动失败,请插入系统盘..",出现这种状况的原因有以下几种: 1.主引导的扇区的损坏或者信息的错乱 ...

随机推荐

  1. java.lang.NoSuchFieldError:INSTANCE

    Java.lang.NoSuchFieldError: INSTANCE异常,可能是包重复了. 我遇到的情况是maven里引入了一个JAR,而我又在lib里面引入了这个jar,并且版本还不相同,就出了 ...

  2. [转载]使用java.lang.Process类的简单例子

    FROM: http://segmentfault.com/blog/lidonghao/1190000000372192 ProcessBuilder类是J2SE 1.5在java.lang中新添加 ...

  3. SQL语句练习手册--第四篇

    一.变量那点事儿 1.1 局部变量 (1)声明局部变量 DECLARE @变量名 数据类型 ) DECLARE @id int (2)为变量赋值 SET @变量名 =值 --set用于普通的赋值 SE ...

  4. SQL语句练习手册--第三篇

    一.CASE的两种用法 1.1 等值判断->相当于switch case (1)具体用法模板: CASE expression WHEN value1 THEN returnvalue1 WHE ...

  5. UIScrollView翻书效果

    代码地址如下:http://www.demodashi.com/demo/12695.html 前言:看到凤凰新闻 头条栏目的编辑推荐新闻是这个效果,觉得不错,就想着实现一下,以下就是我的实现过程. ...

  6. Hive优化策略

    hive优化目标 在有限的资源下,运行效率高. 常见问题 数据倾斜.Map数设置.Reduce数设置等 hive运行 查看运行计划 explain [extended] hql 例子 explain ...

  7. 固态硬盘(SSD) 和机 械硬盘(HDD) 优缺点比較

    Attribute SSD (Solid State Drive) HDD (Hard Disk Drive) Power Draw / Battery Life (功耗/电池寿命) Less pow ...

  8. different between method and function

    A method is on an object. A function is independent of an object. For Java, there are only methods. ...

  9. Qemu线程池介绍

    有时我们希望把一部分工作通过创建线程的方式异步执行,这样我们可以在执行任务的同时,继续执行其他任务.但是如果这种需求比较多的话,频繁的创建和销毁线程带来很大的性能损耗.如果我们能创建一个或一些线程,然 ...

  10. 【Hadoop基础教程】1、Hadoop之服务器基础环境搭建(转)

    本blog以K-Master服务器基础环境配置为例分别演示用户配置.sudo权限配置.网路配置.关闭防火墙.安装JDK工具等.用户需参照以下步骤完成KVMSlave1~KVMSlave3服务器的基础环 ...