根据我的理解,当从STA线程使用标记为使用MTA的COM组件时,调用应该被编组到STA线程并从该专用线程执行。对于Windows客户端应用程序,这意味着它将在UI线程上执行(如果标记为STA),并且从COM组件到我的回调将由发送到隐藏窗口并在其上处理的Windows消息处理。 Windows消息循环。
如果我在IIS中托管的WCF服务中使用STA COM组件会发生什么?工作进程是否会在STA线程上有Windows消息循环?我可以使用自己的消息循环启动自己的STA线程吗?
答案 0 :(得分:5)
COM运行时负责调度对STA内部COM对象的方法的调用:您是正确的,这是基于用于调度Windows消息的相同OS机制,但您不必担心制作发生这种情况 - COM会在幕后为您做这件事。
你做什么需要担心的是你的COM对象将驻留在哪个STA。如果你使用来自WCF服务的COM Interop实例化单元线程COM对象,你需要小心。
如果您执行此操作的线程不是STA线程,则所有进程内COM对象将存在于IIS工作进程的默认Host STA中。您不希望发生这种情况:所有服务操作的所有COM对象都将在同一个STA中结束。线索在名称中 - 所有对象只有一个线程 - 所有对其方法的调用都将被序列化,等待公寓中唯一的线程执行它们。您的服务无法扩展以处理多个并发客户端。
您需要确保实例化为特定WCF请求提供服务的COM对象在其自己的STA中,与为其他请求创建的对象分开。大致有两种方法可以做到这一点:
ApartmentState.STA
中指定SetApartmentState()
,在其上为特定请求实例化COM对象。这是Scott Seely在the link in Kev's answer中详述的方法:他确保在新的STA初始化线程上调用每个服务操作调用。沿着这些方向的更难但更具可扩展性的解决方案是实现可重用的STA初始化线程池。 Activity
)可以处理将不同请求的对象放入不同的STA。 / LI>
当你提到回调时,我不确定你的意思。也许你的意思是COM方法调用托管代码中实现的某个COM接口,通过传入COM对象的引用作为COM对象的一个方法的参数:如果是这样,这应该可行。但也许你的意思是别的,在这种情况下,也许你可以修改问题来澄清。
答案 1 :(得分:3)
我发现您需要在WCF服务中的STA线程上提取消息,或者您错过了COM对象的回调。
以下代码有效,但它要求您通过Dispatcher调用COM对象。
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();
}
然后按以下方式拨打电话。
public void UsefulComOperation()
{
localThreadDispatcher.Invoke(new Action( () => comWrapper.UsefulOperation);
}