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

Langage Delphi Discussion :

Interface et TInterfacedObject


Sujet :

Langage Delphi

  1. #1
    Membre habitué
    Homme Profil pro
    Chef de projets
    Inscrit en
    Août 2008
    Messages
    127
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Chef de projets
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2008
    Messages : 127
    Points : 195
    Points
    195
    Par défaut Interface et TInterfacedObject
    Bonjour,

    Il me semble déjà avoir vu couler beaucoup d'encre sur ce forum et d'autres, concernant les interfaces Delphi.

    Du coup je suis un peu perdu.
    Je souhaiterais travailler avec les interfaces plus pour le polymorphisme que pour le côté COM.

    Du coup, je suis un peu embêté avec TInterfacedObject, et son système de libération automatique.

    J'ai peur qu'un utilisateur instancie un objet (dérivant d'une interface) et ensuite le fasse pointer dans une interface.

    Du coup que faut-il faire ? Réécrire une classe TInterfacedObject sans compteur de référence ?

  2. #2
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 736
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 736
    Points : 25 645
    Points
    25 645
    Par défaut
    Citation Envoyé par joc02 Voir le message
    Du coup, je suis un peu embêté avec TInterfacedObject, et son système de libération automatique.
    C'est vrai que c'est un peu vicieux mais on s'y habitue !

    je suppose que tes craintes sont liés aux sujets :
    Question sur les interfaces, Court circuiter le ref-count d'un TInterfacedObject! ou Les limites de l'implements ?

    le TInterfacedPersistent permet de tricher rapidement avec cela puisque GetOwner renvoie nil par défaut, cela n'incrémente ni ne décrémente donc pas de libération automatiquement par contre QueryInterface fonctionne !

    Pour le COM (ActiveX), cela doit hériter de TAutoObject,
    TTypedComObject ou TComObject

    C'est une branche de l'implementation IInterface totalement différente et réservé à COM !

    Citation Envoyé par joc02 Voir le message
    J'ai peur qu'un utilisateur instancie un objet (dérivant d'une interface) et ensuite le fasse pointer dans une interface.
    un utilisateur ??? Qui ? un autre programmeur ?
    Là je ne comprends pas, tu écris le code des objets et celui que le manipule, tu dois donc être capable de gérer qui fait quoi !
    Ensuite pour les autres développeurs, si tu veux faire un polymorphisme par interface (je suppose que tu veux utiliser Supports GUID)
    Si tu n'utilises pas l'aspect multi-interface permis par les GUID, une sorte de héritage multiple pour l'utilisateur de l'interface
    Je ne vois pas l'intérêt des DelphiInterface sans ça, une abstraction par classe virtuelle totalement abstraite est suffisant !
    Supports(Obj, IBidule) en Delphi c'est l'opérateur as : Obj as IBidule.

    Avec les Interface, suffit de conserver uniquement les référence d'interface et non celle de l'objet, en général cela ne pose aucun problème !

    Donc poses toi ces questions :
    - Interface ou Classe Abstraite ?
    - Ai-je besoin de Support ?
    - Vais-je tout utiliser via interface ou via référence d'objet ?


    Pense que beaucoup utilise les Interfaces Delphi pas spécialement pour le polymorphisme mais plus comme un système de Garbage Collector, et c'est là que certains rencontrent des problèmes liés à la libération car ils s'en sont tellement peu préoccupé, qu'ils ont commis des erreurs !

    Citation Envoyé par joc02 Voir le message
    Du coup que faut-il faire ? Réécrire une classe TInterfacedObject sans compteur de référence ?
    Rien de plus simple
    Comme toi, j'ai utilisé DelphiInterface (DI) en C++Builder pour le polymorphisme sans COM
    J'ai un objet qui implémente une palanquée d'interface
    Cet objet est possédé par un autre, leur durée de vie est la même !
    J'avais pensé quoi toi au départ, finalement, au lieu de stocker l'objet je stocke la DI et j'utilise le compteur pour la libération !
    Et lorsque l'on libère le Premier objet, cela libère aussi l'autre puisque le nombre de référence tombe à zéro car côté appelant, il y a théoriquement plus de référence sur la DI, surtout que dans ma structure de code, l'utilisation des DI est très ponctuel, je ne le conserve pas côté appelant leur référence, mon code est souvent comme ceci

    Code c++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    //---------------------------------------------------------------------------
    IShaiCameraTrigger* TShaiCameraRecording::GetTriggerInterface()
    {
      if ( ! FTrigger)
      {
        IShaiCameraEntityRecordingTriggerableDelphiInterface TriggerIntf; // DI + 1 !
     
        if (Camera->PurposeInterface->Supports(TriggerIntf)) // je vérifie
          FTrigger = TriggerIntf->CreateTrigger(); // je l'utilise, FTrigger est une simple interface c++ (classe abstraite pure et non une DI !)
      }
     
      return FTrigger;
     
      // la DI - 1 mais reste au moins à 1 car Camera possède une référence dessus en interne !
    }

    Camera est une classe abstraite pure C++ IShaiHardwareEntity
    IShaiHardwareEntity a une méthode GetPurposeDelphiInterface (correspondant à la propriété PurposeInterface)
    Camera->PurposeInterface est DelphiInterface (IShaiHardwareEntityPurpose) qui ne contient qu'une méthode qui renvoie une IShaiHardwareEntity
    Cela me permet ainsi de passer de l'une à l'autre sans difficulté dans mon code
    Soit la IShaiHardwareEntity fortement typé et avec un jeu de fonction fixe lié au recensement d'un matériel
    Soit la IShaiHardwareEntityPurpose DelphiInterface offrant le support d'un nombre variable d'Interface !
    Donc toutes autres fonctionnalités passent par d'autres DelphiInterfaces
    IShaiHardwareEntityPurposeHTTPAccess, IShaiHardwareProviderExternalSoftware, IShaiCameraEntityPurpose, IShaiCameraEntityViewable, IShaiCameraEntityRecordable, IShaiCameraEntityRecordingTriggerable, ...
    La plupart de ces DI sont des factory qui m'offre d'autres classes abstraites pures C++ (les DI n'ont quasiment aucune durée de vie, juste le temps du Supports et l'appel de la fonction)

  3. #3
    Membre habitué
    Homme Profil pro
    Chef de projets
    Inscrit en
    Août 2008
    Messages
    127
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Chef de projets
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2008
    Messages : 127
    Points : 195
    Points
    195
    Par défaut
    Citation Envoyé par ShaiLeTroll Voir le message
    un utilisateur ??? Qui ? un autre programmeur ?
    Là je ne comprends pas, tu écris le code des objets et celui que le manipule, tu dois donc être capable de gérer qui fait quoi !
    Oui se sont d'autres dév.

    Après c'est peut-être mon approche qui n'est pas bonne. Mais si on s'en tient à un concept de base de la POO, c'est qu'une interface décrit le comportement d'une classe. Et que par composition on peut ainsi composer une classe "complexe".

    L'interface me parait plus souple, vu qu'on travaille sur un projet plusqu'évolué et conséquent (9 années de dev). Du coup même sur une interface graphique on peut venir forcer l'implémentation de certaines proc.

    Pour en revenir à mon problème de destruction auto (sans trop contrôler). Si j'ai compris les mécanismes (et là rien n’est moins sûr), je devrais écrire qlq chose du genre :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    var fruit : IFruit
    begin
        fruit := TOrange.create
    ....
    end;
    Ainsi dès l'instanciation de mon objet j'incrémente ma référence à une interface.

    Le risque et de trouver ce genre de code qui va provoquer une violation d'accès

    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
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
     
    type
       IFruit = interface
       ['{25F6D937-396D-4526-8209-1E9DCC1AC792}']
          procedure cueillir;
       end;
     
       IAgrume = interface
          procedure eplucher;
       end;
     
       TOrange = class(TInterfacedObject, IFruit, IAgrume)
       public
          procedure cueillir;
          procedure eplucher;
       end;
     
       TRaisin = class(TInterfacedObject, IFruit)
       public
          procedure cueillir;
       end;
     
      TForm1 = class(TForm)
        btOrange: TButton;
        btRaisin: TButton;
        procedure btRaisinClick(Sender: TObject);
        procedure btOrangeClick(Sender: TObject);
      private
        { Déclarations privées }
       procedure cueillir(fruit : IFruit);overload;
     
    var
      Form1: TForm1;
     
    implementation
     
    {$R *.dfm}
     
    { TRaisin }
     
    procedure TRaisin.cueillir;
    begin
       MessageDlg('Je cueille du raisin', mtWarning, [mbOK], 0);
    end;
     
    { TOrange }
     
    procedure TOrange.cueillir;
    begin
       MessageDlg('Je cueille une orange', mtWarning, [mbOK], 0);
    end;
     
    procedure TOrange.eplucher;
    begin
       MessageDlg('J''épluche une orange', mtWarning, [mbOK], 0);
    end;
     
    procedure TForm1.cueillir(fruit: IFruit);
    begin
       fruit.cueillir;
    end;
     
    procedure TForm1.btRaisinClick(Sender: TObject);
    var
       fruit : TRaisin;
    begin
       fruit := TRaisin.Create;
       cueillir(fruit);
    end;
     
    procedure TForm1.btOrangeClick(Sender: TObject);
    var
       orange : TOrange;
    begin
       orange := TOrange.Create;
     
       cueillir(orange);
    end;

    Dans le cas du btOrangeClick à la sortie de la méthode "cueillir" il ne faut plus faire de traitement sur "orange" car elle a été détruite.

  4. #4
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Points : 28 466
    Points
    28 466
    Par défaut
    deux solutions:

    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
     
    procedure TForm1.btRaisinClick(Sender: TObject);
    var
       fruit : TRaisin;
    begin
       fruit := TRaisin.Create;
       fruit._AddRef; // ce qui peut se faire dans le Constructor
       cueillir(fruit);
       fruit._Release; // sinon il n'est pas détruit
    end
     
    procedure TForm1.btRaisinClick(Sender: TObject);
    var
       fruit : IFruit; // Delphi se charge de tout :)
    begin
       fruit := TRaisin.Create;
       cueillir(fruit);
    end

  5. #5
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 736
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 736
    Points : 25 645
    Points
    25 645
    Par défaut
    Citation Envoyé par joc02 Voir le message
    Oui se sont d'autres dév.
    Tu sembles anticiper leur médiocrité ?


    Citation Envoyé par joc02 Voir le message
    Dans le cas du btOrangeClick à la sortie de la méthode "cueillir" il ne faut plus faire de traitement sur "orange" car elle a été détruite.
    Orange et Raisin sont pareils !
    Tes variables sont locales donc ne pourront jamais être utilisé en dehors de la procédure OnClick !
    Il n'y aucun problème dans ce code !

    C'est bien ce que j'ai dit !
    Tu utilises TRaisin et TOrange comme type, le RefCount doit-être à zéro !

    il faut utiliser des variables de type interface
    C'est le passage de paramètre, oui, ça il y a des pièges, je me rappelle quelques soucis à ce sujet justement à cause de mélange Reférence Objet et Interface, tiens ce sujet BUG dans le langage Delphi

    En COM, une interface passée en paramètre est marquée const , cela peut changer le comportement !
    J'ai pendant un moment j'avais pas mal utilisé d'interfaces en paramètre de fonction, je n'avais pas eu de problème

    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
    procedure TForm1.btRaisinClick(Sender: TObject);
    var
       fruit : IRaisin; // Interface !
    begin
       fruit := TRaisin.Create;
       cueillir(fruit);
       cueillir(fruit); // que se passe-t-il ? test en I et en T
    end;
     
    procedure TForm1.btOrangeClick(Sender: TObject);
    var
       orange : IOrange; // Interface !
    begin
       orange := TOrange.Create;
     
       cueillir(orange);
       cueillir(orange); // que se passe-t-il ? test en I et en T
    end;

  6. #6
    Membre habitué
    Homme Profil pro
    Chef de projets
    Inscrit en
    Août 2008
    Messages
    127
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Chef de projets
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2008
    Messages : 127
    Points : 195
    Points
    195
    Par défaut
    C'est peut-être moi qui chipote.

    Citation Envoyé par Paul TOTH Voir le message
    deux solutions:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    procedure TForm1.btRaisinClick(Sender: TObject);
    var
       fruit : TRaisin;
    begin
       fruit := TRaisin.Create;
       fruit._AddRef; // ce qui peut se faire dans le Constructor
       cueillir(fruit);
       fruit._Release; // sinon il n'est pas détruit
    end
    Dans ce cas, ça me gêne d'avoir un "vrai type" avec un "create" et sans free. Vous allez me dire qu'on a le release qui va faire pareil c'est pour ça que je dis que je chipote.

  7. #7
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 736
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 736
    Points : 25 645
    Points
    25 645
    Par défaut
    Citation Envoyé par joc02 Voir le message
    C'est peut-être moi qui chipote.
    Tu n'es pas le seul !

    Pour le AddRef\Release manuel, euh, ça me choque mais après tout l'affectation à une interface fait la même chose !

    @Paul TOTH, regarde ce sujet BUG dans le langage Delphi
    il semblerait que le passage en paramètre soit désastreux !
    Je n'ai pas Delphi et je n'ai pas constaté de problème en C++Builder, j'ai passé des DelphiInterface en paramètres de fonctions (souvent un constructeur ou assimilé) donc la durée de vie de la DI était assuré par la durée de vide de l'objet ayant reçue la DI en paramètre en sachant que même dans la partie implementation je conserve une DI et non une référence sur l'objet d'origine, la durée de vie de la DI était assurée quoi qu'il arrive

    @joc02
    Tient, tu as changé ta réponse sur le TRaisin\IRaisin, tu as bien fait

  8. #8
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Points : 28 466
    Points
    28 466
    Par défaut
    Citation Envoyé par ShaiLeTroll Voir le message
    Tu n'es pas le seul !

    Pour le AddRef\Release manuel, euh, ça me choque !



    Tient, tu as changé ta réponse sur le TRaisin\IRaisin, tu as bien fait
    ça peut vous choquer, mais c'est le seul moyen

    sans le _AddRef, l'appel à ceuillir() va détruire l'objet qui n'a plus de RefCount, impossible de le libérer par un Free

    avec le _AddRef Free n'est pas autorisé car TInterfacedObject.BeforeDestruction déclenche une exception car RefCount > 0

    avec _AddRef et _Release le Free n'est de nouveau plus possible car l'objet a été détruit.

    Or donc c'est bien ce que je disais, deux solutions, avec un TRaisin, appeler manuellement _AddRef et _Release, ou alors déclarer un IRaisin pour que Delphi le fasse automatiquement

  9. #9
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 736
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 736
    Points : 25 645
    Points
    25 645
    Par défaut
    Mais je comprends bien la problématique manuelle du AddRef\Release, pour moi c'est juste que si l'on commence des interfaces faut être rigoureux et ne faire plus que des interfaces, y compris dans l'implémentation si les DI se référence entre-elles !

    Comme c'est un travail d'équipe, il faut expliquer les deux méthodes et leur signification, et vivement conseillé la plus simple avec I

  10. #10
    Membre habitué
    Homme Profil pro
    Chef de projets
    Inscrit en
    Août 2008
    Messages
    127
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Chef de projets
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2008
    Messages : 127
    Points : 195
    Points
    195
    Par défaut
    Alors si on résume :
    - Soit on utilise QUE des variables d'interfaces, et on laisse gérer Delphi. Pas si évident, car si la classe n'implémente pas d'interface, il faudra une variable objet. Par contre demain, je peux venir lui ajouter une interface et là je devrais reprendre tous les cas d'utilisation pour les transformer en variables d'interface.
    - Soit on utilise une syntaxe moins propre et moins standard pour panacher des objets et des variables interfaces.

    Du coup je reviens à ma question initiale. Si on veut juste travailler en POO, ne faut-il pas réécrire une classe sur le principe du TInterfaceObject et en bloquant l'effet du compteur ?
    Quel serait le risque de procéder ainsi ?

  11. #11
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Points : 28 466
    Points
    28 466
    Par défaut
    Citation Envoyé par joc02 Voir le message
    Alors si on résume :
    - Soit on utilise QUE des variables d'interfaces, et on laisse gérer Delphi. Pas si évident, car si la classe n'implémente pas d'interface, il faudra une variable objet. Par contre demain, je peux venir lui ajouter une interface et là je devrais reprendre tous les cas d'utilisation pour les transformer en variables d'interface.
    - Soit on utilise une syntaxe moins propre et moins standard pour panacher des objets et des variables interfaces.

    Du coup je reviens à ma question initiale. Si on veut juste travailler en POO, ne faut-il pas réécrire une classe sur le principe du TInterfaceObject et en bloquant l'effet du compteur ?
    Quel serait le risque de procéder ainsi ?
    le risque c'est de détruire l'objet alors qu'il reste une référence quelque part...ceci dit ce risque existe aussi sans les interfaces

    maintenant si tu veux une gestion propre sans interface il faut utiliser des délégations.

    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
     
    type
      TEplucheur = class
        procedure Eplucher; virtual;
      end;
     
      TFruit = class
        function GetEplucheur: TEplucheur; virtual;
      end;
     
      TEplucheurDePomme = class(TEplucheur)
        procedure Eplucher; override;
      end;
     
      TPomme = class(TFruit)
        function GetEplucheur: TEplucheur; override; // retourne un Eplucheur de Pomme
      end;

Discussions similaires

  1. [VB6] [Interface] ComboBox à plusieurs colonnes
    Par mtl dans le forum VB 6 et antérieur
    Réponses: 7
    Dernier message: 30/03/2004, 18h35
  2. [VB6] [Interface] Horloge 7 segments
    Par selenay dans le forum VB 6 et antérieur
    Réponses: 11
    Dernier message: 07/10/2002, 17h15
  3. interface utilisateur avec OpenGL
    Par demis20 dans le forum OpenGL
    Réponses: 6
    Dernier message: 03/10/2002, 13h27
  4. [VB6] [Interface] Tester le Type de Controle
    Par SpaceFrog dans le forum VB 6 et antérieur
    Réponses: 9
    Dernier message: 16/09/2002, 10h51
  5. [VB6] [Interface] Icones de boutons de barre d'outils
    Par elifqaoui dans le forum VB 6 et antérieur
    Réponses: 9
    Dernier message: 13/09/2002, 16h50

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