【发布时间】:2011-07-18 22:37:57
【问题描述】:
我正在用 C# 编写一个简单的桌面客户端/服务器应用程序。出于自学目的,我为通过 tcp/ip 套接字连接在两个应用程序之间来回发送的消息(定义为类)构建了自己的序列化系统。系统在初始化时使用反射,通过发出IL,为每种消息类型构造序列化/反序列化方法。
该系统的第一个版本使用 DynamicMethod,将 true 传递给构造函数以允许生成的 IL(对消息类型中的任意字段进行操作)忽略访问权限。这奏效了,人们很高兴,但我对调试结果函数的不透明程度感到不满意。所以我决定放弃 DynamicMethod 并使用 *Builder 类来构造一个动态程序集,我可以选择将其保存到磁盘并使用 .NET Reflector 之类的工具进行检查。
我重构了系统,然后碰了壁。每当新的序列化函数之一尝试访问我的一种消息类型中的私有字段或方法时,我都会收到 FieldAccessException 或 MethodAccessException。经过多次谷歌搜索和咬牙切齿后,我想我已经将问题缩小到权限之一;特别是我认为我动态创建的程序集缺少相对于调用/构造程序集(所有反射类型所在的位置)的 ReflectionPermissionFlag.MemberAccess 权限。
不幸的是,我似乎无法弄清楚如何修改动态程序集创建过程,以使程序集具有反射回创建程序集的权限。 DefineDynamicAssembly 的权限参数似乎与限制权限有关,而不是授予权限,这给我们留下了证据参数。证据似乎神奇地转化为一组权限,但我找不到任何有用的例子或解释如何发生这种情况。
所以我的问题是:
(1) 我的假设是否正确,即我的问题是我的动态创建的程序集缺少权限?
(2) 如果是这样,作为调用程序集,我如何为我的动态程序集授予必要的权限?
当前动态程序集创建代码:
AssemblyName assembly_name = new AssemblyName( "LCSerialization" );
assembly_name.Version = new Version( 1, 0, 0, 0 );
m_SerializationAssembly = current_domain.DefineDynamicAssembly( assembly_name, AssemblyBuilderAccess.RunAndSave ); // Fix me
m_SerializationModule = m_SerializationAssembly.DefineDynamicModule( "MainModule", "LCSerialization.dll" );
m_SerializationWrapperClass = m_SerializationModule.DefineType( "CSerializationWrapper", TypeAttributes.Public );
请注意,我的项目面向 .NET 3.5;文档声称 .NET 4.0 使用了不同的安全概念,并弃用了 DefineDynamicAssembly 中基于 Evidence/PemissionSet 的方法。
举个具体的例子,假设我有一个类:
[NetworkMessage]
public class CTestMessage
{
public CTestMessage( int cheeseburgers )
{
m_CheeseBurgers = cheeseburgers
}
private int m_CheeseBurgers = 0;
}
然后我的序列化系统在初始化反射期间遇到这种情况时,大致最终会使用 type = typeof 执行以下操作(此处无法剪切和粘贴) (CTestMessage):
MethodBuilder serialization_builder = m_SerializationWrapperClass.DefineMethod( "Serialize_" + type.Name,
MethodAttributes.Public | MethodAttributes.Static,
null,
new [] { type, typeof( BinaryWriter ) } );
ILGenerator s_il_gen = serialization_builder.GetILGenerator();
BindingFlags binding_flags_local_non_static = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly;
s_il_gen.Emit( OpCodes.Ldarg_1 ); // Eval Stack: BinaryWriter
s_il_gen.Emit( OpCodes.Ldarg_0 ); // Eval Stack: BinaryWriter, testmessage
s_il_gen.Emit( OpCodes.Ldfld, type.GetField( "m_CheeseBurgers", binding_flags_local_non_static ) ); // Eval Stack: BinaryWriter, int
s_il_gen.Emit( OpCodes.Callvirt, typeof( BinaryWriter ).GetMethod( "Write", new Type[] { typeof( Int32 ) } ) ); // Eval Stack:
s_il_gen.Emit( OpCodes.Ret );
后续执行该方法时,在Ldfld指令上抛出异常。
编辑:更多细节表明我所要求的应该是可能的。取上面的代码sn-p,但是把MethodBuilder换成DynamicMethod:
DynamicMethod serialization_builder = new DynamicMethod( "Serialize_" + type.Name, null, new [] { type, typeof( BinaryWriter ) }, true );
现在从 DynamicMethod 创建一个委托:
delegate void TestDelegate( CTestMessage, BinaryWriter );
TestDelegate test_delegate = serialization_builder.CreateDelegate( typeof( TestDelegate ) );
这个委托被 JITed 并正确执行,没有错误:
CTestMessage test_message = new CTestMessage( 5 );
BinaryWriter writer = new BinaryWriter( some_stream );
test_delegate( test_message, writer );
【问题讨论】:
标签: c# .net reflection cil