2010-02-18 85 views
3

我在使用.NET編寫投擲警告,看起來像我的COM組件的麻煩:從WinForms GUI線程調用COM線程方法的問題?

上下文0x15eec0斷開。沒有 代理將用於服務COM組件上的 請求。這可能會導致數據損壞或數據丟失 。爲了 避免此問題,請確保 所有上下文/公寓活路 直到應用程序完全 與代表 住在他們裏面COM組件RuntimeCallableWrappers 完成。

看起來這是由我的GUI線程調用COM線程中的函數而沒有必要的同步引起的。作爲參考,我使用在http://msdn.microsoft.com/en-us/library/ms229609%28VS.80%29.aspx中設置的準則來在COM組件中創建我的GUI線程。

我的代碼看起來是這樣的:

class COMClass { 
    // this is called before SomeMethod 
    public void Init() { 
    ComObject comObject = new ComObject(); // this is imported from a TLB 

    // I create my GUI thread and start it as in the MSDN sample 
    Thread newThread = new Thread(new ThreadStart(delegate() { 
     Application.Run(new GUIForm(comObject)); 
    })); 
    } 

    public void SomeMethod(){ 
    comObject.DoSomething();    // this is where the error occurs 
    } 
} 

class GUIForm : Form { 
    ComObject com; 
    public GUIForm(ComObject com) {comObject = com;} 

    public void SomeButtonHandler(object sender, EventArgs e) { 
    comObject.SomeMethod(); // call on the GUI thread but the com object is bound to the COM thread... 
    } 
} 

是否有處理這個既定的方法?調用GUI是沒有問題的(Invoke/BeginInvoke),但調用其他方式似乎更困難...

編輯:它也不是一個選項修改COM對象的任何方式。

回答

1

我發現這個問題,它不是跨線程操作persay。在我的GUIForm中,我創建了一個子窗口,並使用SetParent()將它父代到COM服務器的應用程序窗口中。這似乎已導致COM代理斷開連接的問題(儘管COM專家可能需要更多經驗才能啓發我爲什麼表現如此)。

而不是父母我的控制窗口我要完全斷開它,只是掛鉤WM_WINDOWPOSCHANGING移動我的控制主應用程序窗口。

0

由於COM對象是在另一個線程上創建的,所以所有對COM對象的調用都應該由該線程創建。啓動GUI線程後,您需要設置某種排隊機制來等待調用來執行方法(可能是一個委託隊列)。您的GUI代碼可以將委託推入隊列,並在原始線程處理隊列時執行(在原始線程上)。請參閱:http://www.yoda.arachsys.com/csharp/threads/deadlocks.shtml(生產者/消費者關於頁面中途的示例)。

+0

這是不適用於此應用程序的COM系統的設計。沒有暴露的循環,我可以插入代碼來使用隊列中的事件;如果我將一個循環添加到在我的COM對象上調用的任何函數,它將永遠不會返回到調用應用程序。 – 2010-02-18 23:18:34

+0

坦率地說,我希望有某種ThreadBoundInvoker,我可以發佈事件來執行綁定的線程。 – 2010-02-18 23:20:03

+0

是否僅由GUIForm使用COM對象?如果是這樣,你可以在窗體中實例化它,並且所有的東西都在GUI線程中。 – PatrickSteele 2010-02-19 01:27:44

2

從你的代碼片段不太清楚如何調用所有重要的Init()方法以及線程是如何啓動的。顯然,創建COM對象的線程與創建SomeMethod()調用的線程不是同一個線程。進一步假定COM服務器是單元線程的,COM需要將SomeMethod()調用編組到創建該對象的線程。叫做Init()的那個。如果該線程不再運行,則會出現鬧劇。

有一個明顯的問題,你忘記調用Thread.SetApartmentState()。

鑑於COM已經封送了線程間調用,您可能沒有通過啓動自己的線程獲得任何東西。如果它拒絕支持它,你不可能神奇地使COM服務器成爲多線程的。

+0

Init()方法由在STA線程上創建我們的COMClass的COM服務器應用程序(我們沒有寫它)調用。在實際的代碼中,我確實有SetApartmentState(ApartmentState.STA)調用,所以GUI和COMClass都應該在單獨的STA中。我的理解是調用應該在它們之間編組,但MDA錯誤似乎表明代理對象由於某種原因被刪除。 – 2010-02-19 14:20:31

+0

我們也沒有多少選擇創建線程;爲了使用我們的COMClass運行WinForms GUI,我們需要初始化WinForms消息泵,這需要一個新的線程。 – 2010-02-19 14:22:50

+0

那麼,Init()是由你不控制的線程調用的。從消息來看,這個線程已經不在了。目前尚不清楚爲什麼*你必須在該線程上創建COM對象。 – 2010-02-19 15:31:26