| Feed: Notes monopolistiques Titre: Notes d'étude sur "COM Principes et applications" | Auteur: FGS_FGS Commentaires |
| Notes d'étude "COM Principes et applications" - Partie 1 COM Principes http://savetime.delphibs.com Heure de début: 2004.1.30 Dernière modification: 2004.2.1 Le format de cet article est: Le texte est automatiquement enveloppé par la fenêtre; tous les codes sont limités par 80 caractères; (Le contenu de cet article est essentiellement extrait du livre "COM Principes et applications". Le droit d'auteur appartient à l'auteur Pan Aimin. Veuillez ne pas l'utiliser dans les médias publics) Table des matières ==============================================. =================== ============================================. ========= ⊙ Présentation du chapitre 1 Qu'est-ce que com COM OBJETS ET INTERFACES Modèle de processus com Com réutilisabilité ⊙ Chapitre 2 Modèle d'objet COM Identifiant globalement unique GUID Objet com Interface com Interface Description Langue Idl Interface iunknown Principes d'interface pour les objets com ⊙ Chapitre 3 Implémentation de COM Informations sur l'enregistrement des composants COM Enregistrer les composants COM Class Factory et DllGetObjectClass Fonction CogetClassObject COCREATEINSTANCE / COCREATEINSTANCEEX FONCTIONS Initialisation de la bibliothèque com Gestion de la mémoire de la bibliothèque com Chargement et désinstallation des programmes de composants Fonctions communes pour la bibliothèque com Type hresult ⊙ CHAPITRE 4 COMENTS CARACTÉRISTIQUES Réutilisabilité: inclusion et agrégation Traiter la transparence (à apprendre) Sécurité (à apprendre) Fonctionnalités multithreading (à apprendre) ⊙ Chapitre 5 Développement d'applications COM avec visuel C ++ Description de certains fichiers d'en-tête fournis par Win32 SDK Quelques macros liées à l'interface COM ==============================================. =================== ============================================. ========= texte ==============================================. =================== ============================================. ========= ⊙ Présentation du chapitre 1 ==============================================. =================== ============================================. ========= Qu'est-ce que com -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- COM est une norme de composante proposée par Microsoft. Dans la norme COM, un programme de composants est également appelé module. Un programme de composants peut contenir un ou plusieurs objets composants. ) est un opérateur de code qui fournit des objets com. Les objets COM sont différents du concept d'objet dans les langues axées sur les objets généraux (tels que C ++). Les objets com sont indépendants du langage. Cette fonctionnalité est possible pour interagir avec des objets composants développés par différents langages de programmation. -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- COM OBJETS ET INTERFACES -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Semblable au concept d'un objet en C ++, un objet est une instance d'une classe; et une classe est une définition qui combine un ensemble de données et de fonctions associées. Une application qui utilise un objet (ou un autre objet) est appelée client, parfois aussi appelée utilisateur de l'objet. Une interface est un ensemble de fonctions logiquement liées, et ses fonctions sont également appelées fonctions membres d'interface. Selon Custom, les noms d'interface sont souvent préfixés avec "I". Les objets offrent aux clients diverses formes de services via des fonctions membres de l'interface. Dans le modèle COM, l'objet lui-même est invisible pour le client, et lorsque le client demande le service, il ne peut être effectué que via l'interface. Chaque interface est identifiée par un identifiant unique à l'échelle mondiale 128 bits (GUID). Le client obtient le pointeur d'interface à travers le GUID, puis passe le pointeur d'interface, et le client peut appeler sa fonction membre correspondante. Semblable aux interfaces, chaque composant est également identifié par un GUID 128 bits, appelé CLSID (identifiant de classe, identifiant de classe ou ID de classe). En fait, une fois que le client crée avec succès l'objet, il obtient un pointeur vers une interface de l'objet. It. Selon la spécification com, si un objet COM implémente plusieurs interfaces, toute autre interface de l'objet peut être obtenue à partir d'une certaine interface. À partir de ce processus, nous pouvons également voir que le client ne traite que l'objet COM via des interfaces, et l'objet n'est qu'un ensemble d'interfaces pour le client. -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Modèle de processus com -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Les objets des composants de service fournis par COM ont deux modèles de processus lors de la mise en œuvre: objets en cours et objets hors de processus. S'il s'agit d'un objet en cours, il s'exécute dans l'espace de processus client; Programme de service en cours: Le programme de service est chargé dans l'espace de processus du client. Programmes de service locaux: Le programme de service s'exécute sur la même machine que le programme client. Programme de service à distance: Le programme de service s'exécute sur une machine différente de celle du client et peut être soit un module DLL ou un fichier EXE. Si le programme de service distant est implémenté sous forme DLL, la machine distante crée un processus proxy. Bien que les objets COM aient des modèles de processus différents, cette différence est transparente pour les programmes clients. Cependant, lors de la mise en œuvre d'objets COM, vous devez toujours choisir soigneusement le modèle de processus. L'avantage des modèles en cours est qu'ils sont efficaces, mais les composants instables entraîneront un plan du processus client, de sorte que les composants peuvent met en danger le client; -Les modèles de processus auront également des problèmes, ce qui peut être dû au fait que les composants et les clients du processus sont dans le même espace d'adresse, il y a une forte possibilité de conflit? La stabilité, et le processus de composant ne mettra pas en danger le programme client. -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Com réutilisabilité -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Étant donné que la norme COM est basée sur le niveau de code binaire, la réutilisabilité des objets COM est différente du processus de réutilisation des objets dans les langages généraux orientés objet tels que C ++. Pour le programme client d'objet COM, il utilise uniquement les services fournis par l'objet via l'interface, et il ne connaît pas le processus d'implémentation à l'intérieur de l'objet. Plutôt que la mise en œuvre spécifique. Com utilise deux mécanismes pour réaliser la réutilisation des objets. Nous supposons qu'il existe deux objets com, et l'objet 1 espère réutiliser la fonction de l'objet 2. Nous appelons l'objet 1 un objet externe et l'objet 2 un objet interne. (1) Voie inclusive. L'objet 1 contient l'objet 2. Lorsque l'objet 1 doit utiliser la fonction de l'objet 2, il peut simplement remettre l'implémentation dans l'objet 2. Bien que l'objet 1 et l'objet 2 prennent en charge la même interface, l'objet 1 est lors de l'implémentation de l'interface. , l'implémentation de l'objet 2 est appelée. (2) Méthode d'agrégation. L'objet 1 soumet simplement l'interface de l'objet 2 au client. exister. ==============================================. =================== ============================================. ========= ⊙ Chapitre 2 Modèle d'objet COM ==============================================. =================== ============================================. ========= Identifiant globalement unique GUID -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- La spécification COM utilise un guid d'identifiant unique 128 bits unique pour identifier les objets et les interfaces, qui sont des nombres aléatoires et ne nécessitent pas d'agences spécialisées pour allouer et gérer. Parce que Guid est un nombre aléatoire, l'unicité n'est pas absolument garantie, mais la possibilité de doubler les identificateurs est très faible. En théorie, si une machine génère 100 000 000 de guids par seconde, il peut être garanti que 3240 ans (au sens de la probabilité) ne seront pas répétés. Le GUID peut être décrit en C / C ++ en utilisant cette structure: typedef struct _guid { Dword data1; Word data2; Word Data3; Data octet4 [8]; } GUID; Exemple: {64BF4372-1007-B0AA-444553540000} Vous pouvez définir un GUID comme suit: extern "c" const Guid clsid_myspellchecker = {0x54bf0093, 0x1048, 0x399d, {0xb0, 0xa3, 0x45, 0x33, 0x43, 0x90, 0x47, 0x47}}; Visual C ++ fournit deux programmes pour générer des guids: uUidgen.exe (ligne de commande) et Guidgen.exe (dialogue). La bibliothèque COM fournit les fonctions API suivantes qui peuvent générer des guides: HRESULT COCREATEGUID (GUID * PGUID); Si Guid est créé avec succès, la fonction renvoie S_OK et PGUID pointe vers la valeur de Guid résultante. -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Objet com -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Dans la spécification com, les objets COM ne sont pas strictement définis, mais COM fournit un modèle de composant orienté objet, et les composants COM fournissent aux clients des entités encapsulées sous forme d'objet. L'entité dans laquelle le programme client interagit avec le programme COM est un objet COM. -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Interface com -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Techniquement parlant, une interface est une structure de données qui contient un ensemble de fonctions, à travers laquelle le code client peut appeler les fonctions des objets composants. L'interface définit un ensemble de fonctions membres, qui sont toutes les informations exposées par des objets composants. Habituellement, nous appelons la table de fonctions de fonction d'interface Table Virtual (VTable), et le pointeur vers le VTable est pvtable. Pour une interface, sa table de fonctions virtuelles est déterminée, donc le nombre de fonctions membres de l'interface est inchangée, et l'ordre des fonctions membres est également inchangé; pour chaque fonction membre, ses paramètres et la valeur de retour sont également définis. Dans la définition d'une interface, toutes ces informations doivent être déterminées au niveau binaire. Pointeur d'interface ----> PvTable ----> Fonction du pointeur 1 -> | ----------- | M_DATA1 Pointer Fonction 2 -> | Implémentation d'objet | Fonction de pointeur M_DATA2 3 -> | ------------ | Le premier paramètre de chaque fonction membre de l'interface est un pointeur vers l'instance d'objet (= ceci). . Dans les fonctions des membres de l'interface, les variables de chaîne doivent utiliser les pointeurs de caractères Unicode. Par conséquent, si les caractères ANSI sont utilisés dans le programme des composants, deux expressions de caractères doivent être converties. Bien sûr, dans le cas de l'établissement de programmes de composants et de programmes clients, vous pouvez utiliser les types de paramètres que vous définissez vous-même tant qu'ils sont compatibles avec les types de paramètres que COM peut reconnaître. Visual C ++ fournit deux conversions de chaînes: Espace de noms _com_util { Bstr convertsstringtobstr (const char * psrc) throw (_com_error); Bstr convertBstrtoString (bstr psrc) throw (_com_error); } BSTR est une chaîne de largeur à deux octets, qui est le type de données automatisé le plus couramment utilisé. -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Interface Description Langue Idl -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- La spécification COM utilise la spécification DCE de l'OSF pour décrire l'interface d'appel distant IDL (langue description de l'interface) et se développe pour former la langue description de l'interface com. Le langage de description de l'interface fournit une méthode de description pour les interfaces qui ne dépendent d'aucune langue, il peut donc devenir un langage commun entre les programmes composants et les programmes clients. Le langage de description de l'interface IDL utilisé par la spécification COM peut non seulement être utilisé pour définir l'interface com, mais également définir certains types de données couramment utilisés et des structures de données personnalisées pour les fonctions membres de l'interface, nous pouvons définir le type et la sortie d'entrée de chaque paramètre de paramètre. . IDL prend en charge les types de pointeurs, qui sont très similaires à C / C ++. Par exemple: Interface Idictionnaire { Hresult initialize () HRESULT LOADLIBRARY ([IN] String); HResult insertword ([in] string, [in] string); HResult Deleteword ([in] string); HRESULT Lookupword ([in] String, [out] String *); HRESULT RESTORELIBRARY ([in] String); HRESULT FREELIBRARY (); } Microsoft Visual C ++ fournit des outils MIDL qui peuvent compiler l'interface IDL Description des fichiers en C / C ++ - Interface compatible Description Fichiers d'en-tête (.h). -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Interface iunknown -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Définition IDL d'Iunknown: interface iunknown { HRESULT QUARYINTERFACE ([IN] REFIID IID, [OUT] void ** PPV); Ulong AddRef (vide); Libération ulong (vide); } Définition C ++ d'Iunkown: classe iunknown { Virus hResult _stdcall QueryInterface (const iid & iid, void ** ppv) = 0; virtuel ulong _stdcall addRef () = 0; virus ulong _stdcall release () = 0; } -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Principes d'interface pour les objets com -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- La spécification COM définit les règles suivantes pour la fonction QueryInterface: 1. Pour différents pointeurs d'interface du même objet, l'interface iunknown obtenue par requête doit être exactement la même. Autrement dit, le pointeur d'interface iunknown pour chaque objet est unique. Par conséquent, pour deux pointeurs d'interface, nous pouvons déterminer s'ils pointent vers le même objet en jugeant si les interfaces iunknown qu'ils interrogent sont égales. 2. Réflexivité de l'interface. Interroger une interface elle-même devrait toujours réussir, par exemple: Pidictionary-> QueryInterface (iid_dictionary, ...) devrait renvoyer S_OK. 3. Symétrie d'interface. Si vous interrogez d'un pointeur d'interface vers un autre pointeur d'interface, le retour du deuxième pointeur d'interface vers le premier pointeur d'interface doit réussir, par exemple: pidctionary-> queryInterface (iid_spellcheck, (void **) & PispellCheck); Si la recherche réussit, la vérification de l'interface IID_Dictionary de PispellCheck réussira certainement. 4. Transitivité d'interface. Si vous interrogez le deuxième pointeur d'interface du premier pointeur d'interface et le troisième pointeur d'interface peut être interrogé à partir du deuxième pointeur d'interface, vous pouvez certainement interroger le premier pointeur d'interface du pointeur de troisième interface. 5. Temps de requête d'interface non pertinent. Si un certain pointeur d'interface peut être trouvé à un certain moment, le même pointeur d'interface sera vérifié à tout moment à l'avenir, et la requête sera certainement réussie. En bref, quelle que soit l'interface à partir de laquelle nous commençons, nous pouvons toujours atteindre n'importe quelle interface et nous pouvons toujours revenir à l'interface d'origine. ==============================================. =================== ============================================. ========= ⊙ Chapitre 3 Implémentation de COM ==============================================. =================== ============================================. ========= Informations sur l'enregistrement des composants COM -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Informations sur tous les composants de la machine actuelle HKEY_CLASS_ROOT / CLSID Composant en processus hkey_class_root / clsid / guid / inprocserver32 Composant hors processus Hkey_class_root / ClSid / Guid / LocalServer32 Catégorie dans quel composant (CATID) HKEY_CLASS_ROOT / CLSID / GUID / IMPLETED CATÉGORIES COM Interface Informations de configuration HKEY_CLASS_ROOT / INTERFACE Proxy dll / stub dll hkey_class_root / clsid / guid / proxystubclsid Hkey_class_root / clsid / guid / proxystubclsid32 Informations sur la bibliothèque de type hkey_class_root / typelib String Naming Progid hkey_class_root / (par exemple "comctl.trectrl") Composant Guid hkey_class_root / comtrl.treeControl / Clsid Numéro de version par défaut HKEY_CLASS_ROOT / COMTRL.TREEControl / Curver (par exemple curver = "comtrl.trectrl.1", alors Hkey_class_root / comtrl.treeControl.1 existe également) Toutes les catégories de composants de la machine actuelle Hkey_class_root / Catégories de composants Com fournit deux fonctions API CLSIDFROMPROGID et PROGIDFROMCLSID pour convertir Progid et CLSID. Si le composant COM prend en charge le même ensemble d'interfaces, ils peuvent être classés dans la même classe et un composant peut être classé en plusieurs classes. Par exemple, si tous les objets d'automatisation prennent en charge l'interface IDispatch, ils peuvent être classés comme "objets d'automatisation". Les informations sur la catégorie sont également décrites par un GUID, appelé CATID. L'objectif principal des catégories de composants est que les clients peuvent rapidement découvrir des types spécifiques d'objets de composants sur la machine. L'interface, maintenant à l'aide de catégories de composants, peut enregistrer le processus de requête. -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Enregistrer les composants COM -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- RegSrv32.exe est utilisé pour enregistrer un composant en cours, qui appelle les fonctions DLREGISTERSERVER et DLLUNGERGISTERSERVER de la DLL pour compléter l'enregistrement et l'annulation du programme de composants. Si l'opération est réussie, renvoyez vrai, sinon renvoyez false. Pour les programmes de composants hors de processus, la situation est légèrement différente car il s'agit d'un programme exécutable lui-même et il ne peut pas fournir de fonctions d'entrée pour les autres programmes à utiliser. Par conséquent, la spécification COM stipule que les composants hors de processus qui prennent en charge l'auto-inscription doivent prendre en charge deux paramètres de ligne de commande / Regserver et / Unregserver pour terminer les opérations d'enregistrement et d'annulation. Les paramètres de ligne de commande sont dépendants du cas et "/" peuvent être remplacés par "-". Si l'opération est réussie, le programme renvoie 0, sinon, le retour non 0 signifie une défaillance. -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Class Factory et DllGetObjectClass -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Class Factory est la base de production des objets com. L'usine de classe elle-même est également un objet com, qui prend en charge une interface spéciale iClassFactory: Classe Iclassfactory: public iunknown { Virtual hresult _stdcall CreateInstance (iunknown * punknownouter, const iid & iid, void ** ppv) = 0; Virtual Hresult _stdcall Lockserver (Bool Block) = 0; } La fonction de membre CreateInstance est utilisée pour créer l'objet com correspondant. Le premier paramètre PunkNownouter est utilisé pour le cas où la classe d'objet est agrégée et est généralement définie sur Null; pointeur d'interface. Les fonctions des membres de lockserver sont utilisées pour contrôler la durée de vie d'un composant. L'objet Class Factory est créé par la fonction DLL DllGetClassObject: HRESULT DllGetClassObject (const clsid & clsid, const iid & iid, (void **) ppv); Le premier paramètre de la fonction DllGetClassObject est le CLSID de l'objet à créer. Étant donné qu'un composant peut implémenter plusieurs classes d'objets COM, il est nécessaire de spécifier le CLSID dans les paramètres de la fonction DllGetClassObject pour créer l'usine de classe correcte. Les deux autres paramètres IID et PPV se réfèrent à l'interface spécifiée IID et au pointeur d'interface d'usine de classe de stockage, respectivement. Après avoir reçu l'instruction pour créer un objet, la bibliothèque Com appelle la fonction DllGetClassObject du composant dans le processus, crée l'objet de classe de classe à partir de la fonction et renvoie le pointeur d'interface de l'objet de classe d'usine. Une fois qu'une bibliothèque ou un client a un pointeur d'interface vers l'usine de classe, il peut créer l'objet COM correspondant via la fonction membre CreateInstance de l'iclassfactory. -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Fonction CogetClassObject -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Dans la bibliothèque com, il existe trois API qui peuvent être utilisées pour la création d'objets, à savoir CogetClassObject, CocreateInstnace et CocreateInstanceEx. En règle générale, le programme client appelle l'un d'eux pour compléter la création de l'objet et renvoie le pointeur d'interface initial de l'objet. La bibliothèque COM et l'usine de classe interagissent également à travers ces trois fonctions. HRESULT COGETCLASSOBJET (const clsid & clsid, dword dwclscontext, CoserveRinfo * pServerinfo, const iid & iid, (void **) ppv); La fonction CoGetClassObject trouve d'abord l'usine de classe de la classe COM spécifiée par CLSID, puis se connecte à l'objet d'usine de classe. S'il s'agit d'un objet de composant en cours, CoGetClassObject appelle le DllGetClassObject du module DLL pour introduire la fonction, passer les paramètres CLSID, IID et PPV à la fonction DllGettClassObject et renvoie le pointeur d'interface de l'objet Class Factory. Normalement, IID est l'identifiant iid_iclassfactory d'Iclassfactory. Si l'objet Class Factory prend également en charge d'autres interfaces qui peuvent être utilisées pour créer des opérations, d'autres identifiants d'interface peuvent également être utilisés. Par exemple, l'interface iClassfactory2 peut être priée de vérifier l'état de la licence de l'utilisateur au moment de la création. L'interface iClassfactory2 est une extension d'Iclassfactory, qui améliore la sécurité de la création de composants. Le paramètre dwclscontext spécifie la catégorie des composants, qui peut être spécifiée en tant que composant en cours, un composant hors de processus ou un objet de contrôle en cours (similaire à l'objet proxy d'un composant hors de processus, principalement utilisé dans la technologie OLE). Les paramètres IID et PPV correspondent aux paramètres de DllGetClassObject, respectivement, et sont utilisés pour spécifier l'interface IID et le pointeur d'interface pour stocker l'objet de classe. Le paramètre PSERVEINFO est utilisé pour spécifier les informations du serveur lors de la création d'objets distants. La situation est beaucoup plus compliquée si l'objet d'usine de classe créé par la fonction CoGetClassObject est situé dans un composant hors de processus. Tout d'abord, la fonction CoGetClassObject démarre le processus de composant, puis attend que le processus de composant enregistre l'usine de classe de l'objet de classe com qu'il prend en charge vers com. Ainsi, la fonction CoGetClassObject renvoie les informations d'usine de classe correspondantes dans com. Par conséquent, lorsqu'un processus hors composant est démarré par la bibliothèque COM (avec le paramètre de ligne de commande "/ incorporer"), il doit enregistrer les objets d'usine de classe COM pris en charge dans la fonction com via la fonction CoreGisterClassObject afin que la bibliothèque COM puisse Créer des objets com pour une utilisation. Lorsque le processus sort, la fonction CoreVokeClassObject doit être appelée pour informer com que l'objet d'usine de classe qu'il a enregistré n'est plus valide. Le programme composant appelle la fonction CoreGisterClassObject et la fonction CoreVokeClassObject doit être appariée pour garantir la cohérence des informations COM. -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- COCREATEINSTANCE / COCREATEINSTANCEEX FONCTIONS -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- HRESULT COCREATEInstance (const Clsid & Clsid, iunknown * punknownouter, Dword dwclscontext, const iid & iid, (void **) ppv); COCreateInstance est une fonction d'assistance enveloppée qui appelle réellement la fonction CogetClassObject à l'intérieur. La signification des paramètres CLSID et DWCLSContext de COCreateInstance sont cohérentes avec les paramètres correspondants de CoGetClassObject (les paramètres IID et PPV de COCreateInstance sont différents de l'information de CogetClassObject, l'une est l'information d'interface représentant l'objet, et l'autre est l'information d'interface représentant la classe usine). Le paramètre PunkNownouter est cohérent avec les paramètres correspondants dans la création de l'interface d'usine de classe, et est principalement utilisé dans le cas où les objets sont agrégés. La fonction COCreateInstance résume le processus de création d'objets via une usine de classe. COCreateInstance peut être implémentée avec le code suivant: (Note de sauvetage: l'application du pointeur PPV dans le code suivant semble être vide **) HRESULT COCREATEInstance (const Clsid & Clsid, iunknown * punknownouter, Dword dwclscontext, const iid & iid, void * ppv) { ICLASSFACTORY * PCF; HRESULT HR; hr = cogetClassObject (clsid, dwclscontext, null, iid_iclassfactory, (void *) pcf); if (échoué (hr)) retourne hr; hr = pcf-> createInstance (punknownouter, iid, (void *) ppv); pfc-> release (); retour hr; } À partir de ce code, nous pouvons voir que la fonction COCreateInstance utilise d'abord la fonction CoGetClassObject pour créer un objet d'usine de classe, puis utilise le pointeur d'interface de l'objet de classe pour créer un objet COM réel. retourné, afin que la classe soit mise en service. Cependant, l'utilisation de COCreateInstance ne crée pas d'objet sur la machine distante, car lors de l'appel CoGetClassObject, le troisième paramètre utilisé pour spécifier les informations du serveur est défini sur NULL. Si vous souhaitez créer un objet distant, vous pouvez utiliser la fonction d'extension de COCreateInstance COCreateInstanceEx: HRESULT COCREATEINSTANCEEX (const Clsid & ClSid, iunknown * Punknownouter, Dword dwclscontext, coserverinfo * pServerinfo, dword dwCount, Multi_qi * rgMultiqi); Les trois premiers paramètres sont les mêmes que COCreateInstance. Points d'interface. -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Initialisation de la bibliothèque com -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Avant d'appeler les fonctions de la bibliothèque Com, afin de rendre la fonction valide, la fonction d'initialisation de la bibliothèque Com doit être appelée: Hresult Coinnitialize (Imalloc * pmalloc); PMALLOC est utilisé pour spécifier un allocateur de mémoire et le principe d'allocation de mémoire peut être spécifié par l'application. Généralement, si nous définissons directement le paramètre sur NULL, la bibliothèque com utilisera l'allocateur de mémoire fourni par défaut. Valeur de retour: S_OK signifie que l'initialisation est réussie S_False indique que l'initialisation est réussie, mais cet appel n'est pas la première fois que la fonction d'initialisation est appelée dans ce processus. S_UNEXPECT indique qu'une erreur s'est produite pendant le processus d'initialisation et que l'application ne peut pas utiliser la bibliothèque com. Habituellement, un processus initialise la bibliothèque com une seule fois, et il n'est pas logique d'initialiser la bibliothèque COM plusieurs fois dans la même unité de module. La seule fonction qui n'a pas besoin d'initialiser la bibliothèque com est d'obtenir la version de la bibliothèque com: DWORD CobuildVersion (); Valeur de retour: numéro de version majeure 16 bits élevé Numéro de version inférieure à 16 chiffres Une fois que le programme COM a utilisé le service de bibliothèque COM, généralement avant la sortie du programme, il est nécessaire d'appeler la fonction de service de bibliothèque com terminée pour libérer les ressources maintenues par la bibliothèque com: void couninitialiser (vide); Remarque: Tout module de processus ou de programme qui appelle la fonction de Coins et Renvoie S_OK doit avoir un appel de fonction Couninitialize correspondant pour s'assurer que la bibliothèque COM utilise efficacement les ressources. (? Si Coinnitialize est appelé dans un module et renvoie S_OK, d'autres modules qui utilisent également la bibliothèque com disposeront d'erreurs après avoir appelé la fonction Counitialize? Ou la bibliothèque COM vérifiera-t-elle automatiquement les modules utilisés?) -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Gestion de la mémoire de la bibliothèque com -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Étant donné que les programmes de composants COM et les programmes clients sont connectés par le biais de normes de niveau binaire, toutes les opérations impliquant des interactions de mémoire entre les clients, les bibliothèques COM et les composants (allocation et version ne sont pas dans le même module) dans les applications COM doivent être utilisées cohérentes Memory Manager. La norme de gestion de la mémoire fournie par COM est en fait une interface Imalloc: // iid_imalloc: {00000002-0000-0000-C000-0000000046} classe Imalloc: public iunknown { void * alloc (ulong cb) = 0; void * realloc (void * pv, ulong cb) = 0; void gratuit (void * pv) = 0; Ulong getsize (void * pv) = 0; int didalloc (void * pv) = 0; void heapminimize () = 0; // Système d'exploitation pour l'optimisation des performances } Obtenez le pointeur d'interface Imalloc: HRESULT COGETMALLOC (DWORD DWMemContext, Imalloc ** ppmalloc); Le premier paramètre de la fonction COGETMALLOC DWMemContext est utilisé pour spécifier le type du gestionnaire de mémoire. La bibliothèque COM contient deux gestionnaires de mémoire, l'une est le gestionnaire de mémoire spécifié lors de l'initialisation ou de son gestionnaire par défaut interne, également connu sous le nom de gestionnaire de travail (tâche Allocator). MEMCTX_TASK Dans le paramètre DWMEMContext; processus et transmis à un deuxième processus, en utilisant cette mémoire dans le deuxième processus ou même en le libérant. Tant que la valeur de retour de la fonction est S_OK, PPMALLOC pointe vers le pointeur de l'interface Memory Manager de la bibliothèque com, et vous pouvez l'utiliser pour effectuer des opérations de mémoire. La bibliothèque COM résume trois fonctions d'API qui peuvent être utilisées pour l'allocation et la version de la mémoire: vide * cotaskmemalloc (Ulong CB); vide cotaskfree (vide * pv); vide cotaskmemrealloc (vide * pv, ulong cb); Ces trois fonctions sont attribuées à trois fonctions membre correspondant à Imalloc: alloc, realloc et libre. Exemple: comment un programme COM trouve la valeur progressive correspondante de la valeur CLSID: Wchar * pwprogid; char pszprogid [128]; HRESULT = :: PROGIDFROMCLSID (CLSID_DICTIONARY, & PWPROGID); if (hresult! = s_ok) { ... } WCSTOMBS (PSZPROGID, PWPROGID, 128); CotaskMemFree (PWProgid); Après avoir appelé la fonction COM PROGIDFROMCLSID à Retour, car la bibliothèque COM alloue l'espace mémoire pour la variable de sortie PWProgId, l'application doit appeler la fonction CotaskMemFree pour libérer de la mémoire après avoir utilisé la variable PWProgId. Cet exemple illustre une situation où la mémoire est allouée dans la bibliothèque com et la mémoire est libérée dans le programme d'appel. Certaines autres fonctions de la bibliothèque COM ont également des caractéristiques similaires, en particulier certaines fonctions qui contiennent des paramètres de sortie de longueur indéfinie. -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Chargement et désinstallation des programmes de composants -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Chargement des composants en cours: 客户程序调用COM 库的CoCreateInstance 或CoGetClassObject 函数创建COM 对象,在CoGetClassObject 函数中,COM 库根据系统注册表中的信息,找到类标识符CLSID 对应的组件程序(DLL 文件)的全路径,然后调用LoadLibrary(实际上是CoLoadLibrary)函数,并调用组件程序的DllGetClassObject 引出函数。DllGetClassObject 函数创建相应的类厂对象,并返回类厂对象的IClassFactory 接口。至此CoGetClassObject 函数的任务完成,然后客户程序或者CoCreateInstance 函数继续调用类厂对象的CreateInstance 成员函数,由它负责COM 对象的创建工作。 CoCreateInstance |-CoGetClassObject |-Get CLSID -> DLLfile path |-CoLoadLibrary |-DLLfile.DllGetClassObject |-return IClassFactory |-IClassFactory.CreateInstnace 进程外组件的装载: 在COM 库的CoGetClassObject 函数中,当它发现组件程序是EXE 文件(由注册表组件对象信息中的LocalServer 或LocalServer32 值指定)时,COM 库创建一个进程启动组件程序,并带上“/Embedding”命令行参数,然后等待组件程序;而组件程序在启动后,当它检查到“/Embedding”命令行参数后,就会创建类厂对象,然后调用CoRegisterClassObject 函数把类厂对象注册到COM 中。当COM 库检查到组件对象的类厂之后,CoGetClassObject 函数就把类厂对象返回。由于类厂与客户程序运行在不同的进程中,所以客户程序得到的是类厂的代理对象。一旦客户程序或COM 库得到了类厂对象,它就可以完成组件对象的创建工作。 进程内对象和进程外对象的不同创建过程仅仅影响了CoGetClassObject 函数的实现过程,对于客户程序来说是完全透明的。 CoGetClassObject |-LocalServer/LocalServer32 |-Execute EXE /Embedding |-Create class factory |-CoRegisterClassObject ( class factory ) |-return class factory (proxy) 进程内组件的卸载: 只有当组件程序满足了两个条件时,它才能被卸载,这两个条件是:组件中对象数为0,类厂的锁计数为0。满足这两个条件时,DllCanUnloadNow 引出函数返回TRUE。COM 提供了一个函数CoFreeUnusedLibraries,它会检测当前进程中的所有组件程序,当发现某个组件程序的DllCanUnloadNow 函数返回TRUE 时,就调用FreeLibrary 函数(实际上是CoFreeLibrary 函数)把该组件从程序从内存中卸出。 该由谁来调用CoFreeUnusedLibraries 函数呢?因为在组件程序执行过程中,它不可能把自己从内存中卸出,所以这个任务应该由客户来完成。客户程序随时都可以调用CoFreeUnusedLibraries 函数完成卸出工作,但通常的做法是,在程序的空闲处理过程中调用CoFreeUnusedLibraries 函数,这样做既可以避免程序中处处考虑对CoFreeUnusedLibraries 函数的调用,又可以使不再使用的组件程序得到及时清除,提高资源的利用率,COM 规范也推荐这种做法。 进程外组件的卸载: 进程外组件的卸载比较简单,因为组件程序运行在单独的进程中,一旦其退出的条件满足,它只要从进程的主控函数返回即可。在Windows 系统中,进程的主控函数为WinMain。 前面曾经说过,在组件程序启动运行时,它调用CoRegisterClassObject 函数,把类厂对象注册到COM 中,注册之后,类厂对象的引用计数始终大于0,因此单凭类厂对象的引用计数无法控制进程的生存期,这也是引入类厂对象的加锁和减锁操作的原因。进程外组件的载条件与DllCanUnloadNow 中的判断类似,也需要判断COM 对象是否还存在、以及判断是否锁计数器为0,只有当条件满足了,进程的主函数才可以退出。 从原则上讲,进程外组件程序的卸载就是这么简单,但实际上情况可能复杂一些,因为有些组件程序在运行过程中可以创建自己的对象,或者包含用户界面的程序在运行过程中,用户手工关闭了进程,那么进程对这些动作的处理要复杂一些。例如,组件程序在运行过程中,用户又打开了一个文件并进行操作,那么即使原先创建的对象被释放了,而且锁计数器也为0,进程也不能退出,它必须继续为用户服务,就像是用户打开的进程一样。对这种程序,可以增加一个“用户控制”标记flag,如果flag 为FALSE,则可以按简单的方法直接退出程序即可;如果flag 为TRUE,则表明用户参与了控制,组件进程不能马上退出,但应该调用CoRevokeClassObject 函数以便与CoRegisterClassObject 调用相响呼应,把进程留给用户继续进行。 如果组件程序在运行过程中,用户要关闭进程,而此时并不满足进程退出条件,那么进程可以采取两种办法:第一种方法,把应用隐藏起来,并把flag 标记设置为FALSE,然后组件程序继续运行直到卸载条件满足为止;另一种办法是,调用CoDisconnectObject 函数,强迫脱离对象与客户之间的关系,并强行终止进程,这种方法比较粗暴,不提倡采用,但不得已时可以也使用,以保证系统完成一些高优先级的操作。 -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- COM 库常用函数 -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- 初始化函数CoBuildVersion 获得COM 库的版本号 CoInitialize COM 库初始化 CoUninitialize COM 库功能服务终止 CoFreeUnusedLibraries 释放进程中所有不再使用的组件程序 GUID 相关函数IsEqualGUID 判断两个GUID 是否相等 IsEqualIID 判断两个IID 是否相等 IsEqualCLSID 判断两个CLSID 是否相等(*为什么要3个函数) CLSIDFromProgID 字符串组件标识转换为CLSID 形式 StringFromCLSID CLSID 形式标识转化为字符串形式 IIDFromString 字符串转换为IID 形式 StringFromIID IID 形式转换为字符串 StringFromGUID2 GUID 形式转换为字符串(*为什么有2) 对象创建函数CoGetClassObject 获取类厂对象 CoCreateInstance 创建COM 对象 CoCreateInstanceEx 创建COM 对象,可指定多个接口或远程对象 CoRegisterClassObject 登记一个对象,使其它应用程序可以连接到它 CoRevokeClassObject 取消对象的登记 CoDisconnectObject 断开其它应用与对象的连接 内存管理函数CoTaskMemAlloc 内存分配函数 CoTaskMemRealloc 内存重新分配函数 CoTaskMemFree 内存释放函数 CoGetMalloc 获取COM 库内存管理器接口 -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- HRESULT 类型 -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- 大多数COM 函数以及一些接口成员函数的返回值类型均为HRESULT 类型。HRESULT 类型的返回值反映了函数中的一些情况,其类型定义规范如下: 31 30 29 28 16 15 0 |-----|--|------------------------|-----------------------------------| 类别码(30-31) 反映函数调用结果: 00 调用成功 01 包含一些信息 10 警告 11 错误 自定义标记(29) 反映结果是否为自定义标识,1 为是,0 则不是; 操作码(16-28) 标识结果操作来源,在Windows 平台上,其定义如下: #define FACILITY_WINDOWS 8 #define FACILITY_STORAGE 3 #define FACILITY_RPC 1 #define FACILITY_SSPI 9 #define FACILITY_WIN32 7 #define FACILITY_CONTROL 10 #define FACILITY_NULL 0 #define FACILITY_INTERNET 12 #define FACILITY_ITF 4 #define FACILITY_DISPATCH 2 #define FACILITY_CERT 11 操作结果码(0-15) 反映操作的状态,WinError.h 定义了Win32 函数所有可能返回结果。 以下是一些经常用到的返回值和宏定义: S_OK 函数执行成功,其值为0 (注意,其值与TRUE 相反) S_FALSE 函数执行成功,其值为1 S_FAIL 函数执行失败,失败原因不确定 E_OUTOFMEMORY 函数执行失败,失败原因为内存分配不成功 E_NOTIMPL 函数执行失败,成员函数没有被实现 E_NOTINTERFACE 函数执行失败,组件没有实现指定的接口 不能简单地把返回值与S_OK 和S_FALSE 比较,而要用SECCEEDED 和FAILED 宏进行判断。 ==============================================. =================== ============================================. ========= ⊙ 第四章COM 特性 ==============================================. =================== ============================================. ========= 可重用性:包容和聚合 -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- 包容模型: 组件对象在接口的实现代码中执行自身创建的另一个组件对象的接口函数(客户/服务器模型)。这个对象同时实现了两个(或更多)接口的代码。 聚合模型: 组件对象在接口的查询代码中把接口传递给自已创建的另一个对象的接口查询函数,而不实现该接口的代码。另一个对象必须实现聚合模型(也就是说,它知道自己正在被另一个组件对象聚合),以便QueryInterface 函数能够正常运作。 在组件对象被聚合的情况下,当客户请求它所不支持的接口或者请求IUnknown 接口时,它必须把控制交给外部对象,由外部对象决定客户程序的请求结果。 聚合模型体现了组件软件真正意义上的重用。 聚合模型实现的关键在CoCreateInstance 函数和IClassFactory 接口: HRESULT CoCreateInstance(const CLSID& clsid, IUnknown *pUnknownOuter, DWORD dwClsContext, const IID& iid, (void **)ppv); // class IClassFactory : public IUnknown virtual HRESULT _stdcall CreateInstance(IUnknown *pUnknownOuter, const IID& iid, void **ppv) = 0; 其中pUnknownOuter 参数用于指定组件对象是否被聚合。如果pUnknownOuter 参数为NULL,说明组件对象正常使用,否则说明被聚合使用,pUnknownOuter 是外部组件对象的接口指针。 聚合模型下的被聚合对象的引用计数成员函数也要进行特别处理。在未被聚合的情况下,可以使用一般的引用计数方法。在被聚合时,由客户调用AddRef/Release 函数时,必须转向外部组件对象的AddRef/Release 方法。这时,外部组件对象要控制被聚合的对象必须采用其它的引用计数接口。 -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- 进程透明性(待学) 安全性(待学) 多线程特性(待学) -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- ==============================================. =================== ============================================. ========= ⊙ 第五章用Visual C++ 开发COM 应用 ==============================================. =================== ============================================. ========= Win32 SDK 提供的一些头文件的说明 -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Unknwn.h 标准接口IUnknown 和IClassFacatory 的IID 及接口成员函数的定义 Wtypes.h 包含COM 使用的数据结构的说明 Objidl.h 所有标准接口的定义,即可用于C 语言风格的定义,也可用于C++ 语言 Comdef.h 所有标准接口以及COM 和OLE 内部对象的CLSID ObjBase.h 所有的COM API 函数的说明 Ole2.h 所有经过封装的OLE 辅助函数 -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- 与COM 接口有关的一些宏 -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- DECLARE_INTERFACE(iface) 声明接口iface,它不从其他的接口派生 DECLARE_INTERFACE_(iface, baseiface) 声明接口iface,它从接口baseiface 派生 STDMETHOD(method) 声明接口成员函数method,函数返回类型为HRESULT STDMETHOD_(type, method) 声明接口成员函数method,函数返回类型为type ==============================================. =================== ============================================. ========= ⊙ 结束 ==============================================. =================== ============================================. ========= |