Vous ne trouvez pas de réponse à votre problème ? Alors posez la question dans le forum. Souvenez-vous qu'il n'y a jamais de question bête, mais rester dans l'ignorance parce que l'on n'ose pas poser une question, ça c'est une erreur !

LE SUBCLASSING EN VB6 ET VB.NET


Information sur le tutorial

Catégorie :Formulaire Date de création : 16/01/2006 19:35:48 Vu : 12 028 fois

Note :
8,67 / 10 - par 3 personnes
8,67 / 10

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

Commentaire sur cette source (9)
Ajouter un commentaire et/ou une note


Description

Ce tutoriel montre comment faire du sous classement de contrôles en VB6 et en VB.Net

Tutorial

Le subclassing 

 

But de ce tutoriel

Lebut de ce tutoriel n'est pas seulement de vous faire découvrir lesubclassing mais aussi de vous montrer comment on peut convertir uncode VB6 utilisant le subclassing vers un code VB.Net n'utilisant pasd'artifices. 

La notion de fenêtre

 

Unefenêtre est tout objet que l'on voit à l'écran. Il peut s'agir d'unefenêtre classique ou de tout autre contrôle comme une zone de texte, uncombo, un bouton radio, un bouton cliquable, un treeview... 

Une fenêtre comporte : 

  • un ensemble de propriétés 

  • une fonction de gestion de son comportement 

  • un nom de classe : il permet de connaître le type de la fenêtre. 

 

Unefenêtre se construit comme une classe en conception objet : on définitl'implémentation pour un type d'objet et ensuite, et seulement ensuite,on l'instancie pour en faire vivre autant de copies que l'on veut. 

Création d'un fenêtre

 

