嗨,我是COM的初学者。我想在STA和MTA模式下测试COM dll。我的第一个问题是:COM对象是否支持STA和MTA?
现在我想象下面的STA代码片段:
// this is the main thread
m_IFoo;
CoInitializeEx(STA); // initialize COM in main thread
CreateInstance(m_IFoo);
m_IFoo->Bar();
CreateThread(ThreadA);
// start ThreadA
// this is secondary thread
ThreadA()
{
CoInitializeEx(STA);
m_IFoo->Buz(); // call m_IFoo's method directly
}
此代码有效吗?我错过了任何基本的东西吗?我知道主线程需要一个窗口消息循环来执行来自其他线程的调用。我必须做些什么吗?
现在我继续测试MTA。如果我只是在上面的代码中用“MTA”替换“STA”,它会起作用吗?
另一个问题是:由于GUI的线程必须是STA,我无法在GUI线程中初始化和测试MTA?
提前致谢,很抱歉我对COM和线程很天真。
答案 0 :(得分:3)
您的代码不是合法的COM,因为您将指针直接从一个STA传递到另一个STA,而COM不允许。
在COM中,界面指针具有“公寓亲和力”,它们只能在公寓内使用。要将指针从一个STA传递到另一个STA,或者在STA和MTA之间传递,您必须“指定”指向安全表示的指针,然后由接收线程解组。
最简单的方法是使用Global Interface Table;你在一个线程中注册接口并返回一个DWORD,然后在另一个线程中使用它来获取另一个线程可以使用的接口版本。
如果两个线程都是MTA,则可以避免这样做。虽然STA是每个线程一个 - 每个STA线程都有自己的aparment - 所有MTA线程共享MTA。这意味着MTA线程可以自由地在它们之间传递COM指针。 (但是如果向STA线程传递指针或者从STA线程传递指针,它们仍然需要编组。)
一般来说,您不会在STA或MTA之间更改代码,您通常会在一开始就决定一次。如果线程有UI,那么它需要一个消息循环,通常是STA。如果没有UI,您可以决定使用MTA。但是一旦你做出了这个决定并编写了你的代码,很少会在以后更改为另一个,因为选择一个或另一个有不同的要求和假设会影响代码;从STA更改为MTA,反之亦然,您必须仔细查看代码,看看是否需要更改指针分配等内容。
答案 1 :(得分:2)
能够从“MTA”切换到“STA”并且这种切换的后果将取决于对象在系统注册表中的注册方式。为了使对象能够“支持”两种情况而不进行编组,必须将ThreadingModel
设置为Both
。
请参阅this great answer - Both
表示“Free
或Apartment
,具体取决于调用者初始化COM的方式”。这正是你想要的。
关于使用“STA”模式 - 是的,胎面对象所属必须通过在循环中调用GetMessage()
,TranslateMesage()
和DispatchMessage()
来运行消息循环。无论如何,对象方法不会直接从第二个线程调用 - 它们将通过代理。有关详细说明,请参阅this very good article。