How to run the exe from a different folder using ProcessStartInfo? - c#

I have a program under c:\windows\system32\csexport.exe . If I open a cmd window, and run the command, it works fine and generate an output file under c:\windows\system32\csexport
However, if I use File.Exists(file), it will always return false because of some security reason. So, I open a cmd window, then go to c:\tmp folder, then run the csexport.exe with full path, then it will output the file in c:\tmp .
So I try to do this in C#. Here is the code
public ProcessResult RunProcess(string fileName, string arguments, string workingDirectory)
{
var processResult = new ProcessResult();
// prep process
var psi = new ProcessStartInfo(fileName, arguments)
{
UseShellExecute = true,
RedirectStandardOutput = true,
RedirectStandardError = true
, WorkingDirectory = workingDirectory
};
// start process
using (var process = new System.Diagnostics.Process())
{ // pass process data
process.StartInfo = psi;
process.Start();
processResult.Data = process.StandardOutput.ReadToEnd();
process.WaitForExit();
processResult.ExitCode = process.ExitCode;
}
return processResult;
}
But, how can I set it to run from c:\tmp ?
I did more test, and had weird result:
I set UseShellExecute = false, and then set workingDirectory = "C:\tmp", the filename with full path. when run it, it shows this error:
The following exception occurred: System.InvalidOperationException: The Process object must have the UseShellExecute property set to false in order to redirect IO streams.
at System.Diagnostics.Process.StartWithShellExecuteEx(ProcessStartInfo startInfo)
at System.Diagnostics.Process.Start()
at ENow.Os.Utilities.ProcessWrapper.RunProcess(String fileName, String arguments, String workingDirectory)
...
But, UseShellExecute is set as false already.

Related

Read output from console dialog box

I want to get output text from the external process that runs the application and this application displays something that looks like a console dialog box like in the screenshot below:
I tried to get output via StandardOutput:
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = pathToExeFile,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
},
};
process.Start();
var output = process.StandardOutput.ReadToEnd();
var err = process.StandardError.ReadToEnd();
process.WaitForExit();
But in this case both output and err variables have null values only. I assume that this is because the output from the app is not available via stdout but from something which looks like a dialogbox/messagebox. Is there any way to get this output?

ProcessStartInfo start "cmd.exe" to run "nvm" commands to install node versions pop up a program association error

I am trying to automate the machine setup using net core 2.0 with a console application, and I need to run some nvm commands to configure node versions.
I am trying to run a .bat file with the nvm commands that I need, but I am getting the following error:
This file does not have a program associated with it for performing this action. Please install a program or, if one is already installed, create an association in the Default Programs control panel.
If I execute the .bat file directly from cmd it works ok, but when my console app run it I get this error.
The 'file.bat' commands are:
nvm version
nvm install 6.11.4
nvm use 6.11.4
nvm list
npm --version
My csharp function to run the command:
public static int ExecuteCommand()
{
int exitCode;
ProcessStartInfo processInfo;
Process process;
processInfo = new ProcessStartInfo("cmd.exe", $"/C file.bat")
{
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardError = true,
RedirectStandardOutput = true
};
process = Process.Start(processInfo);
process.OutputDataReceived += (s, e) =>
{
Console.ForegroundColor = ConsoleColor.DarkGray;
Console.WriteLine("cmd >" + e.Data);
Console.ResetColor();
};
process.BeginOutputReadLine();
process.ErrorDataReceived += (s, e) =>
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(e.Data);
Console.ResetColor();
};
process.BeginErrorReadLine();
process.WaitForExit();
exitCode = process.ExitCode;
Console.WriteLine("ExitCode: " + exitCode.ToString(), "ExecuteCommand");
process.Close();
return exitCode;
}
My expectation is to have this working, because after that I will need to run several other commands, like npm install, gulp install, etc.
Any idea of what could be happening?
Based purely on testing, if you change this section:
processInfo = new ProcessStartInfo("cmd.exe", $"/C file.bat")
{
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardError = true,
RedirectStandardOutput = true
};
to not use the constructor arguments and instead manually set parameters like:
processInfo = new ProcessStartInfo()
{
FileName = "cmd.exe",
Arguments = $"/C file.bat",
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardError = true,
RedirectStandardOutput = true
};
should do the trick. Not sure on why, since from github code on ProcessStartInfo the constructor merely receives arguments and stores them on respective properties (FileName and Arguments).