Pourcréer une fenêtre, il faut une activité (un thread ou la fonctionWinMain). Il faut aussi crée une classe qui porte un nom unique et quicontient les propriétés de base  de la fenêtre :

  • la fonction de fenêtre qui gère les fenêtres de cette classe 

  • le handle de l'icone de ce type de fenêtre 

  • le handle du curseur de ce type de fenêtre 

  • le handle de l'instance de l'application qui enregistre ce type de fenêtre (reçu en paramètre de WinMain) 

  • la couleur de fond de ce type de fenêtre 

  • le nom de la ressource (dans l'exécutable) contenant le menu  de ce type de fenêtre

  • le nom de cette classe de fenêtre 

 

On remplie une structure WNDCLASS avec ces informations et on enregistre ce type de fenêtre avec la fonction RegisterClass(Ex). Ilne faut enregistrer une classe de fenêtre qu'une seule fois même sivous créez plusieurs fenêtre de ce type dans une même instance d'unprogramme.

 

Il faut ensuiteinstancier une fenêtre du type créé, et cela avec la fonctionCreateWindow(Ex). Il faut lui passer les paramètres suivants : 

  • le style étendu : (support du drop de fichier, SDI, MDI, toolbox, bordures, barre de titre, transparence...) 

  • le type de fenêtre : le nom de la classe à instancier (par exemple :BUTTON, EDIT(textbox), STATIC(label)) 

  • un titre (si une barre de titre existe) ou un nom 

  • le style : titre, menu système, bordure, popup, état... 

  • les coordonnées et la taille initiale 

  • la fenêtre parent si cette fenêtre est une fille d'un MDI ou un simple contrôle 

  • le handle d'un menu (chargé avec LoadMenu) 

  • le handle de l'instance de l'application en cours 

  • un éventuel paramètre à passer à la création de la fenêtre 

 

Dèslors un handle (une référence) de cette fenêtre vous est retourné maisla fenêtre n'est pas encore visible. Les actions précédentescorrespondent au Load de VB6. 

 

Il faut ensuite afficher la fenêtre avec ShowWindow et UpdateWindow (pour lui faire dessiner son contenu). 

 

Vous obtenez alors une belle fenêtre mais qui ne réagit à rien. Pour cela, il faut une file de message... 

Boucle et file de messages

 

Cela permet de donner vie à la fenêtre. 

 

SousWindows, les fenêtres sont gérées par une file de messages (évènements)qui appelle une fonction de gestion . Ces messages indique, parexemple, des clics, déplacements, ajouts d'items mais aussirafraîchissement, redessinement...A chaque évènement correspond unmessage. Les messages sont classés dans plusieurs catégories : système,fenêtre générale, listbox, toolbox, ... et messages personnels. 

A chaque activité (thread) est associé une file de message automatiquement par le systèmes. 

 

Une boucle de message typique pour une fenêtre fait les actions suivantes : 

  • tant que l'on peut lire un message avec GetMessage 

    • on traduit les éventuels messages d'entrées de caractères du code virtuel en code ascii. 

    • on appelle la fonction de fenêtre de cette fenêtre pour lui faire traiter le message  

La gestion des fenêtres : les fonctions de fenêtre

 

Les fenêtres sont donc gérées par une fonction ou une chaîne de fonctions. Il y a au moins une fonction par classe de fenêtre,c'est la fonction par défaut fournie par Windows. Elle implémente lesfonctionnalités de base de l'objet fenêtre comme le dessin, le textecontenu sur le contrôle...Le chaînage de fonctions de fenêtre se faitgrâce à la fonction CallWindowProc. Ceci permet un traitement en couchepar rafinement successif des messages traités. Le dernier subclassingmis en place reçoit les messages en premier, le second ensuite jusqu'àla fonction par défaut sauf si l'un des subclassing ne passe pas lemessage  aux fonctions de niveau inférieur (en n'appelant pasCallWindowProc).

 

Une fonction de fenêtre gère toutes les instances de la même classe de fenêtre. 

 

Une fonction de fenêtre ne devrait gérer qu'une seule classe de fenêtres. 

 

Cette fonction a le prototype suivant : 

  • en C 

               LRESULT CALLBACK WndProc(HWND hWnd, UINT uMessage, WPARAM wParam,LPARAM lParam)

  • en VB6 

               Public Function WndProc(ByVal hWnd As Long, ByValuMessage As Long, ByVal wParam As Long, ByVal lParam As Long) As Long

 

  • hWnd: handle de la fenêtre (ou plus précisément, l'instance de la classe defenêtre) qui reçoit le message : utile pour les appels d'API gérant lesfenêtres (SetWindowText, SetParent, GetWindowLong...)

  • uMessage : message envoyé à la fenêtre : indique l'action que la fonction doit faire sur la fenêtre (WM_SETTEXT, LVM_GETITEM...)

  • wParam : paramètre dépendant du message (peut être un pointeur ou une valeur)

  • lParam : paramètre dépendant du message (peut être un pointeur ou une valeur)

 

Le subclassing c'est quoi ?

 

Nevous êtes vous jamais dit : « tiens, ça serait bien que mon contrôlefasse ceci ou cela en réponse à cet évènement » et là, vous cherchezdans la documentation et oh surprise, aucune propriété ne vous lepermet...par contre, ce comportement peut être obtenu par lamodification ou la suppression d'un évènement existant. Eh bien, lesubclassing, ça sert à ça... 

Le subclassing sert doncà changer le comportement d'une fenêtre (au sens général du terme : unezone de texte, un combo, un treeview...) à la réception du message(évènement). Cela permet aussi de modifier le contenu d'un message oud'en empêcher l'acheminement à la procédure qui gère la fenêtre. 

 

Le subclassing est donc plusieurs choses : 

  • le fait d'ajouter une fonction de fenêtre dans la chaine des fonctions de fenêtre d'une instance (et non d'une classe) de fenêtre.

  • Affecter différentes procédures à différentes instances d'une même classe de fenêtre 

 

Il permet : 

  • de modifier/supprimer un comportement face à un évènement particulier 

  • d'activer des fonctionnalités à une fenêtre particulière 

Et VB6 dans tout ça ?

 

VB6gère les fonctions de fenêtres de ses contrôles avec des comportementspar défaut dépendant des propriétés de ceux-ci. Il est donc inutile detenter de créer une fenêtre avec les APIs depuis VB pour deux raisons : 

  • VB6 n'aime pas les fenêtres qu'il n'a pas créé lui-même : risque de plantage 

  • Pourquoi faire compliqué quand on peut faire simple

 

Quels messages traiter ?

 

Celadépend de l'action que vous voulez faire : la documentation sur MSDNpermet de voir les paramètres des différents messages afin de trouverle bon et avec les bon paramètres. 

Il faudra ensuite déclarée au moins une constante pour le message dont vous avez besoin. 

        Private Const UN_MESSAGE As Long = &Hxxx&

 

Quelques types de messages

 

Il existe plusieurs catégories de messages qui ont des préfixes différents (extrait) : 

  • WM_xxx : n'importe quel fenêtre (contrôles, MDI, SDI...)

  • BM_xxx : simple bouton (Command)

  • CB_xxx : simple liste déroulante (combobox)

  • CDM_xxx : Common dialog

  • DTM_xxx : DateTimePicker

  • EM_xxx : simple zone de texte éditable (TextBox)

  • HKM_xxx : contrôle HotKey

  • IPM_xxx : zone de saisie d'IP

  • LB_xxx : simple liste (listbox)

  • LVM_xxx : simple ListView

  • MCM_xxx : calendrier

  • PBM_xxx : ProgressBar

  • SB_xxx : simple barre de status

  • SBM_xxx : HscrollBar ou VScrollBar

  • STM_xxx : simple zone de texte non éditable (Label)

  • TB_xxx : simple barre d'outils

  • TTM_xxx : contrôle pour InfoBulle

  • TVM_xxx : simple TreeView

  • UDM_xxx : simple bouton Up/Down

 

La méthode par APIs pour VB6

Fonctions de mise en place et de gestion du subclassing

 

Le principe est le suivant : 

  • on obtient le handle de la fenêtre que l'on veut subclasser 

  • unremplace dans les propriétés de cette fenêtre (par son handle), lepointeur vers la fonction de fenêtre actuelle par un pointeur versnotre fonction 

  • onconserve une copie du pointeur orignal pour pouvoir transmettre lesmessages à cette fonction (pour ne pas avoir à réimplémenter le dessindu contrôle ;) par exemple) 

  • celapermet de se placer en début de la chaîne des fonctions de fenêtre decette fenêtre et donc de recevoir en premier les messages (à moins q'unautre subclassing est lieu après la mise ne place du notre) 

  • Dans le fonction fenêtre dont on a mis l'adresse pointeur dans les propriétés 

    • on vérifie qu'il n'est pas temps de retirer le subclassing avant de faire planter l'application 

    • on traite le (ou les) message(s) que l'on veut avec un Select Case ou un If par exemple 

    • on passe les messages à la fonction de fenêtre suivante pour ne pas faire planter la fenêtre.Car si on ne passe pas les messages aux autres fonctions de de fenêtreet en particulier à la fonction de fenêtre par défaut pour la classe,la fenêtre apparaîtra comme plantée puisqu'elle ne recevra plus l'ordrede se redessiner ou de réagir aux clics. Si on ne veut pas passer un certain message uniquement, on s'assure de bien passer les autres.

    • Il est aussi important que la fonction de fenêtre renvoie la bonne valeur suivant le message. Si vous gérez vous même un message, assurez-vous de bien renvoyer la valeur adéquat, sinon renvoyez la valeur que renvoie CallWindowProc.

 

Pour mettre en place le subclassing, il faut un module .bas : 

 

'pointeur vers la fonction de fenêtre originale pour pouvoir la restaurer le moment voulu 

Private lpOldWindowProc As Long 

'handle de la fenêtre que l'on subclasse 

Private hWindow As Long 

 

'indique à SetWindowLong/GetWindowLong de définir ou renvoyer la fonction de fenêtre 

Private Const GWL_WNDPROC As Long = (-4) 

'indique le message qui doit déclencher la restauration de la fonction de fenêtre avant que le programme ne plante 

Private Const WM_NCDESTROY As Long = &H82& 

 

'appel la fonction suivante dans la chaine des fonctions de fenêtres qui gèrent la fenêtre hWindow 

PrivateDeclare Function CallWindowProc Lib "user32" Alias "CallWindowProcA"(ByVal lpPrevWndFunc As Long, ByVal hWnd As Long, ByVal msg As Long,ByVal wParam As Long, ByVal lParam As Long) As Long 

 

'change une valeur des propriétés d'une fenêtre : ici la fonction de fenêtre 

PrivateDeclare Function SetWindowLong Lib "user32" Alias "SetWindowLongA"(ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) AsLong 

 

'cette fonction permet de mettre en place le subclassing d'une fenêtre par son handle 

'hWnd : handle de la fenêtre que l'on veut subclasser 

Public Function Subclass(ByVal hWnd As Long) As Boolean 

        'il ne faut pas subclasser une fenêtre deux fois

        If  hWindow = 0 Then

                'si elle n'est pas subclassée, on change sa fonction de fenêtre avec notre nouvelle

                lpOldWindowProc = SetWindowLong(hWnd, _

                                                       GWL_WNDPROC, _

                                                       AddressOf WindowProc)

                'on garde une trace du handle de la fenêtre subclassée pour savoir que l'on l'a fait

                hWindow = hWnd

                Subclass = True

        Else

                Subclass = False

        End If

End  Function

 

'cette fonction retire le subclassing de la fenêtre actuellement subclassée 

Public Function UnSubclass() As Boolean 

        'on ne retire le subclassing que s'il y est

        If  hWindow <> 0 Then

                SetWindowLong(hWindow, _

                                    GWL_WNDPROC, _

                                 lpOldWindowProc)

                hWindow = 0

                UnSubclass = True

        Else

                UnSubclass = False

        End If

End  Function

 

 

'fonction qui est appelées pour chaque message uMsg que la fenêtre reçoit 

'hWnd : handle de la fenêtre qui reçoit le message 

'uMsg : message reçu 

'wParam et lParam : paramètres du message 

Public Function WindowProc(ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) 

        'enlever le sublcassing à la destruction de la fenêtre

        'pour éviter le plantage de l'application genre appel de code qui n'est plus en mémoire...

        If msg = WM_NCDESTROY Then

                        Call UnSubclass()

        End If

 

        'ajouter votre code de traitement des messages ici

        'avec par exemple, un Select Case avec les messages que vous gérez

        WindowProc = CallWindowProc(lpOldWindowProc, hWnd, uMsg, wParam, lParam)

End Function 

 

Présenter comme ceci, ce subclassing ne fait rien. Vous devez ajouter votre code dans la fonction WindowProc sous forme d'un If ou d'un Select :

 

        If uMsg = UN_MESSAGE Then

                'traitement

        End If

 

        'ou

 

        Select uMsg

                Case UN_MESSAGE

                        'traitement

                Case UN_AUTRE_MESSAGE

                        'un autre traitement

        End Select

 

Il est important que WindowProcrenvoie une valeur en fonction du message s'il le traite. Si vous nevoulez pas passer le message plus bas dans la chaîne de procédure,affectez une valeur à WindowProc et faites un Exit Function.

 

ATTENTION: il est vitable pour votre programme que tout message non traité parvotre subclassing soit passé aux fonctions de fenêtre de plus basniveau que la votre sans quoi votre contrôle cesserait de répondre etne se rafraîchirait plus. 

 

ATTENTION : NE JAMAIS ARRETER L'EXECUTION DE VOTRE PROJET VB6 PAR LE BOUTON « STOP »(de la barre d'outils ou du menu Exécution) DE VB. SI VOUS FAITES CELA,VB S'ARRETERA BRUTALEMENT ET VOUS PERDREZ VOS MODIFICATIONS si ellesn'ont pas été enregistrées avant l'exécution du projet.

Lancement et arrêt du subclassing

 

Pour lancer le subclassing d'un contrôle : Call Subclass(objet.hWnd). Ce code se trouve en général dans Form_Load.

Pour arrêter le subclassing d'un contrôle : Call UnSubclass(objet.hWnd). Ce code se trouve en général dans Form_Unload voire Form_Terminate.

 

 

Exemple :

        voir mon code sur comment empêcher le déplacement des icones dans un treeview.

 

 

La méthode VB.Net : subclassing par dérivation

 

Pour subclasser en VB.net, c'est beaucoup plus simple qu'avec VB6. Il suffit que vous dériviez une classe de NativeWindow du l'espace de nom System.Windows.Forms. Ensuite il vous suffit de prendre en paramètre du constructeur le handle de la fenêtre à sublcasser :  ByVal handle As IntPtr. Le constructeur appelle ensuite la méthode AssignHandle  de la classe de base avec ce paramètre. Enfin, il vous faut redéfinir la fonction membre WndProc de NativeWindow pour être notifié des messages destinés à la fenêtre.

 

Il y a deux façons de concevoir la classe de subclassing : 

  • le traitement se fait dans la classe même : c'est alors une classe spécialisée 

  • Le traitement se fait par un évènement déclenché pour chaque message. Ceci peut s'avérer plus lent. 

 

Voici le code de la deuxième méthode (qui peut servir de base pour la première méthode) : 

 

Imports System.Windows.Forms 

 

    Public Class SubClassing

        Inherits System.Windows.Forms.NativeWindow

 

        'Evènement déclencher à chaque message reçu par le contrôle

        Public Event CallBackProc(ByRef m As Message)

 

         'indique si le contrôle est subclassé ou non

        Private m_Subclassed As Boolean = False

 

        'on passe le handle du contrôle que l'on veut subclassé

        'à notre classe

        Public Sub New(ByVal handle As IntPtr)

                'on assigne le handle à la classe dont on hérite

            MyBase.AssignHandle(handle)

        End Sub

 

 

        'définit ou renvoie l'état du subclassing du contrôle

        Public Property SubClass() As Boolean

            Get

                Return m_Subclassed

            End Get

            Set(ByVal Value As Boolean)

                m_Subclassed = Value

            End Set

        End Property

 

         'la fonction de traitement des messages du contrôle

        Protected Overrides Sub WndProc(ByRef m As Message)

                'à la place de ceci vous pouvez mettre

                'votre propre traitement des messages

            If m_Subclassed Then

                   'on ne déclenche l'event que si on subclasse

                RaiseEvent CallBackProc(m)

            End If

                'on passe toujours le message à la fonction de base du contrôle

            MyBase.WndProc(m)

        End Sub

 

         'on définit explicitement le finalizer

        Protected Overrides Sub Finalize()

            MyBase.Finalize()

        End Sub

    End Class

 

L'évènement CallBackProcest déclenché à chaque message. En traitant cet évènement, vous pouvezmodifier le contenu du message. Pour cela, il vous suffit de déclarerune variable globale à la Form contenant le contrôle à subclasser (celapeut être la Form elle-même) : Private WithEvents test As SubClassing. Vous pouvez ainsi choisir l'évènement dans le second combo de la fenêtre de code.

 

Vouspouvez bien entendu traiter les message directement dans la classeSubClassing (ce qui vous permet de ne pas forcement passer les messagesque vous traitez, à la classe de base). Je vous encourage à implémentervos traitement de cette manière directement dans la classe. C'est plusrapide et plus encapsulé. 

 

Exemples : 

        voir mon code sur la détection de l'écran de veille.

24 janvier 2006 23:23:22 :
formattage
24 janvier 2006 23:31:46 :
formattage
25 janvier 2006 08:04:54 :
formattage
17 mars 2006 20:36:20 :
Ajout d'explication
signaler à un administrateur
Commentaire de PCPT le 24/01/2006 00:20:23 administrateur CS

salut,
tu peux refaire la mise en page stp...
merci ;)

signaler à un administrateur
Commentaire de PCPT le 02/02/2006 19:44:50 administrateur CS

merci pour les modifications.
l'explication (VB6 du moins) est succincte mais claire. (8/10)

petit soucis par module de classe (projet ActiveX).
en compoaraison, lors du UnSubClass (au Terminate simplement), mes objets ne se déchargent TOUS que quand le dernier est détruit.

c'est à dire, j'ai par exemple 1 Form, un bouton, new.... Form1.
si je quitte, msgbox dans la classe au terminate, ok.
mais si j'ai par exemple 3 instances de ma form (bouton cliqué 2 fois), j'ai les 3msgbox uniquement lors de la fermeture de la dernière. une idée?
j'ai pas mal cherché, et beaucoup  de dll de subclassing ont ce défaut.

si tu as une piste, bien volontier ;)
PCPT  [AFCK]

signaler à un administrateur
Commentaire de ShareVB le 17/03/2006 17:47:33

salut,

dsl de ne pas avoir répondu avant, mais j'étais débordé...

sinon, pour les explications, je vais en ajouter un peu...

pour l'histoire des forms, c'est normal car l'évènement Terminate est appelé quand toutes les références sont libérées càd quand l'application se termine ou quand on fait un Set reference = Nothing. En fait on peut simplement faire le subclass dans le Load et le unsubclass dans le Unload.

ShareVB

signaler à un administrateur
Commentaire de PCPT le 17/03/2006 18:03:35 administrateur CS

salut,
umm, le "new form1" n'est qu'un exemple et juste pour comparer. j'ai le même souci avec une autre instance de subclassing pour une form différente appelée.

dans ma classe, (toujours en comparaison) si le subclassing est actif (boolean), mon terminate déclenche mon unsubclass.
et ma form contient un set maclass = nothing dans le terminate.

peut-être est-ce juste une subtilité des classes, un attribut ou autre qui m'aurait échappé, mais toutes ont ce défaut (et pas seulement sur VbFrance).
toutes à l'exeption d'une, celle de Rey.
c'est donc faisable... (il n'a malheureusement pas pu me renseigner)

