2009-10-29 55 views
3

我使用ATL創建了一個進程內COM對象(DLL)。請注意,這是一個對象,而不是控件(所以沒有窗口或用戶界面。)我的問題是,我試圖從第二個線程觸發一個事件,並且出現'災難性故障'(0x8000FFFF)。如果我從主線程中觸發事件,那麼我不會收到錯誤。第二個線程調用CoInitializeEx,但這沒有什麼區別。我正在使用Apartment線程模型,但切換到Free Threaded並沒有幫助。從另一個線程觸發COM事件

事實上,我試圖從第二個線程做到這一點顯然至關重要。有沒有簡單的方法來做到這一點,或者我將不得不實施一些隱藏窗口的消息傳遞?

例如,在我的主要對象的源文件:

STDMETHODIMP MyObject::SomeMethod(...) 
{ 
    CreateThread(NULL, 0, ThreadProc, this, 0, NULL); 
    // Succeeds with S_OK 
    FireEvent(L"Hello, world!"); 
    return S_OK; 
} 

DWORD WINAPI ThreadProc(LPVOID param) 
{ 
    CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); 
    MyObject* comObject = reinterpret_cast<MyObject*>(param); 
    // Fails with 0x8000FFFF 
    comObject->FireEvent(L"Hello, world!"); 
} 

void MyObject::FireEvent(BSTR str) 
{ 
    ... 
    // Returns 0x8000FFFF if called from ThreadProc 
    // Returns S_OK if called from SomeMethod 
    pConnection->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &params, NULL, NULL, NULL); 
} 

回答

4

COM基礎

在STA你的對象住在一個單獨的線程(線程)。這個線程是它創建的線程,它的方法被執行,並且事件被觸發。 STA確保你的對象沒有兩個方法被同時執行(因爲它們必須在The Thread上執行,所以這是一個很好的結果)。

這並不意味着您的對象無法從其他線程訪問。這是通過爲線程以外的每個線程創建對象的代理來完成的。在線程中,您打包了一個帶有CoMarshalInterThreadInterfaceInStream的IUnknown,並在另一個線程上使用CoGetInterfaceAndReleaseStream解包,這實際上在另一個線程上創建了代理。此代理使用消息泵將呼叫同步到您的對象,即仍在線程上執行的調用,所以線程必須是空閒的(不忙)才能從另一個線程執行調用。

在你的情況下,你希望你的對象能夠在一個線程上執行方法並且在另一個線程上產生事件。所以這發生在MTA中,所以你的對象必須住在MTA中,所以你的類必須是自由線程的。線程只屬於一個單元,所以一個線程不能同時在MTA和STA中。如果你的對象住在MTA中,只要STA對象試圖使用它,它就必須創建一個代理。所以你會有輕微的開銷。

我猜想的是你正在考慮一些非常聰明的「技術」來卸載你的主線程,並做一些「異步」事件,這將不會在最後飛翔:-))如果你想到它有對這個第二個[工人]線程監聽器...

順便說一句,這條線

MyObject* comObject = reinterpret_cast<MyObject*>(param); 

可以在MTA只能做。

+0

非常好的解釋。非常感謝你。 – Rob 2009-10-30 07:43:51

0

我認爲,真正的問題不是你的組件配置,但主機的過程。許多主機,如Office對象模型的主機,都住在一個單線程的公寓中,在這種情況下,除了主線程之外,不允許從任何東西中調用它們。
如果是這種情況,您可以讓COM通過使用單線程單元模型並將實際調用移動到CoClass中的函數並從您的線程中調用該函數來完成工作。
這隻會在幕後傳遞窗口消息,但是不能讓您自己實現這一點。

Threading in COM(維基百科):
單線程公寓(STA)模型是一個很常用的模型。在這裏,COM對象的位置與桌面應用程序的用戶界面相似。在STA模型中,單個線程專用於驅動對象的方法,即總是使用單個線程來執行對象的方法。在這樣的安排中,來自公寓外的線程的方法調用被編組並且被系統自動排隊(通過標準的Windows消息隊列)。因此,不用擔心競爭條件或缺乏同步性,因爲對象的每個方法調用總是在另一個被調用之前執行完成。

另請參閱Message Pumping在同一篇文章。

+0

你有更多關於這裏涉及什麼的細節? – Rob 2009-10-29 19:49:34

+0

用更多的信息更新了答案 - 現在沒有時間通過​​msdn抓取簡要的細節:) – 2009-10-29 20:38:56

+0

但要縮短它:如果你的主機應用程序是一個STA,你需要從擁有的線程調用或調用呼叫在通過COM(編組)的右側線程上。否則,你必須自己照顧正確的線程。 – 2009-10-29 20:50:36