none
creer une memoire sur un pointeur non type en mode managé RRS feed

  • Question

  • bonjour

    J'ai toujours des problèmes d'allocation mémoire sur mon application.

    J'essaie donc de passer l'allocation mémoire du parser de Brill du mode std au mode managé

    J'ai deux problèmes

    il ne trouve pas un symbole qui est défini juste au dessus de lui. Quelle est la question sous-jacente?

    comment alouer un pointeur sur une mémoire  inconnu de taille définie, sur le tas gc?

    je vous met le code du header:

    ref class memory
    {
    public:
    	VOIDP temp;
    	void bytes;
    	memory::memory();
    	memory::~memory();
    
    	long Memory_unfreed_bytes(NOARGS);
    	static NORET mem_add_to_rgy(temp, bytes);
    	static NORET mem_remove_from_rgy(temp);
    	static int mem_rgy_initialized(NOARGS);
    	VOIDP Memory_allocate(size_t n);
    	VOIDP Memory_reallocate(VOIDP ptr, size_t n);
    };

    puis le code de la primitive

    VOIDP Memory_allocate(size_t n)
    {
      VOIDP temp;
    
      temp = gcnew VOIDP[n];
    
    #ifdef MEMORY_CHECK_POINTERS
      assert(dont_check ||mem_rgy_initialized());
    #endif
    
    
      if (temp == NULL) {
        fprintf(stderr, "Fatal error allocating %d bytes", (int)n);
        abort();
      }
    #ifdef MEMORY_CHECK_POINTERS
      assert(dont_check || (mem_add_to_rgy(temp,n),1));
    #endif
      return temp;
    };

    enfin les diagnostic du compilateur

    1>E:\usr\Memoria\dev\Linguistic\ParserManager\com\memoryM.h(13): error C2182: 'bytes' : utilisation non conforme du type 'void'
    1>E:\usr\Memoria\dev\Linguistic\ParserManager\com\memoryM.h(18): error C2061: erreur de syntaxe : identificateur 'temp'
    1>E:\usr\Memoria\dev\Linguistic\ParserManager\com\memoryM.h(19): error C2061: erreur de syntaxe : identificateur 'temp'
    1>..\..\..\..\Linguistic\ParserManager\src\memoryM.cpp(86): error C2726: 'gcnew' peut uniquement être utilisé pour créer un objet de type managé


    Jean Noël Martin


    vendredi 10 janvier 2014 16:47

