IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C# Discussion :

génération d'IL (emit) et BadImageFormatException


Sujet :

C#

  1. #1
    Expert éminent sénior Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 154
    Points : 25 072
    Points
    25 072
    Par défaut génération d'IL (emit) et BadImageFormatException
    Bonjour,

    Je me lance dans l'IL.
    Je génère un type, avec un constructeur et une méthode, le but étant de créer une classe implémentant une interface et de rediriger les appels aux méthodes implémentées

    J'arrive bien a créer le type, j'arrive à appeler le constructeur qui remplie des variables privées avec les paramètres.
    Par contre quand j'appelle la méthode générée j'obtiens
    Tentative de chargement d’un programme de format incorrect. (Exception de HRESULT : 0x8007000B)

    environnement
    dll en .net standard 2.0
    exe en .net framework 4.7.2

    je ne maitrise pas l'IL donc j'ai écris une méthode pour un cas donné et j'ai regardé avec JustDecompile l'IL généré, tenté de le comprendre et de l'adapter à mon cas.
    Je dois implémenter une interface, donc je fais un for each sur les méthodes de l'interface et j'essaye de créer la méthode avec le code de redirection
    les méthodes pouvant avoir des paramètres divers et ont un retour de type Task<T>


    signature de la méthode générique que je veux appeler par tout les implémenteurs :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    internal async static Task<T> MethodeGenerique <T>(ClasseQuiSaitGerer c, string interfaceName, string methodName, string parametersSignature, params object[] parameters)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
     
       foreach (var m in type.GetMethods(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance))
                    {
                        var mthBuilder = typeBuilder.DefineMethod(m.Name, MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.Final | MethodAttributes.NewSlot, m.ReturnType, m.GetParameters().Select(pi => pi.ParameterType).ToArray()); // ici j'ai essayé aussi sans final newslot et hide
     
                        var mthIl = mthBuilder.GetILGenerator();
                        var nbParameters = m.GetParameters().Count();
                        mthIl.Emit(OpCodes.Ldarg, 0);                 
                        mthIl.Emit(OpCodes.Ldfld, fieldBuilder); // ma variable privée (_c)
                        mthIl.Emit(OpCodes.Ldstr, interfaceName);
                        mthIl.Emit(OpCodes.Ldstr, m.Name);
                        mthIl.Emit(OpCodes.Ldc_I4, nbParameters);
                        mthIl.Emit(OpCodes.Newarr, typeof(object));
                        var parametersSignature = "";
                        for (int i = 0; i < nbParameters; i++)
                        {
                            mthIl.Emit(OpCodes.Dup); // le Dup j'ai pas trop compris ce qu'il fait, et je sais pas s'il va là ou après le Stelemtn_Ref, j'ai essayé les 2
                            mthIl.Emit(OpCodes.Ldc_I4, i);
                            mthIl.Emit(OpCodes.Ldarg, i + 1);
                            mthIl.Emit(OpCodes.Stelem_Ref);
                            parametersSignature += m.GetParameters()[i].ParameterType.FullName + "||";
                        }
                        if (parametersSignature.Length > 0) parametersSignature.RemoveEndingChars(2); // supression du || en trop à la fin
     
                        mthIl.Emit(OpCodes.Ldstr, parametersSignature);
                        MethodInfo mthToCall = typeof(ClasseDeRedirection).GetMethod(nameof(ClasseDeRedirection.MethodeGenerique), BindingFlags.NonPublic | BindingFlags.Static);
     
                        mthIl.EmitCall(OpCodes.Call, mthToCall, null);
                        mthIl.Emit(OpCodes.Ret);
                        typeBuilder.DefineMethodOverride(mthBuilder, m);
                    }
    question subsidiaire, j'ai cru voir qu'avant le Stelem_Ref il y a parfois un box quand c'est des valuetype et que nullable<bool> avait un box aussi (!?)
    mais ce n'est pas mon cas dans mon 1er essai, la seule méthode de l'interface étant Task<bool> Test1 (Class1 c);question subsidiaire 2, sur les opcode parfois ca parle de push sur la stack et parfois de l'evaluation stack, ce sont les même stack ou 2 différentes ?
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  2. #2
    Expert éminent sénior Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 154
    Points : 25 072
    Points
    25 072
    Par défaut
    pour info si quelqu'un tombe ici un jour, j'ai trouvé les différents problèmes de mon code

    apparemment il faut qu'à la fin de la méthode il n'y ait plus rien sur la pile (à part le truc qu'on return), l'instruction pop sert à retirer un truc sans s'en servir

    et je tentais d'appeler une méthode static générique, le générique nécessite de faire un MakeGeneric*, mais au final quand j'ai réussi
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  3. #3
    Expert confirmé
    Inscrit en
    Avril 2008
    Messages
    2 564
    Détails du profil
    Informations personnelles :
    Âge : 64

    Informations forums :
    Inscription : Avril 2008
    Messages : 2 564
    Points : 4 441
    Points
    4 441
    Par défaut
    bonjour

    Effectivement pour :
    1/construire un type generique :
    - construire le type -t- à partir de TypeBuilder
    - construire son type generique à partir de -t- avec t.MakeGeneric(new Type [] {T1,T2,etc...)
    2/ construire un method generique :
    - construire le MethodInfo -m- et
    - construire le method generique à partir de -m- avec m.MakeGeneric()

    code behind.cs :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
     exemple, pour la methode generique:
    public static T Echo<T> (T value)
    {
    return value;
    }
    code IL equivalent:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    TypeBuilder tb = modBuilder.DefineType ("Widget", TypeAttributes.Public);
    MethodBuilder mb = tb.DefineMethod ("Echo", MethodAttributes.Public |
    MethodAttributes.Static);
    GenericTypeParameterBuilder[] genericParams
    = mb.DefineGenericParameters ("T");
    mb.SetSignature (genericParams[0], // Return type
    null, null,
    genericParams, // Parameter types
    null, null);
    mb.DefineParameter (1, ParameterAttributes.None, "value"); // Optional
    ILGenerator gen = mb.GetILGenerator();
    gen.Emit (OpCodes.Ldarg_0);
    gen.Emit (OpCodes.Ret);
    Quant à la pile il y en a une seule
    -OpCode.Ret renvoie le dernier argument evalué sur la pile (null pour un appel de method void et non null pour un function)
    Si on veut renvoyer autre chose il faut le charger en haut de la pile avec ldc pour le renvoyer.
    Pour l'op code Dup: Copies the current topmost value on the evaluation stack, and then pushes the copy onto the evaluation stack. selon MSDN lib.

    bon code...

  4. #4
    Expert éminent sénior Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 154
    Points : 25 072
    Points
    25 072
    Par défaut
    Citation Envoyé par MABROUKI Voir le message
    Pour l'op code Dup: Copies the current topmost value on the evaluation stack, and then pushes the copy onto the evaluation stack. selon MSDN lib.
    oui j'avais bien trouvé ça, mais je comprenais pas trop l'utilité de dupliquer quelque chose ... avant de comprendre qu'un appel depop tout ce qu'il a besoin, et que si on en a encore besoin après il vaut mieux le repush
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [Débutant] génération d'un EJB avec GenIc
    Par Stessy dans le forum JOnAS
    Réponses: 65
    Dernier message: 31/01/2005, 10h50
  2. [UML] génération de code avec omondo.uml
    Par RENAULT dans le forum Eclipse Java
    Réponses: 3
    Dernier message: 31/10/2003, 13h14
  3. Génération programmatique d'un UUID
    Par jIdJo dans le forum MFC
    Réponses: 2
    Dernier message: 18/07/2003, 02h50
  4. [Lomboz] Génération de code pour EJB
    Par paikan dans le forum Eclipse Java
    Réponses: 2
    Dernier message: 09/07/2003, 14h28

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo