Bre*_*ose 11 .net c# reflection cil
我正在用C#编写一个简单的桌面客户端/服务器应用程序.出于自学目的,我为通过tcp/ip套接字连接在两个应用程序之间来回发送的消息(定义为类)构建了自己的序列化系统.系统在初始化时使用反射,通过发出IL来为每种消息类型构造序列化/反序列化方法.
该系统的第一个版本使用DynamicMethod,将true传递给构造函数以允许生成的IL(在消息类型中的任意字段上操作)忽略访问权限.这很有效,人们欢欣鼓舞,但我不满意不透明的调试结果函数是多么痛苦.所以我决定放弃DynamicMethod并使用*Builder类构建一个动态程序集,我可以选择将其保存到磁盘并使用.NET Reflector等工具进行检查.
我重构了系统,然后打了一个砖墙.每当其中一个新的序列化函数尝试访问我的某个消息类型中的私有字段或方法时,我都会收到FieldAccessException或MethodAccessException.经过大量谷歌搜索和咬牙切齿之后,我想我已经将问题缩小到一个权限; 特别是我认为我动态创建的程序集缺少相对于调用/构造程序集的ReflectionPermissionFlag.MemberAccess权限(所有反射类型都在这里).
不幸的是,我似乎无法弄清楚如何修改动态程序集创建过程,以便程序集具有反射权限回到创建程序集中.DefineDynamicAssembly的权限参数似乎与限制权限相关,而不是授予权限,这使我们留下了Evidence参数.证据似乎神奇地转化为一组权限,但我找不到任何有用的例子或解释这是如何发生的.
所以我的问题是:
(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 );
Run Code Online (Sandbox Code Playgroud)
请注意,我的项目是针对.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;
}
Run Code Online (Sandbox Code Playgroud)
然后我的序列化系统,在初始化反射期间遇到这种情况时,大致会以类型= 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 );
Run Code Online (Sandbox Code Playgroud)
随后执行该方法时,将在Ldfld指令上抛出异常.
编辑:更详细的证明我所要求的应该是可能的.获取上面的代码片段,但将MethodBuilder替换为DynamicMethod:
DynamicMethod serialization_builder = new DynamicMethod( "Serialize_" + type.Name, null, new [] { type, typeof( BinaryWriter ) }, true );
Run Code Online (Sandbox Code Playgroud)
现在从DynamicMethod创建一个委托:
delegate void TestDelegate( CTestMessage, BinaryWriter );
TestDelegate test_delegate = serialization_builder.CreateDelegate( typeof( TestDelegate ) );
Run Code Online (Sandbox Code Playgroud)
该委托获得JITed并正确执行,没有错误:
CTestMessage test_message = new CTestMessage( 5 );
BinaryWriter writer = new BinaryWriter( some_stream );
test_delegate( test_message, writer );
Run Code Online (Sandbox Code Playgroud)
问题是该领域是私有的。如果将其公开,则外部方法可以正常工作。尽管 DynamicMethod 是私有的,但它仍然可以工作,因为 CLR 显然允许模块内私有字段访问 - 从 SSCLI,clsload.cpp@2659:
// pCurrentClass can be NULL in the case of a global function
// pCurrentClass it the point from which we're trying to access something
// pTargetClass is the class containing the member we are trying to access
// dwMemberAccess is the member access within pTargetClass of the member we are trying to access
BOOL ClassLoader::CheckAccess(EEClass *pCurrentClass,
Assembly *pCurrentAssembly,
EEClass *pTargetClass,
Assembly *pTargetAssembly,
DWORD dwMemberAccess)
{
// we're trying to access a member that is contained in the class pTargetClass, so need to
// check if have access to pTargetClass itself from the current point before worry about
// having access to the member within the class
if (! CanAccessClass(pCurrentClass,
pCurrentAssembly,
pTargetClass,
pTargetAssembly))
return FALSE;
if (IsMdPublic(dwMemberAccess))
return TRUE;
// This is module-scope checking, to support C++ file & function statics.
if (IsMdPrivateScope(dwMemberAccess)) {
if (pCurrentClass == NULL)
return FALSE;
_ASSERTE(pTargetClass);
return (pCurrentClass->GetModule() == pTargetClass->GetModule());
}
Run Code Online (Sandbox Code Playgroud)
要从外部访问私有字段,您可能必须使用反射,这几乎达不到目的。
编辑只是为了澄清,您发布的内容使用反射来创建程序集,但是您生成的 IL 不使用反射来访问该字段 - 这是一个普通的旧直接字段访问,它会爆炸,因为目标字段是外部和私有的。您必须发出 IL,它本身使用 Type.GetField().GetValue() ,这是毫无意义的。
| 归档时间: |
|
| 查看次数: |
1850 次 |
| 最近记录: |