2015-06-11 96 views
1

我知道,標題可能會讓人困惑,但我簡直無法真正表達它。很熱從另一個線程調用一個方法或從另一個線程中觸發一個事件在主線程上處理

基本上,我正在寫一個TCP服務器。我沒有要顯示的Windows窗體,用戶看到的唯一東西是TrayIcon。

我的TCP服務器類創建一個用於監聽客戶端的線程,然後爲每個處理通信的客戶端創建一個附加線程。當所有通信完成後,我想在主線程上調用一個方法。

我已經完成了從主線程處理的客戶端通信線程發起事件,並且所有工作都正常,直到我想將桌面通知添加到我的應用程序。我已經使用WPF(iControlNotification)構建了一個通知,並希望在我之前提到的事件處理程序中顯示它,但是我收到一條錯誤消息,提示「調用線程必須是STA線程」。

下面是一些代碼(我刪除了不必要的聚會):

static class Program { 

    [...] 

    [STAThread] 
    static void Main() { 
     Log("iControlServerApplication started."); 
     Application.EnableVisualStyles(); 
     Application.SetCompatibleTextRenderingDefault(false); 

     trayIcon = new TrayIcon(); 
     trayIcon.Display(); 

     NotificationManager = new iControlNotificationManager(); 

     server = new TCPServer(); 
     server.CommandReceived += new TCPServer.CommandReceivedEventHandler(tcpServer_CommandReceived); 
     if (server.Start()) { 
      NotificationManager.ShowNotfication("iControl Server Application", "Server started. " + plugins.Count + " plugins loaded."); 
      Application.Run(); 
     } else { 
      MessageBox.Show("Port " + server.Port + " is already in use. Server could not be started.", ApplicationName, MessageBoxButtons.OK, MessageBoxIcon.Error); 
     } 
    } 

    [...] 

    static void tcpServer_CommandReceived(object source, TCPServer.CommandReceivedEventArgs e) { 
     string toolTipText = "[" + e.Client.IPAddress + "] >> " + e.Command; 
     NotificationManager.ShowNotfication("iControl Server Application", toolTipText); 

     foreach (IiControlPlugin plugin in plugins) { 
      plugin.Handle(e.SplittedCommands, e.Client); 
     } 
    } 

    [...] 
} 

-

class TCPServer { 

    public delegate void CommandReceivedEventHandler(object source, CommandReceivedEventArgs e); 
    public event CommandReceivedEventHandler CommandReceived; 
    public class CommandReceivedEventArgs : EventArgs { 
     private string _command; 
     private string[] _splittedCommands; 
     private iControlClient _client; 
     public CommandReceivedEventArgs(string command, iControlClient client) { 
      _command = command; 
      _splittedCommands = command.Split(new Char[]{' '}); 
      _client = client; 
     } 
     public string Command { get { return _command; } } 
     public string[] SplittedCommands { get { return _splittedCommands; } } 
     public iControlClient Client { get { return _client; } } 
    } 

    public TCPServer() { 
     this.tcpListener = new TcpListener(IPAddress.Any, Port); 
     this.icClients = new Dictionary<String, iControlClient>(); 
    } 

    public Boolean Start() { 
     if (PortIsAvailable(Port)) { 
      this.listenThread = new Thread(new ThreadStart(ListenForClients)); 
      this.listenThread.Start(); 
      Program.Log("ListeningThread started."); 
      return true; 
     } else { 
      return false; 
     } 
    } 

    private void ListenForClients() { 
     this.tcpListener.Start(); 

     TcpClient client; 

     while (this.keepListening) { 
      try { 
       client = this.tcpListener.AcceptTcpClient(); 
      } catch { 
       break; 
      } 

      iControlClient icClient = new iControlClient(client); 
      icClient.Thread = new Thread(new ParameterizedThreadStart(HandleClientCommunication)); 
      icClient.Thread.Start(icClient); 
     } 
     Program.Log("Stop listening."); 
    } 

    private void HandleClientCommunication(object client) { 
     iControlClient icClient = (iControlClient)client; 
     NetworkStream clientStream = icClient.TCP.GetStream(); 

     clientStream.ReadTimeout = 10; 

     int bufflen = 4096; 
     byte[] message = new byte[bufflen]; 
     int bytesRead; 

     while (this.keepReceiving && icClient.keepConnected) { 
      bytesRead = 0; 

      try { 
       bytesRead = clientStream.Read(message, 0, bufflen); 
      } catch { 
       break; 
      } 

      if (bytesRead == 0) { 
       break; 
      } 
      ProcessReceivedData(icClient, ParseData(message, bytesRead)); 
     } 

     Program.Log("[" + icClient.IPAddress + "] Connection closed."); 

     icClient.TCP.Close(); 
     this.icClients.Remove(icClient.IPAddress); 
    } 

    private void ProcessReceivedData(iControlClient icClient, String[] commands) { 
     Program.Log("[" + icClient.IPAddress + "] >> " + String.Join(" ", commands)); 

     if (this.CommandReceived != null) { 
      CommandReceived(this, new CommandReceivedEventArgs(String.Join(" ", commands), icClient)); 
     } 

     NetworkStream clientStream = icClient.TCP.GetStream(); 
     ASCIIEncoding encoder = new ASCIIEncoding(); 
     byte[] buffer = encoder.GetBytes("::ok"); 
     clientStream.Write(buffer, 0, buffer.Length); 
     clientStream.Flush(); 

     icClient.keepConnected = false; 
    } 
} 

-

public class iControlNotificationManager { 
    private iControlNotifications _notifications; 

    public void ShowNotfication(string caption, string message) { 
     if ((Boolean)Program.GetSetting("notifications", true) == false) return; 
     Dispatcher.CurrentDispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, new Action(
     () => { 
      iControlNotification noti = new iControlNotification(caption, message); 
      noti.Show(); 
     })); 
    } 
} 

-

public class iControlNotification : Window { 

    private iControlNotificationModel _notification; 

    public iControlNotification(string caption, string message) { // Here's the error 
     InitializeComponent(); 

     _notification = new iControlNotificationModel() { 
      Caption = caption, 
      Message = message 
     }; 

     this.DataContext = _notification; 
    } 
} 

那麼我應該如何調用tcpServer_CommandReceived以便通知窗口能以正確的方式顯示?

我真的被困在這裏,我非常感謝這方面的幫助!

回答

1

//如何調用從另一個線程的方法:通過傳遞的SynchronizationContext對象將其

a)您可以在其他線程調用它:

void Method(object s) 
{ 
SynchronizationContext sync = s as SynchronizationContext; 
sync.Post(delegate { // what to do in other thread}, null); 
} 

然後在你的代碼運行在這種方法新的任務,通過您的同步上下文對象(例如):

Task t = Task.Factory.StartNew(Method, SynchronizationContext.Current); 

b)你可以創建擴展方法用於此目的(我這裏是我在贏形式申請使用用於更新UI):

public static class ControlExtensions 
    { 
     /// Executes the Action asynchronously on the UI thread, does not block execution on the calling thread. 

     public static void UIThread(this Control @this, Action code) 
     { 
      if (@this.InvokeRequired) 
      { 
       @this.BeginInvoke(code); 
      } 
      else 
      { 
       code.Invoke(); 
      } 
     } 
    } 
相關問題