Partager via


Helpers d’implémentation de serveur hors processus

Quatre fonctions d’assistance pouvant être appelées par des serveurs hors processus sont disponibles pour simplifier la tâche d’écriture du code serveur. Les clients COM et les serveurs COM in-process ne les appellent généralement pas. Ces fonctions sont conçues pour empêcher les conditions de concurrence dans l’activation du serveur lorsque les serveurs ont plusieurs appartements ou plusieurs objets de classe. Toutefois, ils peuvent également être facilement utilisés pour les serveurs d’objets monothread et de classe unique. Les fonctions sont les suivantes :

Pour s’arrêter correctement, un serveur COM doit suivre le nombre d’instances d’objet qu’il a instanciées et le nombre de fois où sa méthode IClassFactory::LockServer a été appelée. Ce n’est que lorsque ces deux nombres atteignent zéro qu’un serveur peut s’arrêter. Dans les serveurs COM à thread unique, la décision d’arrêter a été coordonnée avec les demandes d’activation entrantes, qui ont été sérialisées par la file d’attente de messages. Le serveur, après avoir reçu une version sur son objet final instance et décidé de l’arrêter, révoque ses objets de classe avant que d’autres demandes d’activation ne soient envoyées. Si une demande d’activation est arrivée après ce point, COM reconnaît que les objets de classe ont été révoqués et retourne une erreur au Gestionnaire de contrôle de service (SCM), ce qui entraîne l’exécution d’une nouvelle instance du processus de serveur local.

Toutefois, dans un serveur de modèle d’appartement, dans lequel différents objets de classe sont inscrits sur différents appartements, et dans tous les serveurs à threads libres, cette décision d’arrêt doit être coordonnée avec les demandes d’activation sur plusieurs threads afin qu’un thread du serveur ne décide pas de s’arrêter alors qu’un autre thread du serveur est occupé à remettre des objets de classe ou des instances d’objet. Une approche classique mais fastidieuse pour résoudre ce problème consiste à faire en sorte que le serveur, après avoir révoqué ses objets de classe, vérifie à nouveau son nombre de instance et reste en vie jusqu’à ce que toutes les instances aient été publiées.

Pour faciliter la gestion de ces types de conditions de concurrence par les rédacteurs de serveurs, COM fournit deux fonctions de comptage de référence :

Lorsque le nombre global de références par processus atteint zéro, COM appelle automatiquement CoSuspendClassObjects, ce qui empêche toute nouvelle demande d’activation d’arriver. Le serveur peut ensuite annuler l’inscription de ses différents objets de classe à partir de ses différents threads sans craindre qu’une autre demande d’activation arrive. Toutes les nouvelles demandes d’activation sont désormais gérées par le SCM qui lance une nouvelle instance du processus du serveur local.

Le moyen le plus simple pour une application serveur local d’utiliser ces fonctions consiste à appeler CoAddRefServerProcess dans le constructeur pour chacun de ses objets instance et dans chacune de ses méthodes IClassFactory::LockServer lorsque le paramètre fLock a la valeur TRUE. L’application serveur doit également appeler CoReleaseServerProcess dans le destructeur de chacun de ses objets instance et dans chacune de ses méthodes IClassFactory::LockServer lorsque le paramètre fLock a la valeur FALSE.

Enfin, l’application serveur doit prêter attention au code de retour de CoReleaseServerProcess, et si elle retourne 0, l’application serveur doit lancer son nettoyage, ce qui, pour un serveur avec plusieurs threads, signifie généralement qu’elle doit signaler à ses différents threads de quitter leurs boucles de message et appeler CoAddRefServerProcess et CoReleaseServerProcess. Si les fonctions de gestion de la durée de vie du processus serveur sont utilisées, elles doivent être utilisées à la fois dans les instances d’objet et dans la méthode LockServer ; sinon, l’application serveur peut être arrêtée prématurément.

Lorsqu’une requête CoGetClassObject est effectuée, COM contacte le serveur, marshale l’interface IClassFactory de l’objet de classe, retourne au processus client, désactive l’interface IClassFactory et retourne cette opération au client. À ce stade, les clients appellent généralement LockServer avec TRUE pour empêcher l’arrêt du processus serveur. Toutefois, il existe une fenêtre de temps entre le moment où l’objet de classe est marshalé et le moment où le client appelle LockServer dans lequel un autre client peut se connecter au même serveur, obtenir une instance et libérer cette instance, entraînant ainsi l’arrêt du serveur et laissant le premier client haut et sec avec un pointeur IClassFactory déconnecté. Pour éviter cette condition de concurrence, COM ajoute un appel implicite à LockServer avec TRUE à l’objet de classe lorsqu’il marshale l’interface IClassFactory et un appel implicite à LockServer avec FALSE lorsque le client libère l’interface IClassFactory . Par conséquent, il n’est pas nécessaire d’appeler à distance LockServer vers le serveur, et le proxy pour LockServer retourne simplement S_OK sans avoir à distance l’appel.

Il existe une autre condition de concurrence liée à l’activation lors de l’initialisation d’un processus serveur hors processus. Un serveur COM qui inscrit plusieurs classes appelle généralement CoRegisterClassObject avec REGCLS_LOCAL_SERVER pour chaque CLSID qu’il prend en charge. Une fois cette opération effectuée pour toutes les classes, le serveur entre sa boucle de message. Pour un serveur COM à thread unique, toutes les demandes d’activation sont bloquées jusqu’à ce que le serveur entre dans la boucle de message. Toutefois, pour un serveur de modèle d’appartement qui enregistre différents objets de classe dans différents appartements et pour tous les serveurs à threads libres, les demandes d’activation peuvent arriver plus tôt. Dans le cas des serveurs de modèles d’appartement, les demandes d’activation peuvent arriver dès qu’un thread est entré dans sa boucle de message. Dans le cas des serveurs à threads libres, une demande d’activation peut arriver dès que l’objet de première classe est inscrit. Étant donné qu’une activation peut se produire aussi tôt, il est également possible que la version finale se produise (et donc que le serveur commence à s’arrêter) avant que le reste du serveur ait eu la possibilité de terminer l’initialisation.

Pour éliminer ces conditions de concurrence et simplifier le travail de l’enregistreur de serveur, tout serveur qui souhaite inscrire plusieurs objets de classe auprès de COM doit appeler CoRegisterClassObject avec REGCLS_LOCAL_SERVER | REGCLS_SUSPENDED pour chaque CLSID différent pris en charge par le serveur. Une fois que toutes les classes ont été inscrites et que le processus serveur est prêt à accepter les demandes d’activation entrantes, le serveur doit effectuer un appel à CoResumeClassObjects. Cette fonction indique à COM d’informer le SCM de toutes les classes inscrites et commence à laisser les demandes d’activation entrer dans le processus serveur. L’utilisation de ces fonctions offre les avantages suivants :

  • Un seul appel est effectué au SCM, quel que soit le nombre de CLSID inscrits, ce qui réduit le temps d’inscription global (et donc le temps de démarrage de l’application serveur).
  • Si le serveur a plusieurs appartements et que différents CLSID sont inscrits dans différents appartements, ou si le serveur est un serveur à threads libres, aucune demande d’activation n’arrive tant que le serveur n’appelle pas CoResumeClassObjects, ce qui donne au serveur la possibilité d’inscrire tous ses CLSID et d’être correctement configuré avant d’avoir à traiter les demandes d’activation et les demandes d’arrêt possibles.

Responsabilités du serveur COM