Home > Software engineering >  Pipe throw error (trying to send command at wpf app already running)
Pipe throw error (trying to send command at wpf app already running)

Time:01-14

Pipe throw error I can't fix. I want to send commandline at execution time using Command Pipe. Here is the code, the message arrive without a problem but after that throw error:

  public MainWindow()
    {
    InitializeComponent();
    _pipeServer = new NamedPipeServerStream("CommandPipe", PipeDirection.In);
    Thread listenThread = new Thread(ListenForCommands);
    listenThread.Start();
   }
 
  private void ListenForCommands()
   {
    while (true)
    {
        if (!_pipeServer.IsConnected)
        {
            _pipeServer.WaitForConnection();
        }


        using (StreamReader sr = new StreamReader(_pipeServer))
        {
           
            while (_pipeServer.IsConnected)
            {
                string commandLineArguments = sr.ReadLine();
                if (string.IsNullOrEmpty(commandLineArguments))
                    continue;
                // Process the command line arguments
                ProcessCommand(commandLineArguments);
            }
        }
       
    }
}

private void ProcessCommand(string commandLineArguments)
{

    // Your command processing logic here

    MessageBox.Show(commandLineArguments);
    //...
}

The error is System.ObjectDisposedException: 'Cannot access a closed pipe.'

When send command prompt:

echo Hello World > .\pipe\CommandPipe

As I said before message works ok, but throws that error after closing the message box.


OK here an update with working code after Alex suggestion:

 public MainWindow()
    {
        InitializeComponent();
        _pipeWorker = new BackgroundWorker();
        _pipeWorker.DoWork  = PipeWorker_DoWork;
        _pipeWorker.RunWorkerAsync();


    }
   
   private void PipeWorker_DoWork(object sender, DoWorkEventArgs e)
    {
        _pipeServer = new NamedPipeServerStream("CommandPipe", PipeDirection.In);
        while (true)
        {
            _pipeServer.WaitForConnection();
            using (var reader = new StreamReader(_pipeServer))
            {
                string command = reader.ReadLine();
                // Handle the command here
                Dispatcher.Invoke(() => HandleCommand(command));
            }
        }
    }


     private void HandleCommand(string command)
    {
      _pipeServer.Dispose();
      _pipeServer = new NamedPipeServerStream("CommandPipe", PipeDirection.In);

     // do anything you want with the command received
     mytextbox.Text = command;


    }

For test just from windows command prompt you can send messages in in this way:

echo CommandMessage1 > \.\pipe\CommandPipe

echo CommandMessage2 > \.\pipe\CommandPipe

etc!

CodePudding user response:

I suppose you are using WPF (or Maybe WForms) but not a console application, as far you have a message box. Anyway, somewhere there is main and this main instantiates a windows and whatever else necessary for your app to exist. It will likely end with a .Run() method that will bind the lifetime of your application to the lifetime fo the main Window (if you did not create any foreground tasks that will live longer, keeping the program alive after you closed the main windows). Anyway, your MainWindow() is not of that family and will return (finish the method execution) as soon as the listener thread will be started.

First off: I don't catch the reason to create a thread that way. Use a background worker instead.

Second: your _pipServer is a disposable Stream. It will end (get disposed) at the exit of MainWindos(). The thred that will wait for connection will likely see immediately a dead (disposed) object.

Look at https://learn.microsoft.com/en-us/dotnet/api/system.io.pipes.namedpipeserverstream?view=net-7.0 to get how to correctly istantiate a pipe. The main method in the example is all hat has to survive during the entire lifetime of your app (i.e. being the main body of the backgroundWorker).

To better understand how the flow enters and exits the methods: use this snippet:

public MainWindow()
{
    InitializeComponent();
    _pipeServer = new NamedPipeServerStream("CommandPipe", PipeDirection.In);
    Thread listenThread = new Thread(ListenForCommands);
    listenThread.Start();
    try
    {
        MessageBox.Show($"Leaving Main Windows {DateTime.Now} state of pipe CanRead: {_pipeServer.CanRead});
    }
    catch 
    {
        MessageBox.Show($"Pipe is already dead);
    }
}

private void ListenForCommands()
    {
    int count = 0;
    while (true)
     {
        try 
        {
            MessageBox.Show($"ListenForCommands() iteration {count}. Status of pipe Can Read: {pipeServer.CanRead});
            if (!_pipeServer.IsConnected)
            {
                _pipeServer.WaitForConnection();
            }

            using (StreamReader sr = new StreamReader(_pipeServer))
            {
               
                while (_pipeServer.IsConnected)
                {
                    string commandLineArguments = sr.ReadLine();
                    if (string.IsNullOrEmpty(commandLineArguments))
                        continue;
                    // Process the command line arguments
                    ProcessCommand(commandLineArguments);
                }
            }
        } 
        catch
        {
            MessageBox.Show($"Errors in _pipeServer access from ListenForCommands(). Iteartion {count}");
        }
        finally {count  ;}       
    }

private void ProcessCommand(string commandLineArguments)
{

    // Your command processing logic here

    MessageBox.Show(commandLineArguments);
    //...
    MessageBox.Show($"Leaving ProcessCommand {DateTime.Now});
}
  • Related