pour donner une comparaison avec un create timer (en classe toujours), si les classes instanciées sont stoquées dans un tableau (handle/id), même problème. si c'est par contre dans une collection, la destruction est synchro.
la différence étant qu'on détruit l'item (donc un objet) pour la collection, alors qu'on enlève qu'une ligne au tableau (la dernière) ou on vide une valeur (avant).


ouvert à toute suggestion ;)
++
PCPT   [AFCK]

signaler à un administrateur
Commentaire de ShareVB le 17/03/2006 20:59:21

salut,

le Terminate pour tout objet et donc classe est déclencher par un Set obj=Nothing implicit ou explicit...Unload est toujours appelé lorsque la form est déchargée (<> détruite)...

le mieux c'est de mettre le UnSubclass dans le QueryUnload ou Unload comme Rey...l'évènement Terminate est appelé à la fin du programme donc quand tous les objets sont déchargés si c'est une variable globale...

l'autre solution consiste à faire un Set obj = Nothing DES que l'on n'a plus besoin de obj, sinon il sera fait lorsque obj sortira de sa portée...cela permet de déclencher un Terminate...

d'autres part, je ne suis pas sûr qu'un tableau fasse du Set = Nothing sur tous ces éléments...

PS : j'ai ajouté un peu de contenu à mon tuto...si le cache veut bien l'afficher...

