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'unQueryInterface
demande pour leIMarshal
l'interface. La plupart des objets n'implémentent pas l'interfaceIMarshal
et échouer cetteQueryInterface
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'optionIMarshal
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'interfaceIMarshal
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
- Old New Thing : Quelles sont les règles pour CoMarshalInterThreadInterfaceInStream et CoGetInterfaceAndReleaseStream ?
- Old New Thing : Pourquoi est-ce que je reçois l'erreur REGDB_E_IIDNOTREG lorsque j'appelle une méthode qui renvoie une interface ?
- Old New Thing : Pourquoi est-ce que j'obtiens E_NOINTERFACE lorsque je crée un objet qui supporte cette interface ?
- Don Box - MSJ : Standard Marshalling dans COM ( archives )
- Disparition mystérieuse d'échantillons provenant des SDK de Microsoft