2012-05-03 104 views
0

我正在使用命名管道與進程進行通信。我已經能夠使它與以下代碼一起工作。 (原始代碼在這裏找到:via archive.orgC#命名管道而不從控制檯發出命令?

class ProgramPipeTest 
    { 

     public void ThreadSenderStartClient(object obj) 
     { 
      // Ensure that we only start the client after the server has created the pipe 
      ManualResetEvent SyncClientServer = (ManualResetEvent)obj; 

      using (NamedPipeClientStream pipeStream = new NamedPipeClientStream(".","ToSrvPipe",PipeDirection.Out,PipeOptions.None)) 
      { 
       // The connect function will indefinately wait for the pipe to become available 
       // If that is not acceptable specify a maximum waiting time (in ms) 
       pipeStream.Connect(); 

       Console.WriteLine("[Client] Pipe connection established"); 
       using (StreamWriter sw = new StreamWriter(pipeStream)) 
       { 
        sw.AutoFlush = true; 
        string temp; 
        Console.WriteLine("Please type a message and press [Enter], or type 'quit' to exit the program"); 
        while ((temp = Console.ReadLine()) != null) 
        { 
         if (temp == "quit") break; 
         sw.WriteLine(temp); 
        } 
       } 
      } 
     } 

     public void ThreadStartReceiverClient(object obj) 
     { 
      // Ensure that we only start the client after the server has created the pipe 
      ManualResetEvent SyncClientServer = (ManualResetEvent)obj; 

      using (NamedPipeClientStream pipeStream = new NamedPipeClientStream(".", "FromSrvPipe", PipeDirection.In, PipeOptions.None)) 
      { 
       // The connect function will indefinately wait for the pipe to become available 
       // If that is not acceptable specify a maximum waiting time (in ms) 
       pipeStream.Connect(); 

       Console.WriteLine("[ClientReceiver] Pipe connection established"); 

       using (StreamReader sr = new StreamReader(pipeStream)) 
       { 
        // Display the read text to the console 
        string temp; 
        while ((temp = sr.ReadLine()) != null) 
        { 
         Console.WriteLine("Received from server: {0}", temp); 
        } 
       } 
      } 
     } 

     static void Main(string[] args) 
     { 

      // To simplify debugging we are going to create just one process, and have two tasks 
      // talk to each other. (Which is a bit like me sending an e-mail to my co-workers) 

      ProgramPipeTest Client = new ProgramPipeTest(); 

      Thread ClientThread = new Thread(Client.ThreadSenderStartClient); 
      Thread ReceivedThread = new Thread(Client.ThreadStartReceiverClient); 

      ClientThread.Start(); 
      ReceivedThread.Start(); 


     } 
    } 

一切都按預期工作。我能夠向我的目標進程發出命令(大膽)。我的問題是,我基本上想圍繞此代碼包裝C#GUI,但不知道如何修改它,以便通信完成而無需使用控制檯,因爲命令將通過GUI發出或來自代碼。

我試圖打開StreamWriter的SW到類變量,通過屬性暴露它,並用一個方法調用sw.WriteLine(),但似乎並沒有工作。

所以我不能確定如何將流封裝對象中來回很好。

我發現這篇文章看起來像是現貨,Using Named Pipes to Connect a GUI to a Console App in Windows,但不幸的是它似乎並沒有附帶任何代碼,並且沒有任何引用。

那麼,如何使用命名管道,而無需使用控制檯發出的命令?

回答

5

你想要做的是採取邏輯主件它們是發送者,接收者出來的代碼,並將其改寫成可以像一個特定用途的包裝類中使用可重複使用的類。

也許下面的代碼可以作爲一個準則(我沒有檢查,看看是否正常工作,則可能需要微小的變化)

public sealed class ResponseReceivedEventArgs : EventArgs 
{ 
    public ResponseReceivedEventArgs(string id, string response) 
    { 
     Id = id; 
     Response = response; 
    } 

    public string Id 
    { 
     private set; 
     get; 
    } 

    public string Response 
    { 
     private set; 
     get; 
    } 
} 

public delegate void ResponseReceived(object sender, ResponseReceivedEventArgs e); 

public sealed class NamedPipeCommands 
{ 
    private readonly Queue<Tuple<string, string>> _queuedCommands = new Queue<Tuple<string,string>>(); 
    private string _currentId; 

    private readonly Thread _sender; 
    private readonly Thread _receiver; 

    // Equivalent to receiving a "quit" on the console 
    private bool _cancelRequested; 

    // To wait till a response is received for a request and THEN proceed 
    private readonly AutoResetEvent _waitForResponse = new AutoResetEvent(false); 

    // Lock to modify the command queue safely 
    private readonly object _commandQueueLock = new object(); 

    // Raise an event when a response is received 
    private void RaiseResponseReceived(string id, string message) 
    { 
     if (ResponseReceived != null) 
      ResponseReceived(this, new ResponseReceivedEventArgs(id, message)); 
    } 

    // Add a command to queue of outgoing commands 
    // Returns the id of the enqueued command 
    // So the user can relate it with the corresponding response 
    public string EnqueueCommand(string command) 
    { 
     var resultId = Guid.NewGuid().ToString(); 
     lock (_commandQueueLock) 
     { 
      _queuedCommands.Enqueue(Tuple.Create(resultId, command)); 
     } 
     return resultId; 
    } 

    // Constructor. Please pass in whatever parameters the two pipes need 
    // The list below may be incomplete 
    public NamedPipeCommands(string servername, string pipeName) 
    { 
     _sender = new Thread(syncClientServer => 
     { 
      // Body of thread 
      var waitForResponse = (AutoResetEvent)syncClientServer; 

      using (var pipeStream = new NamedPipeClientStream(servername, pipeName, PipeDirection.Out, PipeOptions.None)) 
      { 
       pipeStream.Connect(); 

       using (var sw = new StreamWriter(pipeStream) { AutoFlush = true }) 
        // Do this till Cancel() is called 
        while (!_cancelRequested) 
        { 
         // No commands? Keep waiting 
         // This is a tight loop, perhaps a Thread.Yield or something? 
         if (_queuedCommands.Count == 0) 
          continue; 

         Tuple<string, string> _currentCommand = null; 

         // We're going to modify the command queue, lock it 
         lock (_commandQueueLock) 
          // Check to see if someone else stole our command 
          // before we got here 
          if (_queuedCommands.Count > 0) 
           _currentCommand = _queuedCommands.Dequeue(); 

         // Was a command dequeued above? 
         if (_currentCommand != null) 
         { 
          _currentId = _currentCommand.Item1; 
          sw.WriteLine(_currentCommand.Item2); 

          // Wait for the response to this command 
          waitForResponse.WaitOne(); 
         } 
        } 
      } 
     }); 

     _receiver = new Thread(syncClientServer => 
     { 
      var waitForResponse = (AutoResetEvent)syncClientServer; 

      using (var pipeStream = new NamedPipeClientStream(servername, pipeName, PipeDirection.In, PipeOptions.None)) 
      { 
       pipeStream.Connect(); 

       using (var sr = new StreamReader(pipeStream)) 
        // Do this till Cancel() is called 
        // Again, this is a tight loop, perhaps a Thread.Yield or something? 
        while (!_cancelRequested) 
         // If there's anything in the stream 
         if (!sr.EndOfStream) 
         { 
          // Read it 
          var response = sr.ReadLine(); 
          // Raise the event for processing 
          // Note that this event is being raised from the 
          // receiver thread and you can't access UI here 
          // You will need to Control.BeginInvoke or some such 
          RaiseResponseReceived(_currentId, response); 

          // Proceed with sending subsequent commands 
          waitForResponse.Set(); 
         } 
      } 
     }); 
    } 

    public void Start() 
    { 
     _sender.Start(_waitForResponse); 
     _receiver.Start(_waitForResponse); 
    } 

    public void Cancel() 
    { 
     _cancelRequested = true; 
    } 

    public event ResponseReceived ResponseReceived; 
} 

你可以看到,我已創建抽象爲到Console.ReadLine (命令隊列)和Console.WriteLine(事件)。 「quit」也是一個布爾變量,現在由「Cancel()」方法設置。顯然,這不是最好的/正確的方法 - 我只是向你展示一種將上面的命令式代碼與可重用的包裝類關聯起來的方法。

+0

上帝保佑你。驚人!這工作完美無瑕。 我做了添加在構造函數中附加參數以來的要在另一pipeName和從管道未命名相同的唯一變化。 – bulltorious

+0

LOL,我不確定它有多完整 - 但它本意是成爲未來問題的準則。很高興它解決了! – Ani