Start process with IO stream redirection

How to start process and run command like this:
mysql -u root --password="some-password" < "some-file.sql"
Is it possible to do with process.Start()?
I need cross-platform solution (we cannot use cmd.exe).
Yes, this is possible through the System.Diagnostics.Process class. You need to set RedirectStandardInput to true, after which you can write the content of a file redirect the standard input of a process, and write the contents of the file to the Process.StandardInput (which is a StreamWriter)
This should get you started:
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "mysql.exe", // assumes mysql.exe is in PATH
Arguments = "-u root --password=\"some-password\"",
RedirectStandardInput = true,
UseShellExecute = false
},
};
process.Start();
process.StandardInput.Write(File.ReadAllText("some-file.sql"));
Update: this is pretty well documented [here](
https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.processstartinfo.redirectstandardinput)

C# Read text (output?) from console that runs as a Process

I'm trying to check if username and password for git repository is valid. In console I run:
git clone http://username:password#server/test.git
And I get:
fatal: Authentication failed for ...
So now I know username and password are not valid. I'm trying to run this command as a process:
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "git.exe",
RedirectStandardInput = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
WorkingDirectory = "some_directory"
CreateNoWindow = true,
Arguments = "git clone http://username:password#server/test.git"
},
};
process.Start();
I'd like to access the result of this command. Both process.StandardError and process.StandardOutput are equals string.Empty. Is there any way to read the result?
Normally you should read the exit code of the process.
process.ExitCode
If the process failed, the return value should be non 0. Of course you can only retrieve the exit code after the process completes.
So:
if (process.ExitCode != 0)
//error
Please note: I haven't tested it but it is standard convention.
To read the output, one normally uses:
string output = process.StandardOutput.ReadToEnd();
string err = process.StandardError.ReadToEnd();
Console.WriteLine(output);
Console.WriteLine(err);
What you really need is your standard output, which can be accessed as below:
string stdout = p.StandardOutput.ReadToEnd();
and use p.WaitForExit(); right after because sometimes it takes a while to give error message.

Elevating privileges doesn't work with UseShellExecute=false

I want to start a child process (indeed the same, console app) with elevated privileges but with hidden window.
I do next:
var info = new ProcessStartInfo(Assembly.GetEntryAssembly().Location)
{
UseShellExecute = true, // !
Verb = "runas",
};
var process = new Process
{
StartInfo = info
};
process.Start();
and this works:
var identity = new WindowsPrincipal(WindowsIdentity.GetCurrent());
identity.IsInRole(WindowsBuiltInRole.Administrator); // returns true
But UseShellExecute = true creates a new window and I also I can't redirect output.
So when I do next:
var info = new ProcessStartInfo(Assembly.GetEntryAssembly().Location)
{
RedirectStandardError = true,
RedirectStandardOutput = true,
UseShellExecute = false, // !
Verb = "runas"
};
var process = new Process
{
EnableRaisingEvents = true,
StartInfo = info
};
DataReceivedEventHandler actionWrite = (sender, e) =>
{
Console.WriteLine(e.Data);
};
process.ErrorDataReceived += actionWrite;
process.OutputDataReceived += actionWrite;
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
This doesn't elevate privileges and code above returns false. Why??
ProcessStartInfo.Verb will only have an effect if the process is started by ShellExecuteEx(). Which requires UseShellExecute = true. Redirecting I/O and hiding the window can only work if the process is started by CreateProcess(). Which requires UseShellExecute = false.
Well, that's why it doesn't work. Not sure if forbidding to start a hidden process that bypasses UAC was intentional. Probably. Very probably.
Check this Q+A for the manifest you need to display the UAC elevation prompt.
In my case, it was ok to get the outputs once the elevated child process is done. Here's the solution I came up. It uses a temporary file :
var output = Path.GetTempFileName();
var process = Process.Start(new ProcessStartInfo
{
FileName = "cmd",
Arguments = "/c echo I'm an admin > " + output, // redirect to temp file
Verb = "runas", // UAC prompt
UseShellExecute = true,
});
process.WaitForExit();
string res = File.ReadAllText(output);
// do something with the output
File.Delete(output);
Check this answer.
This seems to provide a workaround. But I recommend to try other methods like Named Pipes when you have access to source code of the child process.

Categories