C# 向动态创建的程序集授予反射权限

C# 向动态创建的程序集授予反射权限,c#,.net,reflection,cil,C#,.net,Reflection,Cil,我正在用C#编写一个简单的桌面客户机/服务器应用程序。出于自我教育的目的,我为通过tcp/ip套接字连接在两个应用程序之间来回发送的消息(定义为类)构建了自己的序列化系统。系统在初始化时使用反射,通过发出IL为每种消息类型构造序列化/反序列化方法 该系统的第一个版本使用DynamicMethod,将true传递给构造函数,以允许生成的IL(在消息类型的任意字段上操作)忽略访问权限。这是可行的,人们都很高兴,但我对调试结果函数的不透明性感到不满。因此,我决定放弃DynamicMethod,使用*B

我正在用C#编写一个简单的桌面客户机/服务器应用程序。出于自我教育的目的,我为通过tcp/ip套接字连接在两个应用程序之间来回发送的消息(定义为类)构建了自己的序列化系统。系统在初始化时使用反射,通过发出IL为每种消息类型构造序列化/反序列化方法

该系统的第一个版本使用DynamicMethod,将true传递给构造函数,以允许生成的IL(在消息类型的任意字段上操作)忽略访问权限。这是可行的,人们都很高兴,但我对调试结果函数的不透明性感到不满。因此,我决定放弃DynamicMethod,使用*Builder类构建一个动态程序集,我可以选择将其保存到磁盘,并使用.NET Reflector之类的工具进行检查

我重构了系统,然后遇到了一些麻烦。每当一个新的序列化函数试图访问某个消息类型中的私有字段或方法时,我都会收到FieldAccessException或MethodAccessException。在谷歌搜索和咬牙切齿之后,我想我已经把问题缩小到了权限问题;特别是,我认为动态创建的程序集缺少ReflectionPermissionFlag.MemberAccess权限,该权限与调用/构造程序集(其中包含所有反射类型)相关

不幸的是,我似乎不知道如何修改动态程序集创建过程,使程序集具有反射权限返回到创建程序集中。DefinedDynamicAssembly的权限参数似乎与限制权限相关,而不是授予权限,这就给我们留下了证据参数。证据似乎神奇地转化为一组权限,但我找不到任何有用的例子或解释来解释这是如何发生的

因此,我的问题是:

(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 );
请注意,我的项目的目标是.NET3.5;文档声称.NET 4.0使用了不同的安全性概念,并反对DefinedDynamicAssembly中基于证据/证据的方法

举一个具体的例子,假设我有一个类:

[NetworkMessage]
public class CTestMessage
{
    public CTestMessage( int cheeseburgers )
    {
        m_CheeseBurgers = cheeseburgers
    }

    private int m_CheeseBurgers = 0;
}
然后,当我的序列化系统在init反射过程中遇到这种情况时,将大致地以type=typeof(CTestMessage)执行以下操作(此处不可能进行剪切和粘贴):

随后执行该方法时,会在Ldfld指令上引发异常

编辑:更详细地说明我所要求的应该是可能的。采用上述代码段,但将MethodBuilder替换为DynamicMethod:

DynamicMethod serialization_builder = new DynamicMethod( "Serialize_" + type.Name, null, new [] { type, typeof( BinaryWriter ) }, true );
delegate void TestDelegate( CTestMessage, BinaryWriter );

TestDelegate test_delegate = serialization_builder.CreateDelegate( typeof( TestDelegate ) );
现在从DynamicMethod创建一个委托:

DynamicMethod serialization_builder = new DynamicMethod( "Serialize_" + type.Name, null, new [] { type, typeof( BinaryWriter ) }, true );
delegate void TestDelegate( CTestMessage, BinaryWriter );

TestDelegate test_delegate = serialization_builder.CreateDelegate( typeof( TestDelegate ) );
此委托将获得JIT并正确执行,没有错误:

CTestMessage test_message = new CTestMessage( 5 );
BinaryWriter writer = new BinaryWriter( some_stream );
test_delegate( test_message, writer );

问题是这个字段是私有的。如果将其公开,则外部方法可以正常工作。尽管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());
    }
要从外部访问私有字段,您可能必须使用反射,这在很大程度上违背了目的


编辑只是为了澄清一下,您发布的内容使用反射来创建程序集,但是您生成的IL不使用反射来访问字段-这是一个普通的旧的直接字段访问,由于目标字段是外部的和私有的,因此会爆炸。您必须发出IL,它本身使用Type.GetField().GetValue(),这是毫无意义的。

是的,动态程序集不允许这样的访问,无论是在.NET 3.5还是4+中。我也有同样的问题。我的解决方法是将实际发出IL的代码分解成一个函数,使用ILGenerator,并使用其他相同的参数调用它两次,一次(可选)使用动态程序集中的方法中的ILGenerator,一次(可选)使用保存到磁盘的peverify/ildasm/等,一次使用DynamicMethod中的ILGenerator。通过这种方式,相同的IL被发射到两种方法中

谢谢你的回答。在进一步挖掘之后,我发现了这个MSDN链接:它似乎明确表示动态程序集不能具有非公共反射权限。真倒霉