2010-10-07 12 views
6

Von dem, was ich verstehe, wenn die COM-Komponente mit STA aus einem MTA-Thread verwendet wird, sollen die Aufrufe an einen STA-Thread gemarshallt und von diesem dedizierten Thread ausgeführt werden. Im Fall einer Windows-Client-Anwendung würde dies bedeuten, dass sie auf dem Benutzeroberflächen-Thread ausgeführt würde (wenn sie als STA markiert wäre) und dass Callbacks von der COM-Komponente zu mir von Windows-Nachrichten verarbeitet würden, die an ein verborgenes Fenster gesendet und verarbeitet würden die Windows-Nachrichtenschleife.Wie werden STA COM-Komponenten behandelt, wenn sie in einem WCF-Dienst verwendet werden, der in IIS (7+) gehostet wird?

Was passiert jedoch, wenn ich eine STA COM-Komponente in einem WCF-Dienst in IIS gehostet verwenden? Hat der Worker-Prozess eine Windows-Nachrichtenschleife in einem STA-Thread? Kann ich meinen eigenen STA-Thread mit einer eigenen Nachrichtenschleife starten?

Antwort

4

Die COM-Laufzeit kümmert sich um das Absetzen von Aufrufen von Methoden auf einem COM-Objekt innerhalb einer STA: Sie haben recht, dass dies auf dem Betriebssystemmechanismus für die Verteilung von Windows-Nachrichten basiert, aber Sie müssen sich keine Sorgen machen Damit das möglich ist - COM macht das für Sie unter der Haube.

Was Sie do tun müssen, ist besorgt, welche STA Ihre COM-Objekte leben werden. Wenn Sie Apartment-Threaded COM-Objekte mit COM-Interop von einem WCF-Dienst instanziieren, müssen Sie vorsichtig sein.

Wenn der Thread, für den Sie dies tun, kein STA-Thread ist, werden alle prozessinternen COM-Objekte im Standard-Host STA für den IIS-Arbeitsprozess verarbeitet. Sie möchten nicht, dass dies geschieht: Alle Ihre COM-Objekte für alle Servicevorgänge landen in derselben STA. Der Hinweis ist im Namen - es gibt nur einen Thread für alle Objekte - und alle Aufrufe ihrer Methoden werden serialisiert und warten darauf, dass der einzige Thread in der Wohnung sie ausführt. Ihr Dienst wird nicht für die Verarbeitung mehrerer gleichzeitiger Clients skaliert.

Sie müssen sicherstellen, dass COM-Objekte, die Sie instanziieren, um eine bestimmte WCF-Anforderung zu bedienen, in ihrer eigenen STA getrennt von Objekten sind, die für andere Anforderungen erstellt wurden. Es gibt im Großen und Ganzen zwei Möglichkeiten, dies zu tun:

  • Spin up Ihre eigenen Thread unter Angabe ApartmentState.STA in SetApartmentState() bevor Sie sie starten, auf dem die COM-Objekte für eine bestimmte Anforderung zu instanziiert. Dies ist der von Scott Seely in the link in Kev's answer beschriebene Ansatz: Er stellt sicher, dass jeder Dienstoperationsaufruf auf einem neuen STA-initialisierten Thread aufgerufen wird. Eine härtere, aber besser skalierbare Lösung in diesem Sinne wäre die Implementierung eines Pools wiederverwendbarer STA-initialisierter Threads.
  • Hosten Sie Ihre COM-Objekte in einer COM + -Anwendung, so dass sie in einem separaten DllHost-Prozess leben, in dem COM + (über seine Abstraktion the Activity) die Objekte für unterschiedliche Anforderungen in verschiedene STAs versetzen kann.

Ich bin mir nicht sicher, was genau Sie meinen, wenn Sie auf Rückrufe beziehen. Vielleicht meinen Sie COM-Methodenaufrufe auf einer COM-Schnittstelle, die in Ihrem verwalteten Code implementiert ist, über einen Verweis, der als Argument für eine der Methoden des COM-Objekts an die COM-Objekte übergeben wird: Wenn ja, sollte das einfach funktionieren. Aber vielleicht meinst du etwas anderes, in diesem Fall könntest du vielleicht die Frage ändern, um es zu klären.

3

Ich habe festgestellt, dass Sie Nachrichten in Ihrem STA-Thread in einem WCF-Dienst pumpen müssen oder Sie Callbacks von dem COM-Objekt vermissen.

Der folgende Code funktioniert, aber Sie müssen das COM-Objekt über einen Dispatcher aufrufen.

ComWrapper comWrapper; 
Thread localThread; 
Dispatcher localThreadDispatcher; 

public Constructor() 
{ 
    localThread = new Thread(ThreadProc) 
    { 
     Name = "test" 
    }; 
    localThread.SetApartmentState(ApartmentState.STA); 

    AutoResetEvent init = new AutoResetEvent(false); 

    localThread.Start(init); 

    init.WaitOne(); 
} 

private void ThreadProc(object o) 
{ 
    localThreadDispatcher = Dispatcher.CurrentDispatcher; 
    ((AutoResetEvent)o).Set(); 

    comWrapper = new ComWrapper() 

    Dispatcher.Run(); 

    localThreadFinished.Set(); 
} 

Und dann Anrufe wie folgt.

public void UsefulComOperation() 
{ 
    localThreadDispatcher.Invoke(new Action(() => comWrapper.UsefulOperation); 
} 
+1

'localThreadFinished' nicht überall definiert ist, soweit ich sagen kann ... war Ihre Absicht, es an der Spitze der' ThreadProc', als 'Autoreset localThreadFinished = (Autoreset) o' zu erklären? – transistor1