Out-of-Process-Serverimplementierungshilfsprogramme

Vier Hilfsfunktionen, die von Out-of-Process-Servern aufgerufen werden können, sind verfügbar, um das Schreiben von Servercode zu vereinfachen. COM-Clients und COM-In-Process-Server rufen sie in der Regel nicht auf. Diese Funktionen wurden entwickelt, um Racebedingungen bei der Serveraktivierung zu verhindern, wenn die Server über mehrere Apartments oder mehrere Klassenobjekte verfügen. Sie können jedoch auch problemlos für Einzelthread- und Einzelklassenobjektserver verwendet werden. Die Funktionen sind wie folgt:

Zum ordnungsgemäßen Herunterfahren muss ein COM-Server nachverfolgen, wie viele Objektinstanzen er instanziiert hat und wie oft seine IClassFactory::LockServer-Methode aufgerufen wurde. Nur wenn beide Zähler 0 erreichen, kann ein Server heruntergefahren werden. Bei Singlethread-COM-Servern wurde die Entscheidung zum Herunterfahren mit eingehenden Aktivierungsanforderungen koordiniert, die von der Nachrichtenwarteschlange serialisiert wurden. Wenn der Server eine Freigabe für sein endgültiges Objekt erhalten instance und sich für das Herunterfahren entscheidet, würde er seine Klassenobjekte widerrufen, bevor weitere Aktivierungsanforderungen gesendet wurden. Wenn nach diesem Punkt eine Aktivierungsanforderung eingeht, erkennt COM, dass die Klassenobjekte widerrufen wurden, und gibt einen Fehler an den Dienststeuerungs-Manager (SCM) zurück, der dann dazu führen würde, dass eine neue instance des lokalen Serverprozesses ausgeführt wird.

In einem Apartmentmodellserver, in dem unterschiedliche Klassenobjekte in verschiedenen Apartments registriert sind, und in allen Freethreadservern muss diese Entscheidung zum Herunterfahren jedoch mit Aktivierungsanforderungen über mehrere Threads hinweg koordiniert werden, sodass ein Thread des Servers nicht zum Herunterfahren entscheidet, während ein anderer Thread des Servers mit der Übergabe von Klassenobjekten oder Objektinstanzen beschäftigt ist. Ein klassischer, aber umständlicher Ansatz zur Lösung dieses Problems besteht darin, dass der Server, nachdem er seine Klassenobjekte widerrufen hat, seine instance Anzahl erneut überprüfen und am Leben bleiben, bis alle Instanzen freigegeben wurden.

Um Serverautoren die Behandlung dieser Arten von Racebedingungen zu erleichtern, stellt COM zwei Referenzzählfunktionen bereit:

Wenn die globale Referenzanzahl pro Prozess 0 erreicht, ruft COM automatisch CoSuspendClassObjects auf, wodurch verhindert wird, dass neue Aktivierungsanforderungen eingehen. Der Server kann dann die Registrierung seiner verschiedenen Klassenobjekte aus seinen verschiedenen Threads nach Belieben aufheben, ohne sich sorgen zu müssen, dass eine weitere Aktivierungsanforderung eintreffen könnte. Alle neuen Aktivierungsanforderungen werden fortan vom SCM verarbeitet, der eine neue instance des lokalen Serverprozesses startet.

Die einfachste Möglichkeit für eine lokale Serveranwendung, diese Funktionen zu nutzen, besteht darin, CoAddRefServerProcess im Konstruktor für jedes seiner instance-Objekte und in jeder ihrer IClassFactory::LockServer-Methoden aufzurufen, wenn der fLock-ParameterTRUE ist. Die Serveranwendung sollte auch CoReleaseServerProcess im Destruktor jedes ihrer instance-Objekte und in jeder ihrer IClassFactory::LockServer-Methoden aufrufen, wenn der fLock-ParameterFALSE ist.

Schließlich sollte die Serveranwendung auf den Rückgabecode von CoReleaseServerProcess achten, und wenn sie 0 zurückgibt, sollte die Serveranwendung ihre Bereinigung initiieren, was für einen Server mit mehreren Threads in der Regel bedeutet, dass sie die verschiedenen Threads signalisieren sollte, ihre Nachrichtenschleifen zu beenden und CoAddRefServerProcess und CoReleaseServerProcess aufzurufen. Wenn die Verwaltungsfunktionen für die Serverprozesslebensdauer verwendet werden, müssen sie sowohl in den Objektinstanzen als auch in der LockServer-Methode verwendet werden. Andernfalls wird die Serveranwendung möglicherweise vorzeitig heruntergefahren.

