2014-12-21 129 views
1

考虑以下情形:COM对象生存

由线程A执行该代码:

CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); 
globalSomeSTAComObject.CreateInstance((__uuidof(CLSID_SomeSTAComObject)); 
return 0; 

现在线程A完成它的执行不会对default STA“继承” globalSomeSTAComObject,它可以通过使用其他线程?
或者这个物体变得不可用?

与线程A相同的问题,但现在认为该对象是在MTA线程上创建的MTA Com对象。
当线程A完成它的执行时,MTA Com对象是否在Multithreaded Apartment内仍然有效并且可以使用?

MSXML2::IXMLDOMDocumentPtr xml; 

unsigned __stdcall CreateXml(void*) 
{ 
    CoInitializeEx(nullptr, COINIT_MULTITHREADED); 
    xml.CreateInstance(__uuidof(MSXML2::FreeThreadedDOMDocument60)); 
    xml->load("c:\\test.xml"); 
    return 0; 
} 


int _tmain(int argc, _TCHAR* argv[]) 
{ 
    CoInitializeEx(nullptr, COINIT_MULTITHREADED); 
    HANDLE handle = reinterpret_cast<HANDLE>(_beginthreadex(nullptr, 0, CreateXml, nullptr, 0, nullptr)); 
    WaitForSingleObject(handle, INFINITE); 
    long numOfSections = xml->documentElement->childNodes->length; //This works why ? 
    xml.Release(); 
    CoUninitialize(); 
    return 0; 
} 

请不是在CreateXml我故意不叫CoUninitialize,因为我想知道什么是XML对象在这种情况下的状态。

+1

显示的代码段很危险。不仅你不显示你将如何去做'CoUnitinialize',而且你还要在主管STA /线程完成之后在其他线程和AND上使用STA特定的COM指针做一些假设。 'globalSomeSTAComObject'只能在这个线程上使用,并且可以在'CoUnitinialize'之前使用。在其他线程上使用可能会奏效,但通常不正确,行为未定义。 –

+0

如果MTA线程创建MTA对象(全局对象)并终止。 MTA公寓里的物体还活着吗?或者在这种情况下,行为是不确定的? – JobNick

+1

COM要求您在调用'CoUninitialize'之前终止您的COM活动。因此,在此初始化之外留下COM指针是不正确的。然而,在MTA情况下,由于指针在任何其他MTA线程的存在下仍然有效,所以它是一个不太常见的问题。总而言之,您有时可能会将COM对象从帮助程序MTA线程“泄漏”,但总结一切,我会说,您仍然在以某种方式错误地提出您的问题。尝试在COM初始化之外尝试使用COM单例对象时,首先会出现问题。 –

回答

0

它取决于COM对象本身和创建它的线程的线程模型。

如果对象是单元线程的,它将存在于它自己的STA中。如果一个STA线程创建该对象,则该线程将在与该对象相同的STA中运行,并接收到该对象的直接指针。如果MTA线程创建该对象,则创建一个新的STA,并且线程接收STA中的对象的代理。在任何一种情况下,如果其他线程想要访问该对象,则必须对该对象进行编组。

如果对象是多线程的,它将存在于MTA中。如果一个STA线程创建该对象,该线程接收MTA中的对象的代理。如果MTA线程创建该对象,它会收到一个指向该对象的直接指针。该对象可以由其他MTA线程直接访问,但是如果由STA线程访问,则必须编组。

如果该对象是自由线程的,它将存在于由STA线程创建的STA中,并且如果由MTA线程创建,则将存在于MTA中。上述规则适用。

本主题进行了详细MSDN上的讨论:

Processes, Threads, and Apartments

Understanding and Using COM Threading Models

COM+ Threading Models

+0

感谢您的回复,但它不回答我关于对象一生的问题。如果STA线程创建STA对象然后终止,该对象是否可以被其他线程访问?同样的问题,但现在MTA线程创建MTA对象,然后终止该对象是否在MTA公寓中保持'活跃'并且可以被访问? – JobNick

+0

对象的生命周期由其引用计数控制,而不是创建它的线程的生命周期。如果你阅读我链接到的文档,线程和公寓是分开的东西。线程运行在公寓中,但它们不创建或拥有它们,COM就是这样。因此,当线程创建对象时,其引用计数为1.在线程终止之前,它必须递减对象的引用计数。当其他线程访问对象时,它们必须根据需要递增和递减引用计数。当引用计数降到0时,对象会自行释放。 –

+0

嗯,我已经测试过我讨论过的场景,并且在创建线程终止仍处于“活动”状态的对象并且可以由其他初始化COM的线程访问后,它们看起来就像在两者中一样。我想知道这只是一种侥幸,或者它是设计行为? – JobNick

0

一个COM对象的生活,只要它认为有对它的引用,即引用计数大于零。当调用CoUninitialize时,公寓所有物件的所有跨公寓链接都会被释放。存根被释放,递减实际对象的重新计数。通过返回RPC错误HRESULT,可以通知旧的代理服务器并在除了refcounting以外的任何其他服务器上失败。

但是,我相信退出线程而没有必要的调用CoUninitialize没有指定的行为。我可以看到没有任何事情发生的场景,所以你最终可能会遇到因为你拉下了它下面的地毯(线程),或者是死的MTA对象,如果没有至少一个MTA线程在运行,这将会报错。另一种情况是,如果OLE32.dll在DLL_THREAD_DETACHDllMain中检测到这一点,并且进行必要的清理,从而在后续的代理方法调用中稍早出错。

您不得存储未编组的接口指针,因为如果这样做,从其他公寓调用其方法将极有可能导致麻烦, STA对象可能未准备好进行多线程,或者可能无法访问线程本地存储,并且MTA对象可能无法准备好用于Windows消息循环或重入。

请注意,可能存在多个STA,并且只有通过返回直接的非编组接口指针,由于COM本身,没有STA对象才会切换其公寓。所以,当一个STA死亡时,它的对象不会被现有的或未来的STA“采用”,甚至不是主要的(第一个STA)或默认的(由COM本身必然创建的),这可能是相同。

此外,创建MTA后,每个新线程隐式属于MTA,如果未在第一次使用COM时明确初始化。但是,这样的线程不会使MTA保持活动状态。


P.S:我读过你的意见,你的MTA对象实际上是一个自由线程对象。有很大的不同,因为自由线程对象绕过进程内编组,因此它们将属于实际创建它们的任何公寓,它的方法在任何当前公寓中运行,并且它们必须为模态内的多线程和重入而做好准备由于来自STA的跨公寓,非阻塞呼叫,Windows消息循环。