C# 命名管道,无需从控制台发出命令

本文关键字:控制台 命令 管道 | 更新日期: 2023-09-27 18:32:58

我正在使用命名管道与进程进行通信。 我已经能够使它使用以下代码工作。 (原始代码在这里找到:通过 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(( 与方法,但这似乎不起作用。

所以我不确定如何在对象中很好地来回封装流。

我发现这篇文章似乎很到位,使用命名管道将 GUI 连接到 Windows 中的控制台应用程序,但不幸的是,它似乎没有任何代码,而且在我的头上没有任何可参考的。

那么如何使用命名管道而不必使用控制台发出命令呢?

C# 命名管道,无需从控制台发出命令

你要做的是从该代码中取出主要的逻辑片段,即发送方、接收方,并将其重写为可重用的类,该类可以像特定于目的的包装类一样使用。

也许下面的代码可以作为指南(我没有检查这是否有效,它可能需要细微的更改(

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(("方法设置。显然,这不是最优化/正确的方法——我只是向您展示一种将上面的命令式代码关联到可以重用的包装类中的方法。