Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Warnung
Apartmentübergreifendes Marshalling aus .NET-Code: Wenn eine .NET-Anwendung ein In-Process-COM-Objekt erstellt, dessen ThreadingModel nicht mit dem aufrufenden Apartment übereinstimmt, erstellt COM das Objekt in einem anderen Apartment und gibt einen Proxy zurück. Das Aufrufen von Methoden über diesen Proxy verursacht Marshalling-Overhead und kann zu Deadlocks führen, wenn der Thread des Ziel-Apartments blockiert ist.
Häufiges Fehlermuster:
// A .NET console app defaults to MTA (no explicit CoInitializeEx).
// Creating an Apartment-threaded COM object forces COM to spin up
// a hidden STA thread. If that STA thread has no message pump,
// calls that require marshaling back to it will hang.
// Fix: If your COM object requires STA, mark Main with [STAThread]:
[STAThread]
static void Main(string[] args)
{
var comObj = new MyCOMObject(); // Created in the STA — no proxy needed
}
Bewährte Vorgehensweisen für .NET-Nutzer von In-Process-COM-Servern:
- Überprüfen des Registrierungswerts des
ThreadingModelObjekts unterHKCR\CLSID\{...}\InprocServer32 - Verwenden Sie
[STAThread]für Ihren Einstiegspunkt, wenn Sie Objekte mit Apartment-Threading verwenden (bei WinForms/WPF geschieht dies automatisch) - Rufen Sie
Thread.Join()oderTask.Wait()niemals auf einem STA-Thread auf — verwenden Sieawaitoder verarbeiten Sie Nachrichten mitDispatcher.PushFrame()
Ein In-Process-Server ruft "CoInitialize", "CoInitializeEx" oder "OleInitialize" nicht auf, um das Threadingmodell zu markieren. Für threadfähige DLL-basierte oder prozessinterne Objekte müssen Sie das Threadingmodell in der Registrierung festlegen. Das Standardmodell, wenn Sie kein Threadingmodell angeben, ist ein Einzelthread pro Prozess. Um ein Modell anzugeben, fügen Sie den ThreadingModel-Wert dem InprocServer32-Schlüssel in der Registrierung hinzu.
DLLs, die die Instanziierung eines Klassenobjekts unterstützen, müssen die Funktionen DllGetClassObject und DllCanUnloadNow implementieren und exportieren. Wenn ein Client eine Instanz der klasse möchte, die die DLL unterstützt, ruft ein Aufruf von CoGetClassObject (entweder direkt oder über einen Aufruf von CoCreateInstance) DllGetClassObject auf, um einen Zeiger auf das Klassenobjekt abzurufen, wenn das Objekt in einer DLL implementiert wird. DllGetClassObject sollte daher in der Lage sein, mehrere Klassenobjekte oder ein einzelnes threadsicheres Objekt zurückzugeben (im Wesentlichen, indem einfach InterlockedIncrement/InterlockedDecrement auf deren interne Referenzzähler angewendet wird).
Wie der Name schon sagt, wird DllCanUnloadNow aufgerufen, um zu bestimmen, ob die DLL, die sie implementiert, verwendet wird, wodurch der Aufrufer es sicher entladen kann, wenn dies nicht der Fall ist. Aufrufe von CoFreeUnusedLibraries von einem beliebigen Thread werden immer über den Thread des Hauptapartments geleitet, um DllCanUnloadNow aufzurufen.
Wie andere Server können In-Process-Server Single-Threaded, Apartment-Threaded oder Free-Threaded sein. Diese Server können von jedem OLE-Client verwendet werden, unabhängig vom Threadingmodell, das von diesem Client verwendet wird.
Alle Kombinationen der Threadingmodellinteroperabilität sind zwischen Clients und In-Process-Objekten zulässig. Die Interaktion zwischen einem Client und einem In-Process-Objekt, das verschiedene Threadingmodelle verwendet, entspricht genau der Interaktion zwischen Clients und Out-of-Process-Servern. Bei einem In-Process-Server muss COM, wenn sich das Threadingmodell des Clients von dem des In-Process-Servers unterscheidet, zwischen den Client und das Objekt zwischengeschaltet werden.
Wenn ein In-Process-Objekt, das das Einzelthreadmodell unterstützt, von mehreren Threads eines Clients gleichzeitig aufgerufen wird, kann COM nicht zulassen, dass die Clientthreads direkt auf die Schnittstelle des Objekts zugreifen können (das Objekt wurde nicht für diesen Zugriff entwickelt). Stattdessen muss COM sicherstellen, dass Aufrufe synchronisiert werden und nur vom Clientthread ausgeführt werden, der das Objekt erstellt hat. Daher erstellt COM das Objekt in der Hauptwohnung des Clients und benötigt alle anderen Clientwohnungen, um mithilfe von Proxys auf das Objekt zuzugreifen.
Wenn ein Apartment mit freier Threadzuordnung (Multithread-Apartmentmodell) in einem Client einen In-Process-Server im Apartment-Threading-Modell erstellt, startet COM im Client einen „Host“-Thread im Singlethread-Apartmentmodell. Dieser Hostthread erstellt das Objekt, und der Schnittstellenzeiger wird zurück in das Free-Threaded-Apartment des Clients gemarshallt. Ebenso startet COM, wenn ein Singlethread-Apartment in einem Client im Apartment-Modell einen freien In-Process-Server erstellt, einen freien Hostthread (ein Multithread-Apartment, auf dem das Objekt erstellt und dann zurück in das Singlethread-Apartment des Clients gemarshallt wird).
Note
Im Allgemeinen sollten Sie, wenn Sie eine benutzerdefinierte Schnittstelle für einen In-Process-Server entwerfen, auch den Marshallingcode dafür bereitstellen, damit COM die Schnittstelle zwischen Client-Apartments marshallen kann.
COM trägt zum Schutz des Zugriffs auf Objekte bei, die von einer Single-Thread-DLL bereitgestellt werden, indem es verlangt, dass der Zugriff aus demselben Client-Apartment erfolgt, in dem sie erstellt wurden. Darüber hinaus sollten alle DLL-Einstiegspunkte (wie DllGetClassObject und DllCanUnloadNow) und globale Daten immer über dasselbe Apartment zugegriffen werden. COM erstellt solche Objekte in der Hauptwohnung des Clients, sodass die Hauptwohnung direkten Zugriff auf die Zeiger des Objekts erhält. Aufrufe aus den anderen Apartments verwenden threadübergreifendes Marshaling, um vom Proxy zum Stub im Hauptapartment und dann zum Objekt zu gelangen. Dadurch kann COM Aufrufe mit dem Objekt synchronisieren. Interthread-Anrufe sind langsam, daher wird empfohlen, dass diese Server umgeschrieben werden, um mehrere Wohnungen zu unterstützen.
Wie bei einem Single-Thread-In-Process-Server muss auf ein Objekt, das von einer DLL mit Apartmentmodell bereitgestellt wird, von demselben Client-Apartment aus zugegriffen werden, in dem es erstellt wurde. Objekte, die von diesem Server bereitgestellt werden, können jedoch in mehreren Wohnungen des Clients erstellt werden, sodass der Server seine Einstiegspunkte (wie DllGetClassObject und DllCanUnloadNow) für die Verwendung mit Multithreads implementieren muss. Wenn beispielsweise zwei Wohnungen eines Clients versuchen, zwei Instanzen des In-Process-Objekts gleichzeitig zu erstellen, kann DllGetClassObject von beiden Wohnungen gleichzeitig aufgerufen werden. DllCanUnloadNow muss geschrieben werden, damit die DLL nicht entladen wird, während Code weiterhin in der DLL ausgeführt wird.
Wenn die DLL nur eine Instanz des Klassenobjekts bereitstellt, um alle Objekte zu erzeugen, muss auch die Implementierung des Klassenobjekts für den Multithreadbetrieb ausgelegt sein, da von mehreren Client-Apartments darauf zugegriffen wird. Wenn die DLL bei jedem Aufruf von DllGetClassObject eine neue Instanz der Klassenfactory erstellt, muss die Klassenfactory nicht threadsicher sein.
Von der Klassenfabrik erstellte Objekte müssen nicht threadsicher sein. Nachdem ein Thread erstellt wurde, wird auf das Objekt immer über diesen Thread zugegriffen, und alle Aufrufe des Objekts werden von COM synchronisiert. Das Apartment eines Clients, das das Apartmentmodell verwendet und dieses Objekt erstellt, erhält einen direkten Zeiger auf das Objekt. Client-Apartments, die sich von dem Apartment unterscheiden, in dem das Objekt erstellt wurde, müssen über Proxys auf das Objekt zugreifen. Diese Proxys werden erstellt, wenn der Kunde die Schnittstelle zwischen seinen Wohnungen marshallt.
Wenn der ThreadingModel-Wert einer In-Process-DLL auf "Both" festgelegt ist, kann ein von dieser DLL bereitgestelltes Objekt in Singlethread- oder Multithread-Clientapartments direkt (ohne Proxy) erstellt und verwendet werden. Es kann jedoch nur innerhalb der Wohnung verwendet werden, in der sie erstellt wurde. Um das Objekt an ein anderes Apartment zu übergeben, muss das Objekt gemarshallt werden. Das DLL-Objekt muss eine eigene Synchronisierung implementieren und kann gleichzeitig von mehreren Clientwohnungen aufgerufen werden.
Um die Leistung für den freithreadigen Zugriff auf In-Process-DLL-Objekte zu beschleunigen, stellt COM die CoCreateFreeThreadedMarshaler-Funktion bereit. Diese Funktion erstellt ein Marshallingobjekt für freie Threads, das mit einem In-Process-Serverobjekt aggregiert werden kann. Wenn ein Client-Apartment im selben Prozess Zugriff auf ein Objekt in einer anderen Wohnung benötigt, stellt das Aggregieren des Freethread-Marshalers dem Client einen direkten Zeiger auf das Serverobjekt statt auf einen Proxy bereit, wenn der Client die Schnittstelle des Objekts zu einem anderen Apartment marshallt. Der Client muss keine Synchronisierung durchführen. Dies funktioniert nur innerhalb desselben Prozesses; Standard-Marshaling wird für einen Verweis auf das Objekt verwendet, der an einen anderen Prozess gesendet wird.
Important
Moderner Ersatz für CoCreateFreeThreadedMarshaler: Für neuen Code für Windows 8.1+ verwenden Sie vorzugsweise RoGetAgileReference und die Schnittstelle IAgileReference. Diese bieten eine sicherere Möglichkeit, Objektreferenzen über Apartments hinweg zu übergeben, ohne die Fallstricke des Free-Threaded-Marshalers in Kauf nehmen zu müssen (der den Apartmentschutz vollständig umgeht und Threading-Fehler verschleiern kann). Für WinRT-Objekte ist Agilität die Standardeinstellung — WinRT-Objekte unterstützen IAgileObject automatisch, sofern sie sich nicht ausdrücklich dagegen entscheiden.
Ein Objekt, das von einer In-Process-DLL bereitgestellt wird, die ausschließlich Free-Threading unterstützt, ist ein Free-Threaded-Objekt. Sie implementiert eine eigene Synchronisierung und kann gleichzeitig von mehreren Clientthreads aufgerufen werden. Dieser Server führt keine Marshallschnittstellen zwischen Threads durch, sodass dieser Server direkt (ohne Proxy) nur durch Multithread-Apartments in einem Client erstellt und verwendet werden kann. Single-Threaded-Apartments, die es erstellen, greifen über einen Proxy darauf zu.
Zugehörige Themen