1

I'm essentially building a remote command line using a Callback WCF Service (with the ultimate goal to manage & update game server instances on my personal server mostly by way of SteamCMD). I am able to send commands to the remote service, have them run in "cmd.exe", and successfully receive real-time progress updates to a WinForm and MVC website test client

enter image description here

However, I would like to add functionality to wait for/provide user input if necessary, mostly because some Steam games servers do not allow anonymous login through SteamCMD and the user will need to enter their steam password or 2FA. A good example I am using for testing input is the "date" command in windows command prompt. Normally, it prints the date then prompts the user to "Enter a new date".

enter image description here

But, when I run the "Date" command through my service, the prompt to enter the new date does not get read on the ReadLine(), and instead just indefinitely waits for input I assume.

enter image description here

The problems I am running into:

  • How do I detect when the Process is waiting for user input
  • The Process is not outputting the last line that typically prompts the user for input (hangs on ReadLine )

The following code blocks are the main functions handling the Process and the redirect of StdOut. "RunCommandLine" pretty much starts the Process and sets all the event handlers, and the "Processer_DoWork" handles pushing the StdOut to the CallbackFunction so the client can see the output in real time.

Is what I am seeking possible with how I have this set up? I was able to implement an interrupt functionality, so I'm thinking I can do something similar for passing the actual input to the process.. I am just lost on how to detect the input is actually needed.

Thanks in advance for any help!

private BackgroundWorker Processer = new BackgroundWorker();
public IGameManagerCallback Callback;

public void RunCommandLine(string WorkingPath, string Commands)
{
    if (Processer.IsBusy)
    {
        return;
    }

    Processer.WorkerReportsProgress = true;
    Processer.WorkerSupportsCancellation = true;
    Processer.ProgressChanged += Processer_ProgressChanged;
    Processer.DoWork += Processer_DoWork;
    Processer.RunWorkerCompleted += Processer_WorkComplete;
    Callback = OperationContext.Current.GetCallbackChannel<IGameManagerCallback>();


    ProcessStartInfo StartInfo = new ProcessStartInfo
    {
        FileName = "cmd.exe",
        Arguments = $@"/c {Commands}",
        UseShellExecute = false,
        RedirectStandardOutput = true,
        RedirectStandardError = true,
        RedirectStandardInput = true,
        CreateNoWindow = true
    };

    if (!string.IsNullOrEmpty(WorkingPath))
        StartInfo.WorkingDirectory = Path.GetDirectoryName($@"{WorkingPath}");

    Process CommandProcess;
    try { CommandProcess = Process.Start(StartInfo); }
    catch (Exception ex)
    {
        Console.WriteLine($"Error starting: {ex.Message}");
        return;
    }

    //attach to background processor            
    Processer.RunWorkerAsync(CommandProcess);
}

private void Processer_DoWork(object sender, DoWorkEventArgs e)
{
    BackgroundWorker worker = (BackgroundWorker)sender;
    Process proc = e.Argument as Process;
    StreamReader StandardOutput = proc.StandardOutput;
    StreamWriter StandardInput = proc.StandardInput;
    string data = StandardOutput.ReadLine();
    while (data != null)
    {
        if (worker.CancellationPending)
        {
            e.Cancel = true;
            break;
        }

        Processer.ReportProgress(0, data);
        data = StandardOutput.ReadLine();//this is where we hang when there is input required
    }
}
Ramil Aliyev 007
  • 4,437
  • 2
  • 31
  • 47
Joe S
  • 21
  • 3
  • Like any communication system the receiver needs to know where ever message ends. Usually with a command line tool you end each message with a CR. You also in windows need to do a Flush() because data is moved in a stream based on the number of bytes and timers. Also when a application closes you have to make sure the data is read before app closes so the last bytes do not get lost. – jdweng Apr 26 '21 at 20:02

0 Answers0