4 votes

Le marshalleur COM standard échoue avec REGDB_E_IIDNOTREG

J'essaie de mettre en place une interface avec un autre fil.

Windows offre la possibilité de CoMarshalInterThreadInterfaceInStream pour prendre en charge le code passe-partout associé à l'utilisation de la fonction CoMarshalInterface directement.

const Guid CLSID_Widget = "{F8383852-FCD3-11d1-A6B9-006097DF5BD4}";
const Guid IID_IWidget  = "{EBBC7C04-315E-11D2-B62F-006097DF5BD4}";

//Create our widget
HRESULT hr = CoCreateInstance(CLSID_Widget, null, 
      CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER, 
      IID_IWidget, out widget);
OleCheck(hr);

//Marshall the interface into an IStream
IStream stm;
hr = CoMarshalInterThreadInterfaceInStream(IID_IWidget, widget, out stm);
OleCheck(hr);

Sauf que l'appel à CoMarshalThreadInterfaceInStream échoue avec :

REGDB_E_IIDNOTREG (0x80040155)
Interface not registered

Aller directement à CoMarshalInterface

La fonction de l'API COM CoMarshalInterThreadInterfaceInStream fournit une enveloppe simple autour de CreateStreamOnHGlobal y CoMarshalInterface , comme indiqué ici :

// from OLE32.DLL (approx.)
HRESULT CoMarsha1InterThreadInterfaceInStream(
   REFIID riid, IUnknown *pItf, IStream **ppStm) 
{
   HRESULT hr = CreateStreamOnHGlobal(0, TRUE, ppStm);
   if (SUCCEEDED(hr))
       hr = CoMarshalInterface(*ppStm, riid, pItf,
             MSHCTX_INPROC, 0, MSHLFLAGS_NORMAL);
       return hr;
}

Nous pouvons donc essayer nous-mêmes.

IStream stm = new Stream()
hr = CoMarshallInterface(stm, IID_IWidget, widget, 
      MSHCTX_INPROC,    // destination context is in-process/same host
      NULL,             // reserved, must be null
      MSHLFLAGS_NORMAL  // marshal once, unmarshal once
);
OleCheck(hr);

Mais cela échoue avec :

REGDB_E_IIDNOTREG (0x80040155)
Interface not registered

Utiliser le marshaling standard

Ma classe fait no mettre en œuvre IMarhsal interface. C'est juste et normal.

Par défaut, lorsque CoMarshalInterface est appelé pour la première fois sur un objet, il est demandé à l'objet s'il souhaite gérer ses propres communications inter-appartements. Cette question se présente sous la forme d'un QueryInterface demande pour le IMarshal l'interface. La plupart des objets n'implémentent pas l'interface IMarshal et échouer cette QueryInterface en indiquant qu'ils sont parfaitement heureux de laisser COM gérer toutes les communications via des appels ORPC. Les objets qui implémentent l'option IMarshal indiquent que l'interface ORPC n'est pas appropriée et que l'implémenteur de l'objet préférerait gérer toutes les communications entre départements. via un proxy personnalisé. Lorsqu'un objet implémente l'interface IMarshal toutes les références à l'objet seront marshalées de manière personnalisée.

Lorsqu'un objet n'implémente pas la fonction IMarshal toutes les références à l'objet seront marshalées de manière standard. La plupart des objets choisissent d'utiliser le marshaling standard.

La question est donc de savoir pourquoi le maréchal standard COM a tant de problèmes. Quelle est la source de l'erreur Interface non enregistrée ?

L'interface n'est, en fait, pas enregistrée

Les exigences pour COM ne sont pas documentées, mais je peux vous dire que le GUID de mon interface ne fait pas partie de cette catégorie. no existe dans :

HKEY_CLASSES_ROOT\Interface\{EBBC7C04-315E-11D2-B62F-006097DF5BD4}

La raison en sera expliquée à la fin.

Je sais que Windows vous fournit CoRegisterPSClsid qui vous permet d'enregistrer une interface à l'intérieur de votre processus, afin que le marshaler standard soit capable de la marshaler :

Permet à une DLL téléchargée d'enregistrer ses interfaces personnalisées au sein de son processus d'exécution afin que le code de marshaling soit en mesure de marshaler ces interfaces.