Réponses

  • new n'est pas un remplaçant de malloc C en C++, Je me demande comment cette migration c'est déroulée. :-(

    Vous êtes revenu au code initial et il fonction, c'est l'essentiel.

    C'est bien la migration le cœur du problème.

    Mais comme vous nous dites rien sur cette migration, qui en plus est inutile, on ne peut pas vous aider dans cette migration.

    Du code C ne ce transforme pas en C++ juste en changeant les malloc en new et les free en delete, loin de là.


    Paul Bacelar, Ex - MVP VC++

    • Marqué comme réponse JeanNoel53 lundi 20 janvier 2014 07:53
    samedi 18 janvier 2014 18:39
    Modérateur

Toutes les réponses

  • Utiliser .NET C++/CLI pour régler des problèmes en C++ Natif n'est JAMAIS la bonne solution.

    Le tas managé est managé et donc gérer par le framework .NET qui se donne le droit de déplacer librement les objets dans ceS tas comme bon lui semble (libération du ramasse-miettes, déplacement des générations d'objets, compression de mémoire, ...)

    Alors : "comment alouer un pointeur sur une mémoire  inconnu de taille définie", oubliez cette idée ridicule.

    Ca serait sympa de votre par d'indiquer les numéros de lignes correspondant messages d'erreur du compilateur, au minimum.

    Au jeu de la devinette (que je n'apprécie pas vraiment), la ligne 13 c'est vraisemblablement "void bytes;" et en C, en C++, en C++/CLI, en C++11, en D, en ... cela na jamais été autorisé, une variable a un type et "void" n'est pas un type, "void*" est un type mais pas "void"; "void bytes();" est aussi autorisé mais ce n'est plus une variable mais une déclaration de fonction/méthode.

    La suite de ce jeu (stupide),  la ligne 18 est "static NORET mem_add_to_rgy(temp, bytes);", en C++ vous n'êtes pas obliger de donner le nom des paramètre lors de la déclaration d'une fonction/méthode mais on êtes obligé de donner le type des paramètres; bin vous, vous faites l'inverse. Ca compilerait mieux avec "static NORET mem_add_to_rgy(VOIDP temp, LeBonTypeEtPasVOID_ALaNoix bytes);"

    ETC...

    Donc revenons au problème initial, corrigez le code en natif, car le problème n'a rien à voir avec le code managé (et commencez une bonne session de révision des base du langage C++ :-( ).


    Paul Bacelar, Ex - MVP VC++

    samedi 11 janvier 2014 13:52
    Modérateur
  • Je veux bien entendre tout ce que vous dites. Mais je vous rappelle que mon problème est de faire marcher un parser fait par une université, et que j’essaie de minimiser les modifications car refaire le parser de Brill from scratch est trop aventureux.Je veux bien vous documenter sur les constantes utilisées? mais ne m’accusez pas des  défauts du code fait par un autre. Le problème initial est que maintenant que le code est largement managé L'appel du new dans le parser de brill est refusé par le système.

    Je suis donc revenu à l’implémentation standard et j'ai les observations suivantes

    Dans le parser de brill, les demande de mémoire sont :

    Un  bloc de 20 Octets  : OK

    un bloc de 388 Octets : OK

    un bloc de 20 Octets   : OK

    un bloc de 388 Octets : OK

    un bloc de 12 Octets  : KO

    J'ai inséré le code dans un bloc try catch et il renouvelle sa demande jusquà ce qu'elle soit acceptée, en produisant des exceptions

    Je vous donne le code

    VOIDP Memory_allocate(size_t n)
    {
      VOIDP temp;
    
      while( temp == NULL)
      {
    	  try
    	  {
    		temp = new VOIDP[n];
    	  }
    	  catch(...)
    	  {
    		  ;
    	  }
      }
    
    #ifdef MEMORY_CHECK_POINTERS
      assert(dont_check ||mem_rgy_initialized());
    #endif
    
    
      if (temp == NULL) {
        fprintf(stderr, "Fatal error allocating %d bytes", (int)n);
        abort();
      }
    #ifdef MEMORY_CHECK_POINTERS
      assert(dont_check || (mem_add_to_rgy(temp,n),1));
    #endif
      return temp;
    };

    et les traces de l'exécution

    Une exception de première chance de type 'System.Runtime.InteropServices.SEHException' s'est produite dans SpecificationLoader.exe
    Une exception de première chance de type 'System.Runtime.InteropServices.SEHException' s'est produite dans SpecificationLoader.exe
    Une exception de première chance de type 'System.Runtime.InteropServices.SEHException' s'est produite dans SpecificationLoader.exe


    Jean Noël Martin



    • Modifié JeanNoel53 dimanche 12 janvier 2014 09:23
    samedi 11 janvier 2014 15:55
  • "faire marcher un parser fait par une université, et que j’essaie de minimiser les modifications car refaire le parser de Brill from scratch est trop aventureux."

    Et vous pensez sérieusement qu'en faire une classe managée va minimiser les modifications et que c'est moins aventureux ? Sérieusement ?

    "L'appel du new dans le parser de brill est refusé par le système."

    Ne serait-se pas parque vous vous voulez mettre l'objet alloué dans un membre d'une classe managée ou que le code devrait faire partie d'une assemblie .NET pure ? Ce qui serait donc normal.

    Donc revenons à notre code Natif.

    je crois vous avoir déjà dit/écrit de ne pas catcher les exceptions et d'utiliser le débuggeur. Cela n'a pas changé, comme le fait qu'une 1ère chance exception peut être bénigne.

    Le message d'erreur montre que vous êtes en code managé. Commencez par faire marcher cette classe dans un projet C++ standard. Il n'est même pas dit que le code de cette classe soit compatible avec VC++, ni même qu'il soit correcte.


    Paul Bacelar, Ex - MVP VC++

    lundi 13 janvier 2014 01:49
    Modérateur
  • bon,Je tire de ce thread qu'il me faut convertir le parser de Brill en code managé. Je prend un suée avec cette conclusion, mais je m'y attaque.

    Tout ce code std a déjà marché dans la première implémentation. C'est quand j'ai commencé à rajouter du code managé que ça a commencé à ne plus marcher.


    Jean Noël Martin

    lundi 13 janvier 2014 03:09
  • C'est l'inverse, laissez le code Natif en code natif et adaptez votre architecture pour ne pas avoir à le convertir.

    Paul Bacelar, Ex - MVP VC++

    lundi 13 janvier 2014 09:10
    Modérateur
  • soit.

    C'est clair je le garde en code natif.

    Mais alors il faut résoudre les échecs de new: il se produisent quand du code std demande une allocation de mémoire. Ce n'est pas un problème de capacité puisque au moment de l'échec on demande un bloc de 12 octets après avoir allouée environ 500 octets. Ce n'est pas un problème de rapport avec le code managé puisqu'à ce moment l'allocation provient d'un module natif et que la demande concerne des types natifs. D'ou vinet le refus d'allocation?


    Jean Noël Martin

    lundi 13 janvier 2014 12:58
  • Ne catchez pas l'exception et laissez le débuggeur nous indiquer le problème.

    Paul Bacelar, Ex - MVP VC++

    lundi 13 janvier 2014 13:07
    Modérateur
  • ok à suivre je vous mettrai l'architecture..Pour l'instant je vous met le code auquel j'ai retiré le try-catch

    VOIDP Memory_allocate(size_t n)
    {
      VOIDP temp;
    	
      temp = new VOIDP[n];
    #ifdef MEMORY_CHECK_POINTERS
      assert(dont_check ||mem_rgy_initialized());
    #endif
      if (temp == NULL) {
        fprintf(stderr, "Fatal error allocating %d bytes", (int)n);
        abort();
      }
    #ifdef MEMORY_CHECK_POINTERS
      assert(dont_check || (mem_add_to_rgy(temp,n),1));
    #endif
      return temp;
    };

    une fois une allocation refuséee, toutes les autres sont refusées et donc on ne sort pas du parser. si on sort du parser après la première erreur on a des données incohérentes.

    je vous met l'archi ci dessous


    Jean Noël Martin



    lundi 13 janvier 2014 17:40
  • Ok, mais c'est quoi le message, le type et la stack trace de l'exception ???

    Paul Bacelar, Ex - MVP VC++

    lundi 13 janvier 2014 17:57
    Modérateur
  • Une exception de première chance de type 'System.Runtime.InteropServices.SEHException' s'est produite dans SpecificationLoader.exe
    Une exception de première chance de type 'System.Runtime.InteropServices.SEHException' s'est produite dans SpecificationLoader.exe
    Une exception de première chance de type 'System.Runtime.InteropServices.SEHException' s'est produite dans SpecificationLoader.exe
    


    Jean Noël Martin

    lundi 13 janvier 2014 18:02
  • Ceci n'est ni le message d'erreur de l'exception initiale, ni son type ni sa stack trace.

    Le debuggeur est votre ami.


    Paul Bacelar, Ex - MVP VC++

    mardi 14 janvier 2014 08:52
    Modérateur
  • comment récupérer le message d'erreur de l'exception initiale, et le contenu de la stack trace

    Jean Noël Martin

    mardi 14 janvier 2014 09:58
  • Dans le débuggeur

    Paul Bacelar, Ex - MVP VC++

    mardi 14 janvier 2014 10:19
    Modérateur
  • j'ai donc essayé au petit bonheur la chance au plus haut de l'application avec le code suivant:

    catch( SEHException^ e)
    		{
    			std::wstring wsMsg;
    			std::string msg;
    			size_t len;
    			chars = (const wchar_t*)(Marshal::StringToHGlobalUni( e->StackTrace())).ToPointer();
    			wsMsg.assign( chars);
    			Marshal::FreeHGlobal(IntPtr((void*)chars));
    			string text = objStrConv.awcstombs((wchar_t*)wsMsg.c_str());
    			msg = msg + text;
                msg = msg + "\n";
                objStrConv.StrFreeA( (char*)text.c_str());
                // il reste à gèrer la mise en fichier et la libération des ressources (à voir)
                // errno_t fopen_s( FILE** pFile, const char *filename, const char *mode)
    			fopen_s( &fp, sLogFile.c_str(), "a");
                len = fprintf(  fp, msg.c_str());
                fclose( fp);
    		}

    mais le compilateur n'aime pas

    1>..\..\..\..\Linguistic\Analyser\src\schedulerGlobal.cpp(417): error C2064: le terme ne correspond pas à une fonction qui prend 0 arguments
    1>..\..\..\..\Linguistic\Analyser\src\schedulerGlobal.cpp(417): error C2228: la partie gauche de '.ToPointer' doit avoir un class/struct/union
    en rote pour construire la solution j'avais réussi à compiler e->StackTrace


    Jean Noël Martin

    mardi 14 janvier 2014 16:38
  • On recommence : PAS DE CATCH.

    C'est le debuggeur qui permet de décortiquer les informations contenues dans l'exception de manière simple et fiable.

    Lisez la documentation pour utiliser correctement le debuggeur.

    Comme je vous l'ai déjà indiqué, commencez par le faire tourner dans un projet NATIF.

    Puis, quand le problème est réglé en Natif ; puis convertir le projet natif minimum initial en un projet managé minimum ; puis dans le projet managé complet.

    http://www.codeproject.com/Articles/469416/10-More-Visual-Studio-Debugging-Tips-for-Native-De


    Paul Bacelar, Ex - MVP VC++

    mardi 14 janvier 2014 17:13
    Modérateur
  • le résultat sous debugger est le suivant

    Une exception de première chance de type 'System.Runtime.InteropServices.SEHException' s'est produite dans SpecificationLoader.exe
    
    Informations supplémentaires : Un composant externe a levé une exception.

    produit sur le new (12)


    Jean Noël Martin

    mardi 14 janvier 2014 17:33
  • "'System.Runtime.InteropServices.SEHException' " et vous allez me dire que c'est un projet Natif ?

    "new (12)" c'est du C++ ça ?

    Le debuggeur, CE N'EST PAS LA FENETRE "Output/Sortie".

    Le debuggeur indique la ligne du code source, la pile d'appel, la valeur des variables locales, des paramètres des fonctions dans la pile d'appel, etc... , surtout si vous êtes en NATIF.

    Je vous demande de vous renseigniez sur l'utilisation du debuggeur de VS et vous me répondez en 20 minutes, vous lisez vite ( ou vous vous foutez des réponses temps qu'elle ne vous réconforte pas dans vos erreurs).


    Paul Bacelar, Ex - MVP VC++

    mardi 14 janvier 2014 17:54
    Modérateur
  • dans la version 2 du programme ce code marchait sans problème

    C'est depuis que l'essentiel du code est en managé que ça ne marche plus. J'ai eu plusieurs étapes:

    Dans un premier temps j'ai convertit du code venu de java en code std: J'ai eu de nombreux problèmes. non reconnaissance du point d'entrée, refus d'allocation,...

    puis j'ai converti ce code en managé. J'ai eu à ce moment la les premier symptômes de refus d'allocation.

    J'ai fais l'hypothèse que c'était une limite technologique du compilateur et j'ai migré l'essentiel du code( 25 000 LOC) en code managé. ça a à peine amélioré la situation: avant ça plantait au premier new, après ça plantait un 5° new

    le code qui plante c'est la ligne temp = new VOIDP[n]; avec n = 12;


    Jean Noël Martin


    • Modifié JeanNoel53 mercredi 15 janvier 2014 09:56
    mercredi 15 janvier 2014 08:25
  • j'ai essayé $err et  $exception sans succes

    Jean Noël Martin

    mercredi 15 janvier 2014 09:43
  • >dans la version 2 du programme ce code marchait sans problème

    Comme je ne sais pas ce qu'est la version 2, c'est du chinois pour moi.

    >Dans un premier temps j'ai convertit du code venu de java en code std: J'ai eu de nombreux problèmes. non reconnaissance du point d'entrée, refus d'allocation,...

    Vous avez eu donc le même problème en C++ STD, comment l'avez-vous corrigé ? Il est très probable que, soit vous avez oublié de l'appliquer sur la version Native, soit que votre correction n'en soit pas une et que le passage en managé n'a fait que la révéler.

    C'est pour cela que j'ai demandé de commencé par corrigé le code en C++ STD. Montrez-nous votre "correction".

    >puis j'ai converti ce code en managé. J'ai eu à ce moment la les premier symptômes de refus d'allocation.

    C'est pas ce qui nous intéresse ici ???

    >J'ai fais l'hypothèse que c'était une limite technologique du compilateur et j'ai migré l'essentiel du code( 25 000 LOC) en code managé.

    Je ne comprend pas, la limitation de quel compilateur ???

    > ça a à peine amélioré la situation: avant ça plantait au premier new, après ça plantait un 5° new

    C'est donc que votre correction n'en est pas une. C'est quoi cette correction/modification ??

    >le code qui plante c'est la ligne temp = new VOIDP[n];

    OK, mais c'est quoi le message d'erreur du débuggeur ? (En Natif, il est vraisemblablement plus explicite pour un non initié)

    C'est quoi un VOIDP ?

    Si c'est une structure de plusieurs centaine de ko, c'est assez logique.

    Si c'est un "void*", c'est vraisemblablement les allocations antérieurs qui ont saturée la mémoire du processus, dans l'hypothèse qur c'est bien une "memory starvation".

    Mais cela peut très bien être un message d'alerte de la corruption des structure mémoire du tas, dont la vérification est faite systématiquement ou non systématiquement lors de nouvelle allocation.

    On en revient toujours au même, faire une version C++ STD et avoir le VRAI message d'erreur du débuggeur.

    Si vous ne maitrisez pas le debugger, investissez plusieurs jours sur sa maitrise, c'est un minimum.


    Paul Bacelar, Ex - MVP VC++

    mercredi 15 janvier 2014 17:31
    Modérateur
  • J'avance dans la compréhension du problème:

    d’abord ce qui ne se passe pas:

    ce n'est pas un problème de ressource: j'ai alloué un bloc de taille moyenne d'un seul tenant au début de la procédure: ça se passe bien

    ensuite ce qui se passe:

    je gère les allocation à la main( pour comprendre ce qui se passe

    les premières allocations se passent bien ( 1 à 4)

    la 5° allocation se fait, mais quand on cherche à écrire dans le bloc alloué on sort en exception accès à une mémoire réservée, et pourtant le bloc a été alloué???

    La suite au prochain numéro:

    la précédente mémoire allouée est à l'adresse 3CC180. la  mémoire qui pose problème est allouée à 3CC680

    la mémoire allouée quand on veut y accéder avec le debugger affiche impossible d'accéder à l'expression.

    à la première tentative d'écriture dans la mémoire j'obtient:

    Une exception de première chance de type 'System.AccessViolationException' s'est produite dans SpecificationLoader.exe
    
    Informations supplémentaires : Tentative de lecture ou d'écriture de mémoire protégée. Cela indique souvent qu'une autre mémoire est endommagée.

    la suite au prochain numéro. En fait à ce moment il y avait une erreur dans la gestion de la mémoire. cette erreur corrigée l'allocation réussit.

    La suite au prochain numéro


    Jean Noël Martin




    • Modifié JeanNoel53 mercredi 15 janvier 2014 22:25
    mercredi 15 janvier 2014 21:49
  • >ce n'est pas un problème de ressource: j'ai alloué un bloc de taille moyenne d'un seul tenant au début de la procédure: ça se passe bien

    Et ?

    Si vous avez 1Go de ligne, la première allocation de 1Go fonctionne, mais l'allocation de 1 octet après ne fonctionnera pas.

    >je gère les allocation à la main

    Pouvez-vous nous donnez les détails de cette allocation à la main ? svp

    Si vous gérez vous même le mapping mémoire avec de la réservation puis de l'allocation, c'est de genre de petit détail qu'il n'est pas inutile de mentionner. :-(((

    >la mémoire allouée quand on veut y accéder avec le debugger affiche impossible d'accéder à l'expression.

    C'est que l'adresse mémoire utilisée n'est même pas mappé dans l'espace d'adressage du processus. Cette adresse est foireuse, qui fournit cette adresse ? Et ne me dites pas que c'est l' appel système "malloc".

    Je pense que vous avez les mains sales ("je gère les allocation à la main").


    Paul Bacelar, Ex - MVP VC++

    jeudi 16 janvier 2014 10:05
    Modérateur
  • ????????????????

    Jean Noël Martin

    jeudi 16 janvier 2014 13:32
  • Donnez nous le code qui a retourné la valeur "3CC680" comme valeur d'un pointeur sur de la mémoire mappée dans le processus et que le débuggeur signale comme non mappée.

    Le débuggeur en mode pas à pas permet de suivre les appels et donc de savoir qui retour cette valeur foireuse.

    Faites une copie d'écran du débuggeur lors de l'incident car je suspecte que vous lisiez incorrectement les indications de celui-ci.


    Paul Bacelar, Ex - MVP VC++

    jeudi 16 janvier 2014 13:47
    Modérateur
  • bonjour

    J'ai avancé sur le debug et j'ai des données qui semble montrer des limites technologiques.

    Si je demande une allocation de 12000 octets, qui sont mon besoin je reçois un bloc de 2100 octets

    si je demande plusieurs blocs de 2100 octets, le système me refuse plus de 9 new successif. plus encore il ne permet d'allouer que 2 blocs sans cela les fopen se passent mal

    si ces limitations sont avérées je m’arrête. je vais être mis à la retraite dans 4 mois et je n'ai pas besoin de finir ce projet. J’attends des confirmation des limites technologiques ou des infirmations.


    Jean Noël Martin

    jeudi 16 janvier 2014 17:44
  • Et la petite marmotte met le chocolat dans le papier d'alu.

    http://www.youtube.com/watch?v=_Qg3Rk-B09o

    Si c'est vraiment une limitation technique/technologiques, il serait évidant de faire un projet minimum qui le reproduise. J'attends avec impatience ce projet VS.

    Vous êtes toujours très nébuleux dans vos "explications", vous ne citez jamais les primitives qui vous utilisez ni comment vous récupérez/lisez les valeurs qui sous-tendent vos "raisonnements".

    Voici une copie d'écran montant que le malloc de 12000 octets marche comme un charme, même en debug.


    Paul Bacelar, Ex - MVP VC++

    jeudi 16 janvier 2014 18:38
    Modérateur
  • le malloc ou le new?

    moi j'ai fait un deuxième new pour un bloc de 113519 octets et il plante au 4628°


    Jean Noël Martin

    vendredi 17 janvier 2014 08:32
  • Vous n'allouez pas avec new ou malloc ???

    Alors avec quoi ?

    Un peu de code donnerait un peu de concret à cette vaporeuse limitation "technologique".

    >moi j'ai fait un deuxième new pour un bloc de 113519 octets et il plante au 4628

    un new de quoi ? BYTE[] ??

    un new est atomique, soit il alloue ce qui est demandé soit il n'alloue rien alors c'est quoi ce 4628 ?

    On va remettre le cloché au centre du village, faite une copie d'écran lors du plantage, AVEC la boite de dialogue, svp.


    Paul Bacelar, Ex - MVP VC++

    vendredi 17 janvier 2014 11:11
    Modérateur

  • Jean Noël Martin

    vendredi 17 janvier 2014 13:03
  • La fonction/méthode "Memory_allocate" est en cause, non ?

    Vous avez les sources, non ?

    Vous pouvez donc voir à quelle ligne de cette fonction/méthode le comportement commence à diverger de vos attente, non ? (en utilisant le débuggeur en mode ligne à ligne avec un point d'arrêt avant la ligne incriminée)

    Vous pouvez faire ce travail de manière récursive, non ?

    Vous pouvez nous posez la question quand une primitive donc vous n'avez pas le source ne fait pas ce que vous demandez, ou semblez demander, non ?

    On se secoue les puces, non ?


    Paul Bacelar, Ex - MVP VC++

    vendredi 17 janvier 2014 13:57
    Modérateur
  • Oui c'est la fonction memory_Allocate qui pose des soucis.

    J'ai résolu les souci en remplacant les new par des malloc et les delete par des free. ce qui est l'implémentation native


    Jean Noël Martin

    samedi 18 janvier 2014 15:24
  • new n'est pas un remplaçant de malloc C en C++, Je me demande comment cette migration c'est déroulée. :-(

    Vous êtes revenu au code initial et il fonction, c'est l'essentiel.

    C'est bien la migration le cœur du problème.

    Mais comme vous nous dites rien sur cette migration, qui en plus est inutile, on ne peut pas vous aider dans cette migration.

    Du code C ne ce transforme pas en C++ juste en changeant les malloc en new et les free en delete, loin de là.


    Paul Bacelar, Ex - MVP VC++

    • Marqué comme réponse JeanNoel53 lundi 20 janvier 2014 07:53
    samedi 18 janvier 2014 18:39
    Modérateur