2014-12-21 69 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消息循環。