2011-03-19 5 views
9

J'écris une simple application client/serveur de bureau en C#. À des fins d'auto-éducation, j'ai construit mon propre système de sérialisation pour les messages (définis comme des classes) envoyés entre les deux applications via une connexion TCP/IP. Le système utilise la réflexion au moment de l'initialisation pour construire des méthodes de sérialisation/désérialisation pour chaque type de message en émettant IL.Octroi d'une autorisation de réflexion à un assemblage créé dynamiquement

La première version de ce système utilisait DynamicMethod, en transmettant true au constructeur pour permettre à l'IL généré (qui opère sur des champs arbitraires dans le type de message) d'ignorer les permissions d'accès. Cela a fonctionné et les gens se sont réjouis, mais je n'étais pas satisfait de la façon dont le débogage des fonctions était douloureusement opaque. J'ai donc décidé d'abandonner DynamicMethod et d'utiliser les classes * Builder pour construire un assemblage dynamique que je pourrais éventuellement enregistrer sur disque et examiner avec un outil tel que .NET Reflector.

J'ai refaçonné le système puis j'ai frappé un peu d'un mur de briques. Chaque fois qu'une des nouvelles fonctions de sérialisation tente d'accéder à un champ ou à une méthode privée dans l'un de mes types de message, j'obtiens une exception FieldAccessException ou MethodAccessException. Après beaucoup de googler et de grincements de dents, je pense que j'ai réduit le problème à l'un des permissions; Je pense en particulier que mon assembly créé dynamiquement ne possède pas l'autorisation ReflectionPermissionFlag.MemberAccess par rapport à l'assembly appelant/constructeur (où tous les types reflétés sont assis).

Malheureusement, je ne parviens pas à comprendre comment modifier le processus de création d'un assemblage dynamique de sorte que l'assemblage dispose de droits de réflexion dans l'assemblage de création. Les paramètres d'autorisation de DefineDynamicAssembly semblent liés à la restriction de l'autorisation, ne l'accordant pas, ce qui nous laisse avec le paramètre Evidence. Les preuves semblent se traduire par magie en un ensemble d'autorisations, mais je ne trouve pas d'exemples ou d'explications utiles sur la façon dont cela se produit.

Mes questions sont les suivantes:

(1) Ai-je raison dans mon hypothèse que mon problème est un manque d'autorisation sur mon assemblage dynamique créé? (2) Si oui, comment puis-je, en tant qu'assemblage appelant, accorder l'autorisation nécessaire à mon assembly dynamique?

Le code actuel de création dynamique de l'assemblage:

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

Notez que mon projet cible .NET 3.5; les revendications de documentation .NET 4.0 utilise une notion de sécurité différente et déprécie les méthodes basées sur Evidence/PemissionSet dans DefineDynamicAssembly.

Pour donner un exemple concret, supposons que j'avais une classe comme:

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

    private int m_CheeseBurgers = 0; 
} 

alors mon système de sérialisation, lors de la rencontre ce lors de la réflexion init, serait environ finissent par faire (couper et de coller isnt possible ici) ce qui suit avec le 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); 

Lorsque la méthode est ensuite exécutée, l'exception est levée sur la Instruc Ldfld tion.

Modifier: Plus de détails démontrant que ce que je demande devrait être possible.Prenez l'extrait de code ci-dessus, mais remplacez MethodBuilder avec DynamicMethod:

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

Maintenant, créez un délégué de la DynamicMethod:

delegate void TestDelegate(CTestMessage, BinaryWriter); 

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

Ce délégué obtient JITed et exécute correctement sans erreur:

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

Répondre

3

Le problème est que le champ est privé. Si vous le rendez public, la méthode externe fonctionne bien. Le DynamicMethod fonctionne bien qu'il soit privé parce que le CLR permet apparemment champ privé intra-module d'accès - du 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()); 
    } 

Pour accéder aux champs privés à l'extérieur, vous avez probablement utiliser la réflexion qui à peu près défait le but. Pour clarifier, ce que vous avez posté utilise la réflexion pour créer l'assemblage, mais l'IL que vous générez n'utilise pas la réflexion pour accéder au champ - c'est un simple accès direct au champ, qui explose parce que le champ cible est externe et privé. Vous devriez émettre IL qui utilise lui-même Type.GetField(). GetValue() qui est assez inutile.

+0

Merci pour la réponse. Après avoir creusé un peu plus, je suis tombé sur ce lien MSDN: [link] (http://msdn.microsoft.com/fr-fr/library/9syytdak.aspx) qui semble dire plat que les assemblées dynamiques ne peuvent pas avoir autorisations de réflexion publique. Bummer. –

0

Oui, les assemblages dynamiques n'autorisent pas un tel accès, que ce soit dans .NET 3.5 ou 4+. J'ai eu le même problème. Ma solution de contournement consiste à décomposer le code émetteur IL en une fonction prenant ILGenerator et à l'appeler deux fois avec les autres arguments de la même manière, une fois (éventuellement) avec un ILGenerator d'une méthode dans un assembly dynamique que je sauvegarderais sur le disque. peverify/ildasm/etc. et une fois avec un ILGenerator d'un DynamicMethod. De cette façon, IL identique est émis dans les deux méthodes.

Questions connexes