没有从控制台发出命令的 C# 命名管道?
Posted
技术标签:
【中文标题】没有从控制台发出命令的 C# 命名管道?【英文标题】:C# Named Pipes without issuing commands from the Console? 【发布时间】:2012-05-03 13:04:16 【问题描述】:我正在使用命名管道与进程通信。我已经能够使用以下代码使其工作。 (此处找到原始代码:via archive.org)
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,但不幸的是,它似乎没有附带任何代码,而且我没有任何参考。
那么如何在不使用控制台发出命令的情况下使用命名管道呢?
【问题讨论】:
【参考方案1】:您要做的是从该代码中取出主要的逻辑部分,即发送者和接收者,并将其重写为一个可重用的类,该类可以像特定用途的包装类一样使用。
也许下面的代码可以作为指导(我没有检查这是否有效,它可能需要稍作改动)
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()”方法设置。显然,这不是最佳/正确的做法——我只是向您展示一种将上面的命令式代码关联到可以重用的包装类的方法。
【讨论】:
上帝保佑你。惊人的!这完美无缺。我所做的唯一更改是在构造函数中为另一个 pipeName 添加了一个附加参数,因为 To 和 From 管道的名称不同。 大声笑,我不确定它有多完整 - 但它旨在作为未来同样问题的指南。很高兴它成功了!以上是关于没有从控制台发出命令的 C# 命名管道?的主要内容,如果未能解决你的问题,请参考以下文章