Home > Software design >  Programmatically input "ENTER" argument in cmd.exe process
Programmatically input "ENTER" argument in cmd.exe process

Time:10-30

I'm working on a C# WinForms app to automate a manual TLS/SSL certificate request executed in the Windows cmd window. The CA provides a desktop client which communicates with the CA's servers which issue domain ownership "Challenges" and perform ownership verification.

Basically it is a three step process:

  1. CA issues challenges
  2. user creates DNS TXT records containing the challenges on the relevant domain
  3. CA checks for the records; if found a new SSL cert is issued

During Challenge issuance processing pauses for each domain listed in the request and waits for the user to press ENTER. After the final challenge is issued the next time ENTER is pressed it kicks off verification. Manually running the process in the CMD window results in

J:\Verify_DNS>le64 --key account.key --email "[email protected]" 
                    --csr example.csr --csr-key example.key 
                    --crt example.crt --generate-missing 
                    --domains "example.com,*.example.com"  
                    --handle-as dns
[ Crypt::LE64 client v0.38 started. ]
Challenge for example.com requires the following DNS record to be created:
Host: _acme-challenge.example.com, type: TXT, value:  
      cb4XNLh_ZnkwkuD7EiwlV9wk7qsP8QHLHUlQ2OO5DX8
Check for DNS propagation using: 
      nslookup -q=TXT _acme-challenge.example.com
When you see a text record returned, press <Enter>

Challenge for *.example.com requires the following DNS record to be created:
Host: _acme-challenge.example.com, type: TXT, value: 
      nUhw1Xy9H219nfiEx0vZuGDVbpe5KXuUenFoOlc3-4Q
Check for DNS propagation using:
      nslookup -q=TXT _acme-challenge.example.com
When you see a text record returned, press <Enter>

Processing the 'dns' verification
Verification result for 'example.com': success.
Verification result for '*.example.com': success.
Saving the full cert chain to example.crt.
The job is done.
J:\Verify_DNS>

Since DNS propagation times are unpredictable the process can be split into two steps by appending a "delay" flag to the argument. When split Step One of the process halts after issuing the challenges and returns control to the user.

Code running Step One and nslookup successfully

ProcessStartInfo si = new ProcessStartInfo();
si.FileName = @"C:\Windows\System32\cmd.exe";
si.RedirectStandardOutput = true;
si.RedirectStandardError = true;
si.RedirectStandardInput = true;
si.UseShellExecute = false;
si.CreateNoWindow = true;
si.WorkingDirectory = Terms.dir_DNS;
si.Verb = "runas";

using (Process proc = new Process())
{
    proc.StartInfo = si;
    proc.ErrorDataReceived  = cmd_DataReceived_DNS;
    proc.OutputDataReceived  = cmd_DataReceived_DNS;
    proc.EnableRaisingEvents = true;
    proc.Start();
    proc.PriorityClass = ProcessPriorityClass.RealTime;
    proc.BeginOutputReadLine();
    proc.BeginErrorReadLine();
    if (nslookup) proc.StandardInput.WriteLine(@"ipconfig /flushdns");
    proc.StandardInput.WriteLine(arg); 
    proc.StandardInput.WriteLine("exit");
    proc.WaitForExit();
}

In Step Two the process runs in its entirety from the top with the attendant pauses waiting for the user to press ENTER. To automate the process I need to "simulate/emulate" pressing ENTER in the cmd window.

This problem appears to be a show stopper; any ideas will be much appreciated.

[EDIT] removed confusing failed attempts inappropriately trying to input { ENTER } via SendKeys

CodePudding user response:

Simply call proc.StandardInput.WriteLine() without any arguments, since pressing enter in a console window is just writing a line terminator to stdin, usually \r\n(Windows) or \n(*nix), aka CR/LF.

Since DNS propagation times are unpredictable the process can be split into two steps by appending a "delay" flag to the argument.

An ideal approach would be waiting the desired output and continue the execution. In this case, you may want to read the output synchronously instead using BeginOutputReadLine():

string output = null;

// Replace "completed_message" with the termination message you want, maybe "The job is done" here.
while(!(output = proc.StandardOutput.ReadLine()).Contains("completed_message"))
{
   ProcessOutput(output);
}

// continue execution or exit the process here
  • Related