How are STA COM components handled when used in a WCF service hosted in IIS (7+)?
From what I understand, when a COM component marked as using STA is used from an MTA thread, the calls are supposed to be marshalled to an STA thread and executed from that dedicated thread. In the case of a Windows client application, this would mean that it would execute on the UI thread (if marked as STA), and that callbacks from the COM component to me would be handled by Windows messages sent to a hidden window and processed on the Windows message loop.
What happens though if I use a STA COM componen开发者_如何学JAVAt in a WCF service hosted in IIS? Will the worker process have a Windows message loop on a STA thread? Can I fire up my own STA thread with its own message loop?
The COM runtime looks after the dispatching of calls to methods on a COM object inside an STA: you are right that this is based on the same OS mechanism used for dispatching Windows messages, but you don't need to worry about making this happen - COM does this for you under the hood.
What you do need to worry about is which STA your COM objects are going to live in. If you instantiate apartment-threaded COM objects using COM Interop from a WCF service, you need to be careful.
If the thread on which you do this is not an STA thread, then all in-process COM objects will live in the default Host STA for the IIS worker process. You do not want this to happen: all your COM objects for all service operations will end up in this same STA. The clue is in the name - there is just one thread for all the objects - and all calls to their methods will be serialized waiting for the one and only thread in the apartment to execute them. Your service will not scale to handle multiple concurrent clients.
You need to make sure that COM objects you instantiate to service a particular WCF request are in their own STA separate from objects created for other requests. There are broadly two ways to do this:
- Spin up your own Thread, specifying
ApartmentState.STA
inSetApartmentState()
before you start it, on which to instantiate the COM objects for a particular request. This is the approach detailed by Scott Seely in the link in Kev's answer: he ensures that each service operation call is invoked on a new STA-initialised Thread. A harder but more scalable solution along these lines would be to implement a pool of reusable STA-initialised Threads. - Host your COM objects in a COM+ Application, so that they live in a separate DllHost process, where COM+ (through its abstraction called the
Activity
) can take care of putting the objects for different requests into different STAs.
I'm not sure exactly what you mean when you refer to callbacks. Perhaps you mean COM method calls on some COM interface implemented in your managed code, via a reference passed in to the COM objects as an argument to one of the COM objects' methods: if so, this should just work. But perhaps you mean something else, in which case perhaps you could amend the question to clarify.
I've found that you need to pump messages on your STA thread in a WCF service or you miss callbacks from the COM object.
The following code works, but it requires you call the COM object via a Dispatcher.
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();
}
And then make calls as follows.
public void UsefulComOperation()
{
localThreadDispatcher.Invoke(new Action( () => comWrapper.UsefulOperation);
}
精彩评论