C# Named Pipes sin emitir comandos desde la Consola?

Estoy usando Named Pipes para comunicarme con un proceso. He podido hacer que funcione con el siguiente código. (El código original se encuentra aquí: a través de 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();


        }
    }

Todo funciona según lo previsto. Puedo emitir comandos a mi proceso de destino (audacia).

Mi problema es que básicamente quiero envolver una GUI de C# alrededor de este código, pero no estoy seguro de cómo modificarlo para que la comunicación se realice sin tener que usar la consola, ya que los comandos se emitirían a través de la GUI o desde el código.

Intenté convertir streamWriter sw en una variable de clase, exponiéndolo a través de la propiedad y llamando a sw.WriteLine() con un método, pero eso no parece funcionar.

Por lo tanto, no estoy seguro de cómo encapsular la transmisión de un lado a otro muy bien dentro de un objeto.

Encontré este artículo que parecía estar en el clavo, Uso de canalizaciones con nombre para conectar una GUI a una aplicación de consola en Windows, pero desafortunadamente no parece venir con ningún código y está un poco por encima de mi cabeza sin ningún tipo de referencia.

Entonces, ¿cómo puedo usar canalizaciones con nombre sin tener que usar la consola para emitir los comandos?

preguntado el 03 de mayo de 12 a las 14:05

1 Respuestas

Lo que quiere hacer es tomar las piezas principales de lógica que son el remitente, el receptor fuera de ese código y reescribirlo en una clase reutilizable que pueda usarse como una clase contenedora de propósito específico.

Tal vez el código a continuación podría servir como guía (NO he verificado si esto funciona, podría requerir cambios menores)

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;
}

Puede ver que he creado abstracciones para Console.ReadLine (la cola de comandos) y Console.WriteLine (el evento). "Quit" también es una variable booleana que ahora se establece mediante el método "Cancel()". Obviamente, esta no es la forma más óptima/correcta de hacerlo: solo le estoy mostrando una forma de relacionar el código imperativo de arriba en una clase contenedora que se puede reutilizar.

contestado el 03 de mayo de 12 a las 16:05

Salud. ¡INCREÍBLE! Esto funciona perfectamente. El único cambio que hice fue agregar un parámetro adicional en el constructor para otro nombre de tubería, ya que las tuberías Hacia y Desde no tienen el mismo nombre. - bulltorio

LOL, no estaba seguro de cuán completo era, pero estaba destinado a ser una guía para futuros problemas en el mismo sentido. ¡Me alegro de que haya funcionado! - Ani

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.