HRESULT CoRegisterPSClsid(
   _In_ REFIID   riid,
   _In_ REFCLSID rclsid
);

Paramètres :

  • riid [en] : Un pointeur vers l'IID de l'interface à enregistrer.
  • rclsid [in] : Un pointeur vers le CLSID de la DLL qui contient le code proxy/stub pour l'interface personnalisée spécifiée par riid.

Ce que je peux essayer d'appeler, mais quoi clsid que j'utilise ?

CoRegisterPSClsid(IID_IWidget, ???);

Quel est le CLSID de la DLL qui contient le code proxy/stub pour l'interface personnalisée spécifiée par riid ? Dois-je utiliser ma classe elle-même ?

CoRegisterPSClsid(IID_IWidget, CLSID_Widget);

Ce n'est pas son C'est vrai ; mais je ne comprends pas assez bien le standard COM marshaler. Le CLSID ne doit-il pas être l'une des classes de marsharling standard de COM, en implémentant IPSFactoryBuffer ?

Dans tous les cas, ça ne marche pas. Je reçois toujours l'erreur "Interface non enregistrée".

Enregistrer l'interface

Je peux bien sûr enregistrer mon interface dans le registre :

HKEY_CURRENT_USER\Software\Classes\Interface\{EBBC7C04-315E-11D2-B62F-006097DF5BD4}
      (default) = "IWidget"

Mais ça ne règle pas le problème. La spéléologie à travers le Interface j'ai remarqué que beaucoup d'entre eux spécifient une clé de registre. ProxyStubClsid32 l'entrée.

Lorsqu'une nouvelle interface est demandée sur un objet, les gestionnaires de proxy et de stub doivent résoudre l'IID demandé sur le CLSID de l'interface marshaler. Sous Windows NT 5.0, le magasin de classes maintient ces mappages dans le répertoire NT et ils sont mis en cache sur chaque machine hôte dans le registre local. Le site machine sont mis en cache à l'adresse suivante

HKEY_CLASSES_ROOT\Interface

et les mappages par utilisateur sont mis en cache à l'adresse suivante

HKEY_CURRENT_USER\Software\Classes\Interface

L'une de ces clés ou les deux contiendront une sous-clé pour chaque interface connue. Si un marshaler d'interface est installé sur l'interface, il y aura une sous-clé supplémentaire supplémentaire (ProxyStubClsid32) qui indique le CLSID du marshaler d'interface. marshaler.

Sauf que quelle classe implémente le marshaling ? Je n'ai pas de marshaler.

COM peut-il marshal automatiquement sur la base d'une TypeLibrary ?

Est-il possible, si j'enregistre une bibliothèque de types avec mon interface, que le marshaler standard de COM soit capable d'amorcer une classe proxy à la volée ?

J'ai enregistré mon interface ci-dessus. Maintenant, j'inclus manuellement la TypeLibrary :

HKEY_CURRENT_USER\Software\Classes\Interface\{EBBC7C04-315E-11D2-B62F-006097DF5BD4}\TypeLib
      (default) = "{38D528BD-4948-4F28-8E5E-141A51090580}"

Et si je surveille le registre pendant l'appel à CoMarshalInterface Je vois qu'il tente, et trouve, mon interface IID :

  • Opération : RegOpenKey
  • Chemin : HKCR\WOW6432Node\Interface\{EBBC7C04-315E-11D2-B62F-006097DF5BD4}
  • Résultat : SUCCESS

Il essaie ensuite de rechercher un ProxyStubClsid32 et échoue :

  • Opération : RegOpenKey
  • Chemin : HKCR\WOW6432Node\Interface\{668790E3-83CC-47E0-907F-A44BA9A99C8D}\ProxyStubClsid32
  • Résultat : NAME NOT FOUND

Mon espoir serait alors celle que le marshaler COM standard tente de rechercher :

  • Opération : RegOpenKey
  • Chemin : HKCR\WOW6432Node\Interface\{668790E3-83CC-47E0-907F-A44BA9A99C8D}\TypeLib

Mais ce n'est pas le cas.

Le marshaler d'OLE Automation

Selon Don Box, le Ole Automation marshaler (PSOAInterface - {00020424-0000-0000-C000-000000000046}) est capable de construire un stub/proxy à partir d'une bibliothèque de types :

