2016-10-06 49 views
0

我有一個用C#實現的COM對象,並從StandardOleMarshalObject繼承來禁用NTA默認行爲。出於某種原因,當我打電話給一個可重入客戶端的服務器時,回調會在另一個線程上結束。爲什麼我的`StandardOleMarshalObject` COM對象是從多個線程調用的?

如何確保所有調用都在主線程上進行?

[ComVisible(true)] 
[InterfaceType(ComInterfaceType.InterfaceIsDual)] 
public interface IComChat 
{ 
    void WriteLine(string text); 
    void Subscribe(IComChat callback); 
} 

public class ComChatServer : StandardOleMarshalObject, IComChat 
{ 
    private List<IComChat> Clients = new List<IComChat>(); 

    public void WriteLine(string text) 
    { 
     foreach (var client in Clients) 
     { 
      // this makes a reentrant callback into the calling client 
      client.WriteLine(text); 
     } 
    } 

    public void Subscribe(IComChat client) => Clients.Add(client); 
} 

public class ComChatClient : StandardOleMarshalObject, IComChat 
{ 
    private IComChat Server; 
    private Thread MainThread; 

    public ComChatClient() 
    { 
     this.MainThread = Thread.CurrentThread; 
     this.Server = /* get server by some means */; 

     this.Server.Subscribe(this); 
    } 

    void IComChat.WriteLine(string text) 
    { 
     // this throws as the call ends up on a different thread 
     Contract.Assert(Thread.CurrentThread == MainThread); 

     Console.WriteLine(text); 
    } 

    void IComChat.Subscribe(IComChat callback) => throw new NotSupportedException(); 

    public void WriteLine(string text) => Server.WriteLine(text); 
} 

public static class Program 
{ 
    public static void Main(string[] args) 
    { 
     var client = new ComChatClient(); 
     Application.Run(new ChatWindow(client)); 
    } 
} 

回答

0

StandardOleMarshalObject如果您在STA線程上創建對象,則只保留主線程上的內容。用[STAThread]標記您的入口點,將您的主線程設置爲單線程:

public static class Program 
{ 
    [STAThread] 
    public static void Main(string[] args) 
    { 
     var client = new ComChatClient(); 
     Application.Run(new ChatWindow(client)); 
    } 
} 
+0

這正是您應該從未**做過的事情。 [STAThread]是一個* promise *,你發誓你的線程行爲良好並且支持非線程安全的代碼。像你的ComChatClient。但是你立即違反了這個承諾,你不會像STA要求的那樣抽出消息循環。所以最初COM創建了一個線程來給你的對象一個安全的家,一個遵循規則。謊言的後果是僵局。可能是非常醜陋的類型,死鎖的終結器線程很難診斷。 –

+0

@HansPassant,應用程序本身在「主」線程上抽取消息。我在示例代碼中錯過了這一點。我自嘲的白癡是一個缺少'STAThreadAttribute'。 – Mitch

+0

Hmya,使用項目模板開始*確實*避免了許多事故。看起來像上世紀的用戶界面也不是那麼漂亮。 –

相關問題