0

I have a client / server program which runs os commands from one pc on the other one. This is my client code for running the command :

        Process p = new Process();

        p.StartInfo.UseShellExecute = false;
        p.StartInfo.CreateNoWindow = true;
        p.StartInfo.RedirectStandardOutput = true;
        p.StartInfo.RedirectStandardError = true;
        p.StartInfo.FileName = "cmd.exe";
        p.StartInfo.Arguments = $"/c {command}";
        p.Start();

        string output = p.StandardOutput.ReadToEnd();
        string error = p.StandardError.ReadToEnd();
        p.WaitForExit();
        // sending output and errors back to server...

This code is a method which gets a parameter : command (sent from server) However, this work fine until I'm trying to send two commands that are attached to each other, for example :

cd c://
dir

When I try to send these two commands (separately, the user enters one in a row each time in the server), it runs the cd first then the dir in separate processes.

Is there any way to fix this? (e.g. create one process of cmd and every time enter one line and get the output without closing it)

Thanks in advance.

Lior V
  • 23
  • 6
  • Yes, it is possible by using the right command line to executed by `cmd.exe`. Open a [command prompt](https://www.howtogeek.com/235101/), run `cmd /?` and read the output usage help explaining how the argument string(s) after option `/C` (run command line and __close__) or option `/K` (run command line and __keep__ running) are interpreted by `cmd.exe` depending on various criteria. Read also [single line with multiple commands](https://stackoverflow.com/a/25344009/3074564) explaining the usage of the command operators `&`, `&&` and `||`. – Mofi Jun 03 '22 at 19:07
  • Further, it would be a good idea to read the Microsoft documentation pages for the [Process Class](https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.process) and the [ProcessStartInfo Class](https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.processstartinfo) which has the `WorkingDirectory` property to define the directory being the current working directory for the started application which is in this case `cmd.exe`. Then a command like `cd` to change the current working directory is not needed at all. – Mofi Jun 03 '22 at 19:13
  • You can use `RedirectStandardInput = true` in conjunction with `p.StandardInput.WriteLine("cd ..");` (for example) and that will achieve the desired functionality on that end. But you'll have to re-think how you get the output as `ReadToEnd` won't work since it'll be waiting forever. – Kirk Woll Jun 03 '22 at 19:14
  • There should be read also the MS documentation pages [Naming Files, Paths, and Namespaces](https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file) explaining that ``\`` is the directory separator on Windows and not `/` as on Linux/Mac and [CreateProcess](https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw) and [STARTUPINFO](https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfow) for which the c# `Process` and `ProcessStartInfo` classes are c# wrapper classes. – Mofi Jun 03 '22 at 19:16
  • @Mofi, Thank you very much for your comment. I'll just use the command operator, &. – Lior V Jun 03 '22 at 19:17
  • @KirkWoll The usage of `RedirectStandardInput = true` is of no help on using `cmd.exe` with option `/c` instructing `cmd.exe` to execute the `command` and then close itself. There cannot be sent one more command via the standard input stream to Windows Command Processor if it terminates itself after execution of the command specified after option `/c`. This method should be never used at all because of `cmd.exe` is not designed to interact with other processes. It is designed to execute commands and executables and that's it. – Mofi Jun 03 '22 at 19:19
  • @Mofi, yes I know that. I wasn't disputing anything you had written. I was providing more details on the input side of the question. – Kirk Woll Jun 03 '22 at 19:21
  • One more hint: If there should be run multiple executables and not multiple internal commands of `cmd.exe` for which `cmd` uses Windows library functions all available also as C# functions, it is better to use the `Process class` multiple times for each executable to run instead of using the `Process class` (`CreateProcess`) to run `cmd.exe` which uses also `CreateProcess` to run each executable specified on a command line. – Mofi Jun 04 '22 at 08:15

1 Answers1

1

I will admit that I am doing that on the same computer but I am using the events for new data received. An example will help here...

ProcessStartInfo procStartInfo = new ProcessStartInfo("CMD", "");
procStartInfo.RedirectStandardOutput = true;
procStartInfo.RedirectStandardInput = true;
procStartInfo.RedirectStandardError = true;
procStartInfo.UseShellExecute = false;
procStartInfo.CreateNoWindow = true;

errors = "";
lines = "";
proc = new Process();
proc.StartInfo = procStartInfo;
proc.OutputDataReceived += new DataReceivedEventHandler(proc_OutputDataReceived);
proc.ErrorDataReceived += new DataReceivedEventHandler(proc_ErrorDataReceived);
proc.Start();
myStreamWriter = proc.StandardInput;
proc.BeginOutputReadLine();
proc.BeginErrorReadLine();

Then there are the event handlers (I will include the one for stdout here - there is one for the error as well):

void proc_OutputDataReceived(object sender, DataReceivedEventArgs e)
        {
            if (e.Data != null)
            {
                // Get the new data, show it on screen and store for retrieval.
                string newLine = e.Data.Trim() + Environment.NewLine;
                lines = lines + newLine;
            }
        }

As usual, don't forget to unsubscribe from the events and close the stream when done.

RobCole
  • 30
  • 5