Le maréchal-des-logis

Quand ces interfaces spécialement annotées sont rencontrées par RegisterTypeLib (ou LoadTypeLib en mode hérité), COM ajoute la fonction ProxyStubClsid32 pour l'interface avec la valeur {00020424-0000-0000-C0000-000000000046} . Ce GUID correspond à la classe Interface PSOA qui est enregistré comme vivant dans OLEAUT32.DLL, la DLL d'automatisation OLE. En raison de la DLL dans laquelle il vit, ce marshaler est parfois appelé le [oleautomation] bien qu'il soit également appelé "marshaler de bibliothèque de types" ou "marshaler universel". Je vais m'y référer en tant que marshaler de la bibliothèque de types, puisqu'il a vraiment très peu à voir avec IDispatch. (En fait, il est courant d'utiliser l'option [oleautomation] sur les interfaces qui ne dérivent pas d'IDispatch directement ou indirectement).

La fabrique de classes pour la bibliothèque de types fait quelque chose de très délicat dans son fichier CreateProxy y CreateStub routines. Plutôt que de renvoyer une table virtuelle compilée statiquement (ce qui est impossible étant donné que l'interface demandée n'existait pas lorsque OLEAUT32.DLL a été construit dans le cadre du système d'exploitation), le gestionnaire de bibliothèque de types construit en fait un proxy et un stub de style /Oicf basés sur la bibliothèque de types de l'interface. Parce qu'il n'y a pas de moyen efficace de trouver l'interface ITypeInfo pour une interface arbitraire, le LIBID y version de la bibliothèque de types de l'interface doit être stockée sous le nom de :

HKCR\Interface\{XXX}\TypeLib

clé de registre.

J'ai essayé de mettre PSOAInterface comme classe ProxyStub de mon interface :

  • Enregistrez la classe COM standard PSOAInterface comme notre stub proxy clsid

    HKEY_CURRENT_USER\Software\Classes\Interface\{EBBC7C04-315E-11D2-B62F-006097DF5BD4}\ProxyStubClsid32
           (default) = "{00020424-0000-0000-C0000-000000000046}"
  • Enregistrer notre bibliothèque de types pour notre interface

    HKEY_CURRENT_USER\Software\Classes\Interface\{EBBC7C04-315E-11D2-B62F-006097DF5BD4}\TypeLib
           (default) = "{38D528BD-4948-4F28-8E5E-141A51090580}"
           Version   = "1.0"
  • La bibliothèque de types elle-même est déjà enregistrée :

    HKEY_CURRENT_USER\Software\TypeLib\{38D528BD-4948-4F28-8E5E-141A51090580}\1.0\0\win32
           (default) = "D:\Junk\ComLibraryProxyTest\Win32\Project1.dll"

Mais il échoue toujours.

  • On peut y lire HKCR\Interface\[IID_IWidget]
  • On peut y lire HKCR\Interface\[IID_IWidget]\ProxyStubClsid32

Mais ça :

  • ne lit jamais : HKCR\Interface\[IID_IWidget]\TypeLib

Il n'y a donc pas de proxy pour l'objet.

Questions

  • Est-ce que c'est possible pour la norme COM "Ole Automation" marshaler pour construire une classe proxy à partir d'une bibliothèque de types au moment de l'exécution ?
  • Est-il possible pour moi pour construire une classe proxy à partir d'une bibliothèque de types au moment de l'exécution ?

Contexte

CoMarshalInterface prend un pointeur d'interface en entrée et écrit le fichier représentation sérialisée du pointeur dans un flux d'octets fourni par l'appelant. Ce flux d'octets peut ensuite être transmis à un autre appartement, où l'interface CoUnmarshalInterface La fonction API utilise le flux d'octets pour renvoyer une interface d'interface qui est sémantiquement équivalent à l'objet original mais qui peut être légalement accessible dans l'appartement qui exécute la CoUnmarshalInterface appeler. Lors d'un appel CoMarshalInterface l'appelant doit indiquer à quelle distance l'appartement importateur est censé se trouver. COM définit une énumération pour exprimer cette distance :

enum MSHCTX {
    MSHCTX_INPROC = 4,           // in-process/same host
    MSHCTX_LOCAL = 0,            // out-of-process/same host
    MSHCTX_NOSHAREDMEM = 1,      //16/32 bit/same host
    MSHCTX_DIFFERENTMACHINE = 2, // off-host
};

Il est légal de spécifier une distance plus grande que celle requise, mais il est plus efficace d'utiliser le MSHCTX correct lorsque cela est possible. CoMarshalInterface permet également l'appelant de spécifier la sémantique de la mise en forme en utilisant les drapeaux de mise en forme suivants :

enum MSHLFLAGS {
    MSHLFLAGS_NORMAL,      // marshal once, unmarshal once
    MSHLFLAGS_TABLESTRONG, // marshal once, unmarshal many
    MSHLFLAGS_TABLEWEAK,   // marshal once, unmarshal many
    MSHLFlAGS_NOPING = 4   // suppress dist. garbage collection

Le marshaling normal (parfois appelé call marshaling) indique que le fichier ne doit être démarchée qu'une seule fois, et si d'autres objets de type supplémentaires sont nécessaires, des appels supplémentaires à CoMarshalInterface sont nécessaires. La table marshaling indique que la référence de l'objet marshaled peut être démarshaled zéro ou plusieurs fois sans nécessiter d'appels supplémentaires à CoMarshalInterface.

Je pense que toutes les bibliothèques de types d'objets COM, lorsqu'elles sont compilées par MIDL, peuvent créer automatiquement une fabrique de proxy/stub. Mais dans mon cas :

si le marshaler standard COM ne peut pas trouver une usine de proxy/stub pour une interface, il renvoie l'erreur REGDB_E_IIDNOTREG.

Il se peut que je doive le faire aussi :

  • utiliser CreateProxyFromTypeInfo et CreateStubFromTypeInfo pour créer mon propre proxy
  • laisser le marshaler COM standard créer automatiquement un proxy/stub s'il y a un chunk typeinfo associé au GUID de l'interface.

Lecture en prime

2voto

Roman R. Points 34579

Quoi CoMarshalInterface a réellement besoin est IMarshal (pointeur) pour l'interface donnée, afin que l'API puisse lui demander la magie du marshaling et, en particulier, demander à IMarshal::GetUnmarshalClass afin d'obtenir l'information qui va faire la magie inversée par la suite :

Cette méthode est appelée indirectement dans un appel à CoMarshalInterface, par n'importe quel code du processus serveur responsable de la mise en forme d'un pointeur vers une interface sur un objet. Ce code de marshalling est généralement un stub généré par COM pour l'une des nombreuses interfaces qui peuvent marshaler un pointeur vers une interface implémentée sur un objet entièrement différent.

Vous n'avez pas IMarshal implémenté sur votre widget, vous allez donc le récupérer quelque part.

Comme vous avez commencé votre question en mentionnant que vous "voulez maréchalisez une interface vers un autre fil" et que le commentaire du code dit "Créer notre widget", il y a une chance que vous puissiez utiliser IMarhsal mise en œuvre de maréchal-ferrant à filetage libre . La question ne fournit pas d'informations permettant de dire si cette solution est possible et/ou acceptable.

Pour en revenir à la difficulté d'obtenir un marshaler, vous essayez de contourner le problème en utilisant un marshaler "standard" en "enregistrant l'interface" :

Pourquoi le maréchal des logis standard a-t-il tant de problèmes ?

[...]

Je peux bien sûr enregistrer mon interface dans le registre.

Eh bien, ce n'est pas comme ça que les choses fonctionnent en réalité.

L'enregistrement d'une interface n'est pas simplement une clé de registre qui dit "ok, cet IID a sa propre clé dans le registre". Le but d'un tel enregistrement est d'indiquer où chercher la paire proxy/stub pour cette interface. Votre création manuelle d'entrées de registre ne peut être utile ici si vous n'avez pas de DLL proxy/stub pour l'interface en question. Si vous l'aviez, vous n'auriez qu'à la regsvr32 de la manière habituelle pour créer les clés de registre.

Le soi-disant marshaler standard, votre prochaine tentative, n'est pas censé marshaler une interface et votre IWidget en particulier. OLE fournit un marshaler appelé "PSOAInterface", qui est capable de fournir des paires proxy/stub pour les interfaces compatibles avec OLE Automation. Rien que pour elles ! Le marshaler n'a pas tant de problèmes, il n'en a en fait qu'un seul : votre IWidget n'est probablement pas compatible, sinon vous n'auriez pas le problème en premier lieu.

Si votre IWidget était compatible, avait une bibliothèque de types associée, où elle était marquée comme [oleautomation] le processus d'enregistrement de la bibliothèque de types aurait automatiquement créé les clés de registre faisant référence à PSOAInterface et fournissant le nom de la bibliothèque de types. ProxyStubClsid32 . L'API de marshaling aurait alors choisi PSOAInterface pour votre widget, elle aurait récupéré la bibliothèque de types enregistrée, chargé les détails de l'interface, puis fourni une paire proxy/stub standard pour celle-ci et c'est tout. Le marshaler standard ne fonctionne que dans le cadre de ces contraintes et pas seulement pour toute interface pointée.

C'est-à-dire que vos options sont :

  • mettre en œuvre IMarshal sur le serveur de widgets
  • faire IWidget Compatible avec OLE Automation, avec la bibliothèque de types, de sorte que le processus d'enregistrement de la bibliothèque de types active le "marshaler standard" PSOAInterface pour votre interface.
  • construire, si possible et applicable, l'implémentation du proxy/stub pour votre interface automatiquement générée par le compilateur MIDL (vous pouvez vérifier le projet DLL ATL standard, créé à partir du modèle Visual Studio sur la façon dont cela peut être fait - il crée un projet supplémentaire avec le suffixe "PS", pour un proxy/stub DLL supplémentaire)
  • mettre en œuvre séparément IMarshal dans une DLL autonome, et l'enregistrez dans le registre et votre IWidget de sorte que l'API de marshaling le détecte lorsqu'elle tente de marshaler le pointeur d'interface (je suppose que c'est la seule option ici si votre IWidget est incompatible avec OLE et que vous avez des raisons de ne pas modifier l'implémentation originale)
  • utiliser le marshaler à fil libre dans le serveur de widgets si ses restrictions sont acceptables

Oh, attendez, il y en a un autre qui est bizarre. Si vous ne voulez pas ou ne pouvez pas vous permettre, ou si vous êtes réticent à changer le serveur COM (c'est-à-dire le widget) mais que vous pouvez modifier le code côté client à votre guise, vous pouvez créer un wrapper fin qui implémente deux interfaces IWidget (toutes les méthodes font suivre les appels au serveur réel) et IMarshal du côté client, et passe son IWidget à la CoMarshalInterThreadInterfaceInStream API. Cela obligera COM à utiliser votre marshaling sans modifier le serveur d'origine. Bien entendu, c'est à vous de faire le marshalage proprement dit par la suite. Il est peu probable que cela corresponde à votre besoin réel, et ce n'est qu'une note abstraite à la discussion (qui consiste principalement en des tentatives de faire l'impossible sans détails sur l'interface elle-même et les options disponibles sur la modification de l'implémentation du serveur).

1voto

Paulo Madeira Points 1986

TL;DR : Questions réelles :

  • Est-ce que c'est possible pour la norme COM "Ole Automation" marshaler pour construire une classe proxy à partir d'une bibliothèque de types au moment de l'exécution ?

Réponse courte : oui.

  • Est-il possible pour moi pour construire une classe proxy à partir d'une bibliothèque de types au moment de l'exécution ?

Réponse courte : oui, avec le marshaler de la bibliothèque des types et le IPSFactoryBuffer . Ou si vous êtes prêt à utiliser l'option non documentée CreateProxyFromTypeInfo y CreateStubFromTypeInfo .

Je me demande pourquoi vous voulez faire ça.


Cette question est truffée de faux-fuyants.

J'essaie de mettre en place une interface avec un autre fil.

(...) l'appel à CoMarshalThreadInterfaceInStream échoue avec :

REGDB_E_IIDNOTREG (0x80040155)
Interface not registered

Un code d'erreur utile, dans ce cas.

Allez directement à CoMarshalInterface

(...) qui échoue avec :

REGDB_E_IIDNOTREG (0x80040155)
Interface not registered

Ce n'est pas en changeant des appels d'API qui font essentiellement la même chose que vous pourrez résoudre votre problème.

Utiliser le marshaling standard

Ma classe n'implémente pas IMarhsal interface.

Pouvez-vous confirmer que ce n'est pas le cas INoMarshal , IStdMarshalInfo y IAgileObject soit, juste pour être exhaustif ?

L'interface n'est, en fait, pas enregistrée

C'était attendu.

Je sais que Windows vous fournit CoRegisterPSClsid

(...)

Ce que je peux essayer d'appeler, mais quoi clsid que j'utilise ?

CoRegisterPSClsid(IID_IWidget, ???);

Si vous n'avez pas de proxy/stub, pourquoi passer par là ?

Mais pour répondre à cette question, un CLSID standard de maréchal est généralement le même GUID que le premier IID trouvé dans un fichier IDL .

Enregistrer l'interface

Je peux bien sûr enregistrer mon interface dans le registre :

HKEY_CURRENT_USER\Software\Classes\Interface\{EBBC7C04-315E-11D2-B62F-006097DF5BD4}
      (default) = "IWidget"

Mais ça ne règle pas le problème. En fouillant dans les clés de registre de l'interface, je remarque que beaucoup d'entre elles spécifient un ProxyStubClsid32 entrée.

Vous devriez lire la documentation, au lieu de vous fier à ce que contiennent les autres entrées de registre.

(...) Je n'ai pas de maréchal.

Cela devrait être le vrai problème. Si cet objet vous appartient, comment comptez-vous l'utiliser ? Veuillez consulter la section inférieure de ma réponse.

COM peut-il marshal automatiquement sur la base d'une TypeLibrary ?

C'est clairement de la rhétorique. Pourquoi pensez-vous à une bibliothèque de types juste maintenant, au lieu de commencer ? Le reste de votre question corrobore.

Maintenant, j'inclus manuellement la TypeLibrary :

HKEY_CURRENT_USER\Software\Classes\Interface\{EBBC7C04-315E-11D2-B62F-006097DF5BD4}\TypeLib
      (default) = "{38D528BD-4948-4F28-8E5E-141A51090580}"

Vous le mentionnez deux fois.

Mais ça :

  • ne lit jamais : HKCR \Interface [IID_IWidget] \TypeLib

Le marshaler de bibliothèque de type a son propre cache de l'interface->type de bibliothèque. Pour effacer le cache, essayez de recréer les entrées du registre, de vous déconnecter et de vous reconnecter, ou enfin, de redémarrer.

L'objet peut mettre en œuvre IProvideClassInfo je ne sais pas si le marshaler de la bibliothèque de types se soucie réellement de QueryInterface pour que celui-ci récupère un runtime ITypeInfo pour travailler avec.


Ce sont les principaux types de maréchaux :

  • Les marshalers standard, qui sont généralement compilés dans une DLL à partir du code source C/C++ généré par MIDL (qui consiste principalement en des déclarations sur la façon de marshaler les types).

  • Le marshalleur de la bibliothèque de types, qui peut marshaler les types au moment de l'exécution sur la base d'informations sur les types compatibles avec l'automatisation.

  • Le marshaler free-threaded agrégé avec CoCreateFreeThreadedMarshaler ce qui évite le marshaling entre les threads d'un même processus.

  • Les marshalers personnalisés, qui font ce que le développeur veut, le plus souvent pour implémenter le marshal-by-value, ou un marshaler free-threading à l'époque où CoCreateFreeThreadedMarshaler n'existait pas

Les marshalers standard générés par MIDL consistent principalement en des déclarations sur la manière de marshaler les types en entrée et en sortie, de sorte que leur fonctionnement est identique à celui du marshaler de la bibliothèque de types. Selon Don Box, le résultat est très similaire.

Cependant, les déclarations réelles sont très différentes. Le marshaler de la bibliothèque de types travaille sur les informations de type qui sont censées fonctionner dans VB6 et (à l'exception de certaines choses, comme les types définis par l'utilisateur) dans les langages de script (principalement VBScript et JScript) et sont destinées à être consommées par les IDE (par exemple VB6, VBA, Delphi, OleView) et les générateurs de code d'interopérabilité (par exemple VC++ ). #import tlbimp.exe, "Import Type Library" de Delphi, Lisp. 1 2 ), etc.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X