From a C# application I'm starting a process to run Debug.exe (...\system32\Debug.exe) using an existing file as Debug's parameter.
This is a list of StartInfo parameters I've tried:
FileName = "Debug.exe", Arguments = "CS.exe"
FileName = "cmd.exe", Arguments = "/C Debug.exe CS.exe", WorkingDirectory = "", UseShellExecute = false, RedirectStandardOutput = true
To access CS.exe I use Directory.SetCurrentDirectory(*fullname of CS*) before starting the process
But all I get is an empty window with a blinking cursor.
What am I doing wrong here?
Thanks in advance for your help
Have you tried through PowerShell?
#$script = "c:\Windows\System32\calc.exe" (location of .exe to be executed)
#$csdefFilaPath = "C:\Input.txt" (Parameter that the exe accepts/path)
Param($scriptLocation, $filePath)
function RunExe()
{
Write-Output "Running exe"
& $scriptLocaion $filePath
Write-Output "Finished Running exe"
}
Call -> RunExe
Related
I'm having issues with a process started in C# not outputting to console despite it's output being redirected.
I'm running a console application inside a unityci docker container which allows me to start unity in batch mode and is supposed to output something to console.
If I use bash to start unity using unity-editor -projectPath myProject -executeMethod myMethod -logFile - I get all the output displayed in console as expected.
If I use C# to start a bash process using the same arguments, I get no output.
Here's the code I'm using to start a new process:
void StartProcess()
{
string argsString = "-projectPath myPath -executeMethod myMethod -logFile -";
ProcessStartInfo startInfo = new ProcessStartInfo(argsString)
{
FileName = "/bin/bash",
Arguments = $"-c unity-editor \"{argsString}\"",
RedirectStandardOutput = true,
RedirectStandardError = false,
UseShellExecute = false,
CreateNoWindow = true,
};
using Process proc = Process.Start(startInfo);
OutputDataReceived += OnOutputDataReceived;
proc.BeginOutputReadLine();
proc.WaitForExit();
}
void OnOutputDataReceived(object obj, DataReceivedEventArgs args)
{
if(args.Data.StartsWith("[")
Console.WriteLine(args.Data);
}
The unity-editor command is part of the unityci docker container I'm using. It does the following:
#!/bin/bash
if [ -d /usr/bin/unity-editor.d ] ; then
for i in /usr/bin/unity-editor.d/*.sh; do
if [ -r $i ]; then
. $i
fi
done
fi
xvfb-run -ae /dev/stdout "$UNITY_PATH/Editor/Unity" -batchmode "$#"
So in the end I have a console application that runs an executable that starts another application, and the console output gets lost somewhere in between.
Can someone explain where the output is going and how to get it to display in console?
I figured it out. The problem was how I was starting the process.
Instead of starting a instance of bash to run a script like this:
ProcessStartInfo startInfo = new ProcessStartInfo("/bin/bash", $"-c unity-editor \"{argsString}\"");
I should have just started the script as a process directly:
ProcessStartInfo startInfo = new ProcessStartInfo("unity-editor", argsString);
I was under the impression that you can't start bash scripts as processes directly as that would throw a System.ComponentModel.Win32Exception: Exec format error exception, but having #!/bin/bash as the first line of the script allows them to be run without issue.
I have some script files that would usually be edited and run through a ui, but the offer to run on the command line when using the syntax: program.exe scriptfile.fmw --userparameter "some parameter".
If I simply write
arguments = "program.exe scriptfile.fmw --userparameter \"some parameter\"";
Process process = Process.Start("cmd.exe", $"/K {arguments};
process.WaitForExit();
the commandline starts, calls the correct exe and runs the script.
Now I want to retrieve the response of the script after it ran to catch possible error messages.
As far as I can see this requires to use ProcessStartInfo and that's basically where my problem seems to start.
ProcessStartInfo startInfo = new ProcessStartInfo()
{
FileName = "cmd.exe",
Arguments = $"/K {arguments}",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true
};
Process process = Process.Start(startInfo);
process.BeginErrorReadLine();
process.WaitForExit();
The code above opens a commandline window, but never uses the arguments given.
It escapes me on how I should hand the argument line over to the cmd.exe - process.
I was playing around with RedirectStandardInput and its StreamWriter, but never managed to get anything written into the cmd-window.
The same goes for the RedirectStandardOutput, where I would like to gather the script response in the cmd-window as a string or string[], to parse for specific exit codes of the script.
I think this is what you want, have a look below :
Process process = new Process();
ProcessStartInfo startInfo = new ProcessStartInfo()
{
FileName = "program.exe", //You must use your program directly without invoking through cmd.exe
Arguments = "scriptfile.fmw --userparameter \"some parameter\"", //Add parameters and arguments here needed by your application
UseShellExecute = false,
EnableRaisingEvents = true, //You are missing this
RedirectStandardOutput = true,
RedirectStandardError = true
};
process.ErrorDataReceived += process_ErrorDataReceived; //You should listen to its output error data by subscribing to this event
process.BeginErrorReadLine();
process.Start(startInfo);
process.WaitForExit(); // You may now avoid this
Then at here do anything with your received error data!
private static void process_ErrorDataReceived(object sender, System.Diagnostics.DataReceivedEventArgs e)
{
DoSomething(e.Data); // Handle your error data here
}
EDIT-(1) : Please try this solution and ping me if it works or if you need some extra help. Everyone in comments is suggesting that you must not use cmd.exe to invoke your program as it may causes debugging overhead, performance issue and you might not get error details as well.
Cheers!
I am writing a software update process on Linux. Application is .NET 5 RC1 (Sept 15 2020 release). When a certain packet is received by my application, it downloads the software update to a sub-folder then spawns off the executable to perform the software update.
Unfortunately, using Process.Start and ProcessStartInfo seems to create a process that is attached to the main process. Since the software update must stop the process in order to update it, it also gets stopped because it is a child of the process, having been spawned via Process.Start.
How do I create a detached process on Linux? On Windows I am using PInvoke and the CreateProcess API with the DETACHED_PROCESS flag, see the following:
var processInformation = new ProcessUtility.PROCESS_INFORMATION();
var startupInfo = new ProcessUtility.STARTUPINFO();
var sa = new ProcessUtility.SECURITY_ATTRIBUTES();
sa.Length = Marshal.SizeOf(sa);
CreateProcess(null, "\"" + fileName + "\" " + arguments, ref sa, ref sa, false, DETACHED_PROCESS, IntPtr.Zero, Path.GetDirectoryName(fileName), ref startupInfo, out processInformation);
Here is my code for Linux. I had read that appending & to a process on Linux creates it detached, but that does not appear to be the case.
ProcessStartInfo info = new ProcessStartInfo
{
// Linux uses " &" to detach the process
Arguments = arguments + " &",
CreateNoWindow = true,
FileName = fileName,
UseShellExecute = false,
WindowStyle = ProcessWindowStyle.Hidden,
WorkingDirectory = Path.GetDirectoryName(fileName)
};
Process.Start(info);
I was unable to get nohup or disown to work from C#. Killing the parent process always resulted in the child process being terminated as well.
I ended up using at, which can be installed via sudo apt install at. The atd service is installed and will stay running even when rebooted.
Here is the C# code that I used:
// the following assumes `sudo apt install at` has been run.
string fileName = "[your process to execute]";
string arguments = "[your command line arguments for fileName]";
string argumentsEscaped = arguments.Replace("\"", "\\\"");
string fullArgs = $"-c \"echo sudo \\\"{fileName}\\\" {argumentsEscaped} | at now\"";
ProcessStartInfo info = new()
{
Arguments = fullArgs,
CreateNoWindow = true,
FileName = "/bin/bash",
UseShellExecute = false,
WindowStyle = ProcessWindowStyle.Hidden,
WorkingDirectory = Path.GetDirectoryName(fileName)
};
using var process = Process.Start(info);
process.WaitForExit();
// make sure to check process.ExitCode == 0
For me, setsid in combination with & makes a spawned child process out-living its parent process when invoked via sh -c.
Example:
var command = $"dotnet \"PathToDll\" param1 param2";
process.StartInfo = new ProcessStartInfo
{
Arguments = $"-c \"setsid {command.Replace("\"", "\\\"")} &\"",
CreateNoWindow = true,
FileName = "/bin/sh",
};
process.Start();
This was tested on Debian and Ubuntu.
Slightly refactored version of #jjxtra solution, so it's easier to understand what's going on in the arguments.
Btw, the echo is not an example, but the way of executing at command.
string command = $"actual command to run";
string atdCommand = $#"echo \""{command}\"" | at now";
string bashCommand = $#"-c ""{atdCommand}"" ";
ProcessStartInfo psi = new ProcessStartInfo
{
FileName = "/bin/bash",
Arguments = bashCommand,
...
};
I´m trying to get the return code of a batch script, which intern calls a couple other batch and exe files. When I execute the script in a CMD window and print the errorlevel, I get the correct error code, however when I do the same in C# with a process, I always get 0 as the error code.
This is my C# code
private Process ExecuteBatchFile(string batchFile)
{
Process process = new Process
{
StartInfo = new ProcessStartInfo
{
RedirectStandardOutput = false,
RedirectStandardError = false,
UseShellExecute = true,
FileName = "CMD.exe",
WorkingDirectory = Constants.ToolsPath,
Arguments = $"/c \"{batchFile} & pause\""
}
};
process.Start();
return process;
}
batchFile = $"testScript.bat -tns {Project.TnsName} & echo Error: %errorlevel%";
The output I get for the echo is Error: 0 and the process.ExitCode value is 0
If I open a CMD window and enter
cmd.exe /c "testScript.bat -tns MYTNS & echo Error: %errorlevel% & pause"
I get the correct errorlevel value.
I´m guessing it has something to do with the batch script but I don´t understand why it works in a CMD window but not in a C# Process, especially since I´m using the same method to connect a network drive and to execute an exe file.
Edit: Code without using CMD:
private Process ExecuteBatchFile(string batchFile, string args)
{
Process process = new Process
{
StartInfo = new ProcessStartInfo
{
RedirectStandardOutput = false,
RedirectStandardError = false,
UseShellExecute = true,
FileName = batchfile,
WorkingDirectory = Constants.ToolsPath,
Arguments = args
}
};
process.Start();
return process;
}
batchFile = "testScript.bat";
args = $"-tns {Project.TnsName}";
I suspect your .bat file has a line somewhere like exit /b 1. When running this .bat file from a cmd instance, the /b flag allows the .bat file to exit without closing the parent cmd process (great for running manually!).
Unfortunately, that means that when you do Process.Start("testScript.bat");, under the hood C# uses cmd to run your bat. The .bat file exits with the /b flag, signaling to the parent cmd process it shouldn't crash and therefore it exits "successfully" (exit code 0).
You have two options:
Remove the /b flag (which will correctly return the exit code .bat > cmd > process.ExitCode) - though this will mean that executing the .bat manually via a cmd instance will terminate the parent cmd upon exit
Add to your C# code to write a wrapper .bat that will pipe all args to your bat file and properly return %exitcode%:
private int ExecuteBatWithWrapper(string batFile, string args){
string runnerPath = Path.Combine(Path.GetDirectoryName(batFile), "runner.bat");
File.WriteAllText(runnerPath, $"call {batFile} %*\nexit %ERRORLEVEL%");
Process process = Process.Start(runnerPath, args); //Alternatively construct with ProcessStartInfo
File.Delete(runnerPath);
return process.ExitCode;
}
(of course, if this is used in any sort of important environment, you should incorporate that into some sort of using statement that upon disposal deletes the runner, but this is just proof-of-concept)
See https://bytes.com/topic/c-sharp/answers/511381-system-diagnostics-process-bat-file-always-returns-exit-code-0-a#post1989782 for a similar post
I'm trying to run automated installations via CMD commands. The programs output progress and I need to capture that output and calculate total progress in a nice window. In my understanding it is impossible to elevate and redirect at the same time. I've tried...
Running cmd elevated and feeding it commands.
var proc = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "cmd.exe",
Verb = "runas",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardInput = true,
RedirectStandardError = true,
CreateNoWindow = true
}
};
proc.Start();
proc.StandardInput.WriteLine("command");
Running cmd with command as an argument
var proc = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "cmd.exe",
Arguments = "/C " + "command",
Verb = "runas",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardInput = true,
RedirectStandardError = true,
CreateNoWindow = true
}
};
proc.Start();
To no effect. Is there any tricks to this? Elevating after input/output has been captured? I need this to work. Would this be possible with psexec?
If you own the installer programs, you can rewrite them so that they do not use standard streams as a means of communication, but for example named pipes. If you don't, you can write a wrapper program that runs elevated (you run it with UseShellExecute), does not use the standard streams, but runs the installer programs and redirects their input (you run them without UseShellExecute) reporting progress to the nonelevated main program via named pipes or some other means. I hope this diagram makes it clearer:
non-elevated.exe
\
\ named pipes
\
elevated-wrapper.exe (ran with UseShellExecute=true)
\
\ redirected standard streams
\
installer-program.exe (ran with UseShellExecute=false and redirected streams)