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
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".
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.
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
}
}