ShareVB

signaler à un administrateur
Commentaire de PCPT le 17/03/2006 22:05:51 administrateur CS

je met à jour le cache et te tiens au courant des modifs de mon côté (plus tard, pas le temps en ce moment).
merci de tes précisions ;)
++

signaler à un administrateur
Commentaire de ShareVB le 11/08/2006 22:38:24

salut à tous,

retrouvez ce tuto sur mon site perso à www.sharevb.net...rubrique Programmation\VB\

ShareVB

signaler à un administrateur
Commentaire de GeneticW le 17/11/2006 17:12:19

sans vouloir faire le troll, c'est pas trop fort de publier ce genre d'article sans une source fonctionnelle.

les explications sont top cependant.

signaler à un administrateur
Commentaire de ShareVB le 17/11/2006 17:31:57

salut,

certes :) , cependant, voici deux exemples (effectivement pas forcément évident à trouver directement) :
en VB6 : http://www.vbfrance.com/codes/DETECTER-CONTROLER-ECRAN-VEILLE-RUNTIME-VB6_39051.aspx ou encore http://www.vbfrance.com/codes/OUVRIR-DOCUMENTS-DANS-MEME-INSTANCE-APPLICATION-SANS-RELANCEMENT_27089.aspx
en VB.Net : http://www.vbfrance.com/codes/DETECTER-CONTROLER-ECRAN-VEILLE-RUNTIME-VB-NET_35502.aspx

j'adore la théorie...lol :))

ShareVB

Ajouter un commentaire



Nos sponsors

Sondage...

CalendriCode

Octobre 2008
LMMJVSD
  12345
6789101112
13141516171819
20212223242526
2728293031  

Consulter la suite du CalendriCode



Développement réalisé par Nicolas SOREL (Nix) avec l'aide de : Cyril DURAND et Emmanuel BAÏSE, Merci à Vincent pour ses précieux conseils
CodeS-SourceS.com© Toute reproduction même partielle est interdite sauf accord écrit du Webmaster
CodeS-SourceS.com© est une marque déposée tous droits réservés
Temps d'éxécution de la page : 0,016 sec

Google Coop CodeS-SourceS Google Coop CodeS-SourceS


Certaines images présentes sur le site (notament certains avatars) sont issues des collections IconShock, donc si vous souhaitez utiliser ces icons vous devez les acheter, ne les copiez pas et ne utilisez pas dans vos sites et applications sans les avoir commandé.