Wenn eine CoGetClassObject-Anforderung gestellt wird, kontaktiert COM den Server, marshallt die IClassFactory-Schnittstelle des Klassenobjekts, kehrt an den Clientprozess zurück, hebt die IClassFactory-Schnittstelle auf und gibt dies an den Client zurück. An diesem Punkt rufen Clients in der Regel LockServer mit TRUE auf, um zu verhindern, dass der Serverprozess heruntergefahren wird. Es gibt jedoch ein Zeitfenster zwischen dem Marshallen des Klassenobjekts und dem Aufruf von LockServer, in dem ein anderer Client eine Verbindung mit demselben Server herstellen, eine instance abrufen und diesen instance freigeben kann, sodass der Server heruntergefahren wird und der erste Client mit einem getrennten IClassFactory-Zeiger hoch und trocken bleibt. Um diese Racebedingung zu verhindern, fügt COM dem Klassenobjekt einen impliziten Aufruf von LockServer mit TRUE hinzu, wenn es die IClassFactory-Schnittstelle marshallt, und einen impliziten Aufruf von LockServer mit FALSE , wenn der Client die IClassFactory-Schnittstelle freigibt. Daher ist es nicht notwendig, lockServer remote an den Server zurückzurufen, und der Proxy für LockServer gibt einfach S_OK zurück, ohne den Aufruf tatsächlich zu remotieren.

Es gibt eine weitere aktivierungsbezogene Racebedingung während der Initialisierung eines Out-of-Process-Serverprozesses. Ein COM-Server, der mehrere Klassen registriert, ruft in der Regel CoRegisterClassObject mit REGCLS_LOCAL_SERVER für jede unterstützte CLSID auf. Nachdem dies für alle Klassen geschehen ist, tritt der Server in seine Nachrichtenschleife ein. Bei einem Singlethread-COM-Server werden alle Aktivierungsanforderungen blockiert, bis der Server in die Nachrichtenschleife wechselt. Für einen Apartmentmodellserver, der verschiedene Klassenobjekte in verschiedenen Wohnungen registriert, und für alle Freethreadserver können Aktivierungsanforderungen jedoch früher eintreffen. Bei Apartmentmodellservern können Aktivierungsanforderungen eintreffen, sobald ein Thread in die Nachrichtenschleife eingedrungen ist. Bei Freethreadservern kann eine Aktivierungsanforderung eintreffen, sobald das objekt der ersten Klasse registriert ist. Da eine Aktivierung so früh erfolgen kann, ist es auch möglich, dass das endgültige Release auftritt (und daher das Herunterfahren des Servers beginnt), bevor der Rest des Servers die Möglichkeit hatte, die Initialisierung abzuschließen.

Um diese Racebedingungen zu beseitigen und die Aufgabe des Serverschreibers zu vereinfachen, sollte jeder Server, der mehrere Klassenobjekte bei COM registrieren möchte , CoRegisterClassObject mit REGCLS_LOCAL_SERVER aufrufen | REGCLS_SUSPENDED für jede unterschiedliche CLSID, die der Server unterstützt. Nachdem alle Klassen registriert wurden und der Serverprozess bereit ist, eingehende Aktivierungsanforderungen zu akzeptieren, sollte der Server einen Aufruf von CoResumeClassObjects tätigen. Diese Funktion weist COM an, den SCM über alle registrierten Klassen zu informieren, und beginnt, Aktivierungsanforderungen an den Serverprozess zuzulassen. Die Verwendung dieser Funktionen bietet die folgenden Vorteile:

  • Unabhängig davon, wie viele CLSIDs registriert sind, wird nur ein Aufruf des SCM ausgeführt, wodurch die gesamte Registrierungszeit (und damit die Startzeit der Serveranwendung) verringert wird.
  • Wenn der Server über mehrere Apartments verfügt und unterschiedliche CLSIDs in verschiedenen Apartments registriert sind oder wenn der Server ein Freethreadserver ist, werden keine Aktivierungsanforderungen gesendet, bis der Server CoResumeClassObjects aufruft, sodass der Server alle CLSIDs registrieren und ordnungsgemäß eingerichtet werden kann, bevor er sich mit Aktivierungsanforderungen und möglichen Herunterfahren von Anforderungen befassen muss.

COM-Serververantwortlichkeiten