I am trying to run a bash script from inside a windows forms application. The code I have looks like this:
// Command String
string flashNVS = "c:\\msys32\\usr\\bin\\env MSYSTEM=MINGW32 "
"c:\\msys32\\usr\\bin\\bash -l -c "
"\"cd " unixProdPath
" && ./provision-device-secure " deviceId " \"";
cmdProcess.ExecuteCommandSync(flashNVS);
With the cmdProcess code:
public static void ExecuteCommandSync(object command)
{
flashOK = 0;
try
{
// create the ProcessStartInfo using "cmd" as the program to be run, and "/c " as the parameters.
// Incidentally, /c tells cmd that we want it to execute the command that follows, and then exit.
System.Diagnostics.ProcessStartInfo procStartInfo = new System.Diagnostics.ProcessStartInfo("cmd", "/c " command);
// The following commands are needed to redirect the standard output.
//This means that it will be redirected to the Process.StandardOutput StreamReader.
procStartInfo.RedirectStandardOutput = true;
procStartInfo.RedirectStandardError = true;
procStartInfo.UseShellExecute = false;
// Do not create the black window.
procStartInfo.CreateNoWindow = true;
// Now we create a process, assign its ProcessStartInfo and start it
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo = procStartInfo;
proc.Start();
// Get the output into a string
string result = proc.StandardOutput.ReadToEnd();
//resultString = result;
if ( (result.IndexOf("Hard resetting via RTS pin") > 0) || // Success FW Flash OR NVS Flash!
(result.IndexOf("Hash of data verified") > 0) ) // Success NVS Flash
{
Console.WriteLine("SUCCESS : FlashOK = 1");
flashOK = 1;
}
// Display the command output.
Console.WriteLine(result);
}
catch (Exception objException)
{
// Log the exception
Console.WriteLine("ExecuteCommandSync failed" objException.Message);
}
}
The file ./provision-device-secure script does a lot of stuff. And it works fine except for one line:
$IDF_PATH/components/esptool_py/esptool/esptool.py -p $ESPPORT --before default_reset --after no_reset write_flash 0x3E0000 mfg_config_encrypted.bin 0x3E8000 nvs_keys.bin
The completely baffling thing is that if I run the script directly from inside git bash,it works fine. But from inside the windows forms application, that line doesn't complete properly... I need it to run as part of a windows app I'm writing. Any suggestions of how to tweak my code to get this to run? And an explanation of why it doesn't?
Just to describe the failure. In the git bash, it executes with this output:
Created encryption keys: ===> C:\Users\thebi\esp\esp32_repo\11_SN_WI_003_WiFi\prod\keys\nvs_keys.bin
Creating NVS binary with version: V2 - Multipage Blob Support Enabled
Created NVS binary: ===> C:\Users\thebi\esp\esp32_repo\11_SN_WI_003_WiFi\prod\mfg_config_encrypted.bin
but in the windows forms it only does this:
Created encryption keys: ===> C:/Users/thebi/esp/esp32_repo/11_SN_WI_003_WiFi/prod/keys/nvs_keys.bin
Creating NVS binary with version: V2 - Multipage Blob Support Enabled
And the output file (mfg_config_encrypted.bin) is zero length...
CodePudding user response:
First of all you should quote your parameters as they have spaces inside. Second of all... I will just give you class that I have written:
public delegate void ProcessMessageDelegate(object sender, string msg);
public class ProcessManager
{
public event ProcessMessageDelegate OnOutputData;
public event ProcessMessageDelegate one rrorData;
public event EventHandler OnProcessEnd;
Process process;
List<string> outputBuffer = new List<string>();
public string GetProcessOutput(string cmdName, List<string> args)
{
InitProcess(cmdName, args);
string result = "";
if (process.Start())
{
try
{
process.BeginOutputReadLine();
process.StandardError.ReadToEnd();
process.WaitForExit();
return OutputBufferToStr();
}
catch (Exception ex)
{
if (string.IsNullOrWhiteSpace(result))
{
one rrorData?.Invoke(this, ex.Message);
return null;
}
else
return result;
}
}
return result;
}
public void RunProcessAsync(string cmdName, List<string> args)
{
InitProcess(cmdName, args);
if (process.Start())
{
process.BeginOutputReadLine();
process.StandardError.ReadToEnd();
}
}
void InitProcess(string cmdName, List<string> args)
{
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = cmdName;
List<string> escapedArgs = new List<string>();
foreach (var arg in args)
{
string validArg = arg;
if (validArg.Contains(" "))
validArg = $"\"{validArg}\"";
escapedArgs.Add(validArg);
}
psi.Arguments = string.Join(" ", escapedArgs);
psi.ErrorDialog = true;
psi.UseShellExecute = false;
psi.RedirectStandardError = true;
psi.RedirectStandardOutput = true;
psi.WindowStyle = ProcessWindowStyle.Hidden;
psi.CreateNoWindow = true;
process = new Process();
process.StartInfo = psi;
process.OutputDataReceived = Process_OutputDataReceived;
process.Exited = Process_Exited;
process.Disposed = Process_Disposed;
process.EnableRaisingEvents = true;
outputBuffer.Clear();
}
string OutputBufferToStr()
{
return string.Join(Environment.NewLine, outputBuffer);
}
private void Process_Disposed(object sender, EventArgs e)
{
OnProcessEnd?.Invoke(this, EventArgs.Empty);
}
private void Process_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
outputBuffer.Add(e.Data);
one rrorData?.Invoke(this, e.Data);
}
private void Process_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
outputBuffer.Add(e.Data);
OnOutputData?.Invoke(this, e.Data);
}
private void Process_Exited(object sender, EventArgs e)
{
if (process != null)
process.Dispose();
OnProcessEnd?.Invoke(this, EventArgs.Empty);
}
}
Feel free to use it.
Now, you have here two main methods:
GetProcessOutput
that runs process synchronuosly and returns whole output to you. You just pass path to the process and list of parameters.
The second method is RunProcessAsync
- it runs the process asynchrouosly. You call it the same way, BUT you have to connect to events: OnOutputData - it's called everyrtime that something is written to output
OnErrorData - when error occurs
OnProcessEnd - when the process ends.
I hope you'll like it.
CodePudding user response:
Just going to throw this out. I'm on very shaky ground here as I don't really understand this stuff in any detail. But what eventually worked was to change the command to select git bash, not MINW32. Like this:
string flashNVS = "\"C:\\Program Files\\Git\\git-bash.exe\" -c \"cd " unixProdPath
" && ./provision-device-secure test-dev > unixLog.txt \"'";
Console.WriteLine(flashNVS);
I'm not going to accept my own answer though, because it doesn't explain the why part of this. If anyone can fill in the blanks, that would be great.