I am trying to run logman.exe for a elevated CMD, for this below code I tried,
var proc = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = #"C:\Windows\System32\cmd.exe",
Arguments = "cmd /k logman.exe PerfCounterCustom | findstr \"Root\"",
Verb = "runas",
UseShellExecute = true,
}
};
try
{
proc.Start();
while (!proc.StandardOutput.EndOfStream)
{
string line = proc.StandardOutput.ReadLine();
}
Console.WriteLine("Successfully elevated!");
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
And it's giving error output like,
System.InvalidOperationException: StandardOut has not been redirected or the process hasn't started yet.
at System.Diagnostics.Process.get_StandardOutput()
2 Questions,
when I am running application exe, it's showing 2 CMD window, the 1st one showing error and 2nd one showing result for argument "cmd /k logman.exe PerfCounterCustom | findstr \"Root\"" [Root Path]
how to disable showing both window?
Why I am getting this error?
To your 1st Question: In the ProcessStartInfo set WindowStyle to ProcessWindowStyle.Hidden
An alternative solution to read the output of the command is to write the output to a text file. Therefore you have to add >> "[Name or Path of file].txt" to the end of your command. Then just read the file from C# e.g. with File.ReadAllLines.
Two things to consider here:
If you do that often at Runtime and the command delivers huge amounts of text don't write it to an SSD.
Please check that the file is empty / not existing before, because Windows just appends the output to the end of the file. If you run that in multiple threads use a thread identifier in the file name.
You have to set RedirectStandardOutput of the ProcessStartInfo to true and you have to run proc.WaitForExit() before reading the output.
Please note that this solution causes incompatibilities with running the process as administrator via runas.
Related
I was trying to start a exe with arguments by Process.Start.
My first try is using Process.Start("Path/of/the/exe", "arguments of exe").
Here's my code snippets:
Process.Start(#"D:\Program Files\ITASCA\UDEC700\Exe64\udecConsole2017.exe", #"call 'D:\Work\202205\20220525\tunnel-for-cmd.txt'");
However the initialization of this exe is a bit slow, and the result is, I can only start the exe but the failed passing arguments. The following is the screenshot:
which is exactly the same result that starts without arguments.
By referencing this post C# - Making a Process.Start wait until the process has start-up, I changed my code as follows:
var process = Process.Start(#"D:\Program Files\ITASCA\UDEC700\Exe64\udecConsole2017.exe", #"call 'D:\Work\202205\20220525\tunnel-for-cmd.txt'");
while (string.IsNullOrEmpty(process.MainWindowTitle))
{
System.Threading.Thread.Sleep(100);
process.Refresh();
}
however these changes does not work.
I think my goal is to wait until exe completely started and then run it with arguments, but I dont know how to implement this.
=====================================================
New additions:
if I type in arguments call 'D:\Work\202205\20220525\tunnel-for-cmd.txt' in this started process, I will get my result:
SO I think the input arguments should be OK?
=======================================
new addition 2:
code for checking outputstream end
It appears this is a console application and you are typing in the console after it starts. This typing is not arguments: Arguments are provided only when starting a new process and never change.
What you are doing is providing something to the standard input of the program. Console programs have three streams the OS provides (one input and two output). You need to redirect these to detect when the program has started and to provide the proper input.
Something like this:
// Start with stdio redirected
var psi = new ProcessStartInfo()
{
UseShellExecute = false,
FileName = #"your exe",
RedirectStandardInput = true,
RedirectStandardOutput = true,
};
var p = System.Diagnostics.Process.Start(psi);
// Read until the udec> prompt
while(true)
{
var line = p.StandardOutput.ReadLine();
if(line.StartsWith("udec>"))
break;
}
// Write the command
p.StandardInput.WriteLine(#"call 'D:\Work\202205\20220525\tunnel-for-cmd.txt'");
// Read the result
p.StandardOutput.ReadToEnd();
When i try to run this function it keep crashing when the process starts.
public static void MapDestinationToSource (string destination, string source)
{
System.Diagnostics.Process proc = new System.Diagnostics.Process ();
// set the file to execute
proc.StartInfo.FileName = "mklink";
proc.StartInfo.Arguments = $"/D \"{source}\" \"{destination}\"";
// Redirect the output stream of the child process.
proc.StartInfo.UseShellExecute = true;
//proc.StartInfo.RedirectStandardOutput = true;
// start the process
proc.Start ();
// Do not wait for the child process to exit before
// reading to the end of its redirected stream.
// p.WaitForExit();
// Read the output stream first and then wait.
string output = proc.StandardOutput.ReadToEnd ();
proc.WaitForExit ();
}
Exception:
System.ComponentModel.Win32Exception occurred
HResult=0x80004005
Message=The system cannot find the file specified
Source=System
StackTrace:
at System.Diagnostics.Process.StartWithShellExecuteEx(ProcessStartInfo startInfo)
at System.Diagnostics.Process.Start()
at GameCloud.StorageLibrary.MapDestinationToSource(String destination, String source) in D:\Development\code\sample\server\ClientAgent\ClientAgent\StorageLibrary.cs:line 130
at GameCloud.Program.Main(String[] args) in D:\Development\code\sample\server\ClientAgent\ClientAgent\Program.cs:line 135
When i execute the command on command line, it works. But it does not when in the code. I already set the security policies to allow the current user to execute the mklink command without elevated access.
If you are trying to execute executable programs (bob.exe) check out my striked out answer below.
Since you are trying to run mklink which is built-in to cmd then you need to use:
proc.StartInfo.FileName = "cmd.exe";
proc.StartInfo.Arguments = $"/C mklink /D \"{source}\" \"{destination}\"";
The docs state:
When UseShellExecute is true, the WorkingDirectory property specifies
the location of the executable. If WorkingDirectory is an empty
string, it is assumed that the current directory contains the
executable.
and then:
When UseShellExecute is false, the FileName property can be either a
fully qualified path to the executable, or a simple executable name
that the system will attempt to find within folders specified by the
PATH environment variable.
As such, you need to either set UseShellExecute to false (so that your PATH is used to find the executable) or set WorkingDirectory to the folder that contains the executable.
I found some command line arguments to run generate the CSSLint report in xml format. It is working fine while running through command prompt.
Arguments:
csslint --format=csslint-xml "{SourceDir}\bootstrap.css" > "C:\temp\csslint.xml"
I want to execute it through C# application. I tried the below code.
Process process = new Process()
{
StartInfo =
{
FileName = "cmd.exe",
Arguments = "csslint --format=csslint-xml " + #"""{SourceDir}\bootstrap.css""" + #" > ""C:\Temp\CssLint.xml""",
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true,
}
};
process.Start();
process.WaitForExit();
But it is not working. Can i have a solution or idea for this issue?
Also is there any way to generate the CSSLint report for the specified directory? I want to give the directory path instead of file name.
You need to add /Kor /C to cmd to execute a process passed as a parameter, thus:
Arguments = "/C csslint --format=csslint-xml " + #"""{SourceDir}\bootstrap.css""" + #" > ""C:\Temp\CssLint.xml""",
From the documentation:
Options
/C Run Command and then terminate
/K Run Command and then return to the CMD prompt.
This is useful for testing, to examine variables
One caveat... the piping (the > "C:\temp\csslint.xml" part of your command line) is not an argument, it's a redirection.
If you are redirecting your stdout (the RedirectStandardOutput = true) from your app, you can capture it directly from C#, no need to pipe it to a file like you are trying to do: you'd need to handle the Process.OutputDataReceived event between your Start and WaitForExit calls, or read from the Process.StandardOutput stream).
As for your second question, the csslint CLI allows passing in a directory instead of a file
Am a Newbie in C# and I have 3 commands(command2, command3 and command4) I need to execute in the elevated command prompt and I will also like to view the execution process as it happens. Currently, the problem is that the code below just opens the elevated command prompt and without executing the commands. I also seek better interpretations of the lines if wrong.
My code and Interpretation/Understanding of each line based on reviews of similar cases: ConsoleApp1
class Program
{
static void Main(string[] args)
{
string command2 = #"netsh wlan";
string command3 = #" set hostednetwork mode=true ssid=egghead key=beanhead keyusage=persistent";
string command4 = #" start hostednetwork";
string maincomm = command2.Replace(#"\", #"\\") + " " + command3.Replace(#"\", #"\\") ; //I merged commands 2 and 3
ProcessStartInfo newstartInfo = new ProcessStartInfo();
newstartInfo.FileName = "cmd"; //Intend to open cmd. without this the newProcess hits an error saying - Cannot run process without a filename.
newstartInfo.Verb = "runas"; //Opens cmd in elevated mode
newstartInfo.Arguments = maincomm; //I intend to pass in the merged commands.
newstartInfo.UseShellExecute = true; //
newstartInfo.CreateNoWindow = true; // I intend to see the cmd window
Process newProcess = new Process(); //
newProcess.StartInfo = newstartInfo; //Assigns my newstartInfo to the process object that will execute
newProcess.Start(); // Begin process and Execute newstartInfo
newProcess.StartInfo.Arguments = command4; //I intend to overwrite the initial command argument hereby passing the another command to execute.
newProcess.WaitForExit(); //
}
}
This is what I did to overcome the challenge and It gave me exactly what I wanted. I modified my code to use the System.IO to write directly to the elevated command prompt.
ProcessStartInfo newstartInfo = new ProcessStartInfo();
newstartInfo.FileName = "cmd";
newstartInfo.Verb = "runas";
newstartInfo.RedirectStandardInput = true;
newstartInfo.UseShellExecute = false; //The Process object must have the UseShellExecute property set to false in order to redirect IO streams.
Process newProcess = new Process();
newProcess.StartInfo = newstartInfo;
newProcess.Start();
StreamWriter write = newProcess.StandardInput ; //Using the Streamwriter to write to the elevated command prompt.
write.WriteLine(maincomm); //First command executes in elevated command prompt
write.WriteLine(command4); //Second command executes and Everything works fine
newProcess.WaitForExit();
Referrence: http://msdn.microsoft.com/en-us/library/system.diagnostics.process.standardinput(v=vs.110).aspx
http://msdn.microsoft.com/en-us/library/system.diagnostics.processstartinfo(v=vs.110).aspx
I think an understanding of some properties of the ProcessStartInfo might clear things.
The verb - Gets or sets the verb to use when opening the application or document specified by the FileName property.,
+The UseShellExecute - Gets or sets a value indicating whether to use the operating system shell to start the process.
+The FileName - Gets or sets the application or document to start MSDN Docs
When you use the operating system shell to start processes, you can start any document (which is any registered file type associated with an executable that has a default open action) and perform operations on the file, such as printing, by using the Process object. When UseShellExecute is false, you can start only executables by using the Process object Documentation from MSDN.
In my case, cmd is an executable. the verb property is some thing that answers the question "How should my I run my FileName(for executables e.g cmd or any application)?" for which I answered - "runas" i.e run as administrator. When the FileName is a document (e.g `someFile.txt), the verb answers the question "What should I do with the file for which answer(verb) could be -"Edit","print" etc. also?"
use true if the shell should be used when starting the process; false if the process should be created directly from the executable file. The default is true MSDN Docs - UserShellInfo.
Another thing worth noting is knowing what you are trying to achieve. In my case, I want to be able to run commands via an executable(cmd prompt) with the same process - i.e starting the cmd as a process I can keep track of.
I have a batch file like this
#echo off
xcopy /e %1 %2
I have my C# code as follows:
string MyBatchFile = #"C:\Program Files (x86)\MybatchFile.bat";
string _sourcePath = #"C:\FolderToCopy";
string _tempTargetPath = #"C:\TargetFolder\";
var process = new Process {
StartInfo = {
Arguments = string.Format("{0} {1}",
_sourcePath,
_tempTargetPath)
}
};
process.StartInfo.FileName = MyBatchFile;
bool b = process.Start();
I expect this to copy the source files to target location. But nothing happens. My console window also does not stay for enough time so that I can see the error. Can anyone guide to achieve this. I am new in batch files processing.
Edit
By adding a pause in the end of batch file. Able to reproduce error. Getting error as
Files not found - Program
Running batch file directly does work fine. Just now noticed......when source path has any spaces....I am getting error
What about quoting argument?
Arguments = String.Format("\"{0}\" \"{1}\"", _sourcePath, _tempTargetPath) …
.bat file is a text file, in order to execute it, you should start cmd process.
Start it like this:
System.Diagnostics.Process.Start("cmd.exe", "/c yourbatch.bat");
Additional arguments may follow. Try this without c#, in a cmd window, or Run dialog.
try
string MyBatchFile = #"C:\MybatchFile.bat";
string _sourcePath = #"C:\FolderToCopy\*.*";
string _tempTargetPath = #"C:\TargetFolder\";
i.e. add *.* to the source path
and add a 3rd line pause to the batch file
#echo off
copy /e %1 %2
pause