【Emit】关于System.MethodAccessException解决方案
最近学习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解决方案的更多相关文章
- [C#] 解决Silverlight反射安全关键(SecuritySafeCritical)时报“System.MethodAccessException: 安全透明方法 XXX 无法使用反射访问”的问题
作者: zyl910 一.缘由 在Silverlight中使用反射动态访问时,经常遇到"System.MethodAccessException: 安全透明方法 XXX 无法使用反射访问-- ...
- MySQL Unable to convert MySQL datetime value to System.DateTime 解决方案
Unable to convert MySQL date/time value to System.DateTime 解决方案 这个问题发生在MySQL数据里面有Date类型数据,在C#中查询出来时候 ...
- Android 开发中遇到Read-only file system问题解决方案
问题描述: 在往scdcard中复制mp3文件时,复制不成功.查看了一下sdcard里面没有内容,且无法直接在里面创建文件会出现-- read only file system类似的内容提示. ...
- Sql server 打不开了,无法识别的配置节 system.serviceModel 解决方案
异常描述: System.Configuration.ConfigurationErrorsException: 配置系统未能初始化 ---> System.Configuration.Conf ...
- .netcore中无法使用System.Drawing --解决方案
问题重现: 无法正常使用 解决方法: 安装System.Drawing.Common的NuGet就能正常使用了 操作之后: 这个是.netcoe中的解决办法,.net framework解决方案中添 ...
- Rvm 进行gem安装时必须输入密码Your user account isn't allowed to install to the system RubyGems 解决方案
今天开发过程中,从master拉下代码后重启项目,想用控制台时,却发现需要密码??并且三次密码确认后还是疯狂报错. 当时第一想到是rvm版本不一致,随即则检查了版本跟gem生成,当确认rvm版本无误时 ...
- log4j:WARN Please initialize the log4j system properly.解决方案
在使用quarz任务调度框架时的错误,实际上这个问题很常见,并不影响程序的使用,只是缺少日志输出,完整错误信息: log4j:WARN No appenders could be found for ...
- 关于read only file system问题解决方案
切换到超级用户sudo -sadb kill-serveradb rebootadb remount
- 开机出现loading Operating System的解决方案
今天清理机箱之后开机发现电脑屏幕出现以下界面,提示的内容是"正在加载操作系统,磁盘启动失败,请插入系统盘..",出现这种状况的原因有以下几种: 1.主引导的扇区的损坏或者信息的错乱 ...
随机推荐
- django模型manager学习记录
Managers 在语句Book.objects.all()中,objects是一个特殊的属性,需要通过它查询数据库. 在第5章,我们只是简要地说这是模块的manager .现在是时候深入了解mana ...
- 在eclipse中使用Lombok
1.下载Lombok.jar http://projectlombok.googlecode.com/files/lombok.jar2.运行Lombok.jar: java -jar D:\001 ...
- yarn Fairscheduler与Capacityscheduler
Capacityscheduler Capacityscheduler允许多个组织共享整个集群,每个组织可以获得集群的一部分计算能力.通过为每个组织分配专门的队列,然后再为每个队列分配一定的集群资源, ...
- [转载]Elasticsearch Java API总汇
from: http://blog.csdn.net/changong28/article/details/38445805#comments 3.1 集群的连接 3.1.1 作为Elasticsea ...
- ajty
] .Tag //; ].Columns["NodeID"].ColumnName ; ].Tag)["NodeID"]; //(DataRow)row[&qu ...
- PS 如何附加增效工具.
编辑-首选项-增效工具,选择目标增效工具文件夹
- Laravel之缓存
一.默认缓存的配置 缓存配置位于config/cache.php,你可以使用memcache,redis,数据库缓存,文件缓存等.默认是文件缓存 二.缓存获取 1.获取 $value = Cache: ...
- Linux非阻塞IO(五)使用poll实现非阻塞的回射服务器客户端
前面几节我们讨论了非阻塞IO的基本概念.Buffer的设计以及非阻塞connect的实现,现在我们使用它们来完成客户端的编写. 我们在http://www.cnblogs.com/inevermore ...
- Makefile.am, Makefile.in 与 Makefile的关系(转)
文章出处:http://blog.mcuol.com/User/wangguangdong/Article/17384_1.htm Makefile.am, Makefile.in, Makefile ...
- 优化MyDb
import pymysqlclass MyDb(object): #新式类 def __del__(self):#析构函数 self.cur.close() self.coon.close() pr ...