2011-03-19 17 views
9

Estoy escribiendo una aplicación simple de cliente/servidor de escritorio en C#. Para fines de autoeducación, construí mi propio sistema de serialización para los mensajes (definidos como clases) enviados de ida y vuelta entre las dos aplicaciones a través de una conexión de socket tcp/ip. El sistema usa la reflexión en el momento de la inicialización para construir métodos de serializar/deserializar para cada tipo de mensaje emitiendo IL.Concesión de permiso de reflexión a un ensamblaje creado de forma dinámica

La primera versión de este sistema usa DynamicMethod, pasando al constructor para permitir que la IL generada (que está operando en campos arbitrarios en el tipo de mensaje) ignore los permisos de acceso. Esto funcionó y la gente se regocijó, pero no estaba satisfecho con la depuración dolorosamente opaca de las funciones resultantes. Así que decidí abandonar DynamicMethod y usar las clases * Builder para construir un ensamblaje dinámico que opcionalmente podía guardar en el disco y examinar con una herramienta como .NET Reflector.

Refactoreé el sistema y luego golpeé un poco de una pared de ladrillos. Cada vez que una de las nuevas funciones de serialización intenta acceder a un campo o método privado en uno de mis tipos de mensajes, obtengo una excepción FieldAccessException o MethodAccessException. Después de mucho googlear y rechinar de dientes, creo que reduje el problema a uno de los permisos; en particular, creo que a mi ensamblado creado dinámicamente le falta el permiso ReflectionPermissionFlag.MemberAccess relativo al ensamblado de llamada/construcción (donde están sentados todos los tipos reflejados).

Desafortunadamente, parece que no puedo encontrar la manera de modificar el proceso de creación del ensamblaje dinámico de modo que el conjunto tenga permiso de reflejo en el ensamblaje de creación. Los parámetros de permisos para DefineDynamicAssembly parecen estar relacionados con la restricción del permiso, no con la concesión, lo que nos deja con el parámetro Evidencia. La evidencia parece traducirse mágicamente en un conjunto de permisos, pero no encuentro ejemplos ni explicaciones útiles sobre cómo ocurre esto.

Así que mis preguntas son:

(1) Estoy en lo correcto en mi suposición de que mi problema es la falta de permiso en mi montaje creado de forma dinámica?

(2) En caso afirmativo, ¿cómo puedo, como el ensamblado que realiza la llamada, otorgar el permiso necesario para mi ensamblaje dinámico?

El actual código de creación de ensamblaje dinámico:

 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); 

Nota que mi proyecto se dirige a .NET 3.5; la documentación afirma que .NET 4.0 usa una noción diferente de seguridad y desaprueba los métodos basados ​​en Evidence/PemissionSet en DefineDynamicAssembly.

Para dar un ejemplo concreto, supongamos que tenía una clase como:

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

    private int m_CheeseBurgers = 0; 
} 

entonces mi sistema de serialización, al encontrarse con esta reflexión durante la inicialización, sería más o menos terminar haciendo (cut-y- pega isnt posible aquí) lo siguiente con 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); 

Cuando se ejecuta posteriormente el método, la excepción es lanzada en la instruc Ldfld ción.

Editar: Más detalles que demuestren que lo que estoy pidiendo debería ser posible.Tomar el fragmento de código anterior, pero sustituyendo MethodBuilder con DynamicMethod:

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

A continuación, cree un delegado de la DynamicMethod:

delegate void TestDelegate(CTestMessage, BinaryWriter); 

TestDelegate test_delegate = serialization_builder.CreateDelegate(typeof(TestDelegate)); 

Este delegado se JITed y ejecuta correctamente sin errores:

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

Respuesta

3

El problema es que el campo es privado. Si lo haces público, el método externo funciona bien. El DynamicMethod funciona a pesar de ser privado porque el CLR aparentemente permite el acceso de campo privada intra-módulo - de la SSCLI, [email protected]:

// 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()); 
    } 

Para acceder a los campos privados externa es probable que tenga que utilizar la reflexión que prácticamente derrota el propósito.

Edición Solo para aclarar, lo que envió utiliza la reflexión para crear el montaje, pero la IL generas no utilizar la reflexión para acceder al campo - que es una llanura de acceso directa de campo antigua, que explota debido a que el campo de destino es externo y privado. Tendría que emitir IL que usa Type.GetField(). GetValue() que es bastante inútil.

+0

Gracias por la respuesta. Después de excavar un poco más, encontré este enlace de MSDN: [link] (http://msdn.microsoft.com/en-us/library/9syytdak.aspx) que parece decir que los ensambles dinámicos no pueden tener no- permisos de reflexión pública. Gorrón. –

0

Sí, los conjuntos dinámicos no permiten dicho acceso, ya sea en .NET 3.5 o 4+. Yo tuve el mismo problema. Mi solución consiste en dividir el código emisor de IL real en una función que toma un ILGenerator y llamarlo dos veces con los mismos argumentos, una vez (opcionalmente) con un ILGenerator de un método en un ensamblaje dinámico que guardaría en el disco para peverify/ildasm/etc. y una vez con un ILGenerator de DynamicMethod. De esta forma, se emite IL idéntica en ambos métodos.

Cuestiones relacionadas