The scenario is to start elevated cmd which calls a bat file and right after starting the process the app quits.
The code from the wpf app:
private static void CmdCommand(string filename, string workingDir)
{
var processInfo = new ProcessStartInfo(workingDir + filename);
processInfo.UseShellExecute = true;
processInfo.Verb = "runas";
processInfo.WorkingDirectory = workingDir;
processInfo.Arguments = "\""+workingDir+"\"" ;
Thread t1 = new Thread(new ThreadStart(
delegate
{
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
{
Process.Start(processInfo);
}));
}));
t1.Start();
Thread t2 = new Thread(new ThreadStart(
delegate
{
Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() =>
{
System.Windows.Application.Current.Shutdown();
}));
}));
t2.Start();
}
The code in the bat file:
set Path1=%~1
ping 127.0.0.1 -n 6 > nul
copy /Y "%Path1%dbg\lib.dll" "%Path1%lib.dll"
pause
ping 127.0.0.1 -n 6 > nul
call "%Path1%wpf.exe"
The result is that the cmd appears for a moment and disappears and no result.
This works when not elevated. But I need it to be elevated as there are no rights to copy in the working dir.
I tried recording screen to pause when the cmd appears but the recorder scips this part.
So how can I debbug it?
EDIT
Actually it is working but without one line
processInfo.Arguments = "\""+workingDir+"\"" ;
and I need to pass the arguments
EDIT 2
Apparently the problem is """ but if I don't add those I'll have a bug with paths containing spaces.
Related
Below codes have been always working in one environment.
Now we switched to a new environment (new VM), this codes do not work any more, it just hangs there until I manually close the pop-up window from cmd.exe. Obviously, there is some dead lock. But why it only happens in the new environment?? Even now it still works in the old environment.
public static void Main()
{
Process p = new Process()
{
StartInfo = new ProcessStartInfo()
{
FileName = "c:\\windows\\System32\\cmd.exe",
Arguments = "/C START \"exeName\" /D %SystemDrive%\\somefolder start.bat",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
WorkingDirectory = "c:\\windows\\System32",
CreateNoWindow = true
}
};
p.Start();
string cv_error = null;
Thread et = new Thread(() => { cv_error = p.StandardError.ReadToEnd(); });
et.Start();
string cv_out = null;
Thread ot = new Thread(() => { cv_out = p.StandardOutput.ReadToEnd(); });
ot.Start();
p.WaitForExit();
Console.WriteLine("process finish " + p.HasExited); // This line prints "process finish true" successfully
ot.Join(); // Stuck here until I manually close the popup window. But it works in another machine with this popup window stay open
et.Join();
Console.WriteLine("success" + p.ExitCode);
Console.ReadLine();
}
The start.bat looks like
#echo off
Echo "something"
mklink /D Logs "D:/logs"
Echo "Starting"
cd %~dp0\someFolder
.\ExeToInstallTools.exe arg1 // it will popup a console window showing the progress and logs
IF ERRORLEVEL 1 goto error_handler
Exit /B 0
:error_handler
Echo "Failed"
Exit /B 1
UPDATE:
I have try to use some stackTrace tool to find the difference: when my app (process=4736) start the cmd process, cmd.exe /C turns into cmd.exe /K, it does not happen in the old machine.
StackTraceToolView
I want to receive information from a device connected in USB via a C# program using ADB commands then flush the output.
Process process = new Process();
process.StartInfo.FileName = "adb.exe";
process.StartInfo.Arguments = "logcat -d -b main -v raw -s Command:I"; // get data in Log.I() with name == "Command"
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.Start();
string output;
do {
process.StandardInput.WriteLine("logcat -d -b main -v raw -s Command:I");
output = process.StandardOutput.ReadToEnd();
} while (output.Length < 1);
Console.WriteLine(output);
process.StandardInput.WriteLine("logcat -c"); // flush logcat
process.WaitForExit();
Doing that I understand that the program is constantly executing adb logcat -d -b main -v raw -s Command:I, isn't there a way to call a command one time and wait for the output?
I can do that in windows prompt using :
adb shell 'logcat -b main -v raw -s UNIQUE_TAG:I | (read -n 1 && kill -2 $((BASHPID-1)))'
but is this doable in C#?
Also the output doesn't seem to be flushed
You can't read to the end more than once; ReadToEnd ultimately means "until the pipe is closed and I've consumed everything, and no more data will ever ever arrive". ReadToEnd will also never finish until adb.exe either intentionally signals that it is done writing (and will never write again), or terminates - so this is why you aren't getting as far as flushing.
What you can do is read (a byte/character at a time, if needed) until you have, say, a single line - so if the expectation here is "one command in, one line out" then it is simple enough. If there isn't a way of knowing how many lines will come back, then you would usually use two threads, and have a dedicated reader thread that takes data from the process.StandardOutput (byte/character at a time, or line by line) and does whatever is needed with it.
This is how i used to code for such things
Here is my code Hope u understand it
//to run the all the cmd,adb.fastboot.exe command using these function
//here in startfunc pass the .exe file u want to execute,Arguments which u want to pass
//string msg = It is nothing Extra message if u want to add to your final output
private static string Execute(string Startfunc, String Arguments,string msg)
{
string text = null;
Process p = new Process();
p.StartInfo.FileName = Startfunc;
p.StartInfo.Arguments = Arguments;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
p.EnableRaisingEvents = true;
p.Start();
do
{
Application.DoEvents();
string output = p.StandardOutput.ReadToEnd();
text += output;
string err = p.StandardError.ReadToEnd();
text += err;
}
while (!p.HasExited);
text += msg;
return text;
}
//now i will call above function and make it wait until we get the result
private void normal_reboot_Click(object sender, EventArgs e)
{
txt_Log.Text = "rebooting" +Environment.NewLine;
Task t = Task.Factory.StartNew(() =>
{
txt_Log.Text=Execute("adb.exe","reboot","Done");
});
do { Application.DoEvents(); } while (!t.IsCompleted);
}
You can also look for output/error messages asyncly. So you don't need a (blocking) loop:
This example just outputs the messages, but can be extended by full methods
p.OutputDataReceived+= (s, e) => Console.WriteLine(e.Data);
p.BeginOutputReadLine();//This is important, otherwise the event won't be fired
p.ErrorDataReceived += (s, e) => Console.WriteLine(e.Data);
p.BeginErrorReadLine();
This should print every line received by adb
Working code below, we don't call adb.exe directly but through cmd.exe with a command waiting for any information log coming from logcat with the title "Command" : adb shell logcat -b main -v raw -s Command:I
private Process listeningProc;
public someClass(){
listeningProc = new Process();
listeningProc.StartInfo.FileName = "cmd.exe";
listeningProc.StartInfo.UseShellExecute = false;
listeningProc.StartInfo.RedirectStandardOutput = true;
listeningProc.StartInfo.RedirectStandardInput = true;
listeningProc.Start();
listeningProc.OutputDataReceived += new DataReceivedEventHandler(
(s, e) => {
if (e.Data == "SPECIFIC_COMMAND") {
// do something
}
}
);
listeningProc.BeginOutputReadLine();
}
private void ListenToCommands(){
listeningProc.StandardInput.WriteLine("adb shell logcat -b main -v raw -s Command:I"); //Listening for Info Logs titled "Command"
while (true) { }
}
To flush logcat or send any command from the PC through ADB, I open another process and send the commands from there.
Edit:
You might want to add System.Threading.Thread.Sleep(2000); //pause for 2 seconds after each commands sent from the PC through ADB otherwise the device sometimes doesn't execute all commands when sent successively too quickly
I have a method that running an EXE file using cmd. This EXE file getting an exception during its initializing, then retrying and starting a process on my system. I cannot edit the EXE file to change its behavior.
when running the code, after the exception, the command line window closed immediately.
How can I keep the cmd window open till the retry?
Thanks,
Tal.
public void RunJob(Process cmdProcess)
{
cmdProcess.StartInfo.FileName = "cmd.exe";
cmdProcess.StartInfo.UseShellExecute = false;
cmdProcess.StartInfo.CreateNoWindow = true;
cmdProcess.StartInfo.RedirectStandardOutput = true;
cmdProcess.OutputDataReceived += new DataReceivedEventHandler(form.SortOutputHandler);
cmdProcess.StartInfo.RedirectStandardError = true;
cmdProcess.ErrorDataReceived += new DataReceivedEventHandler(form.SortOutputHandler);
cmdProcess.StartInfo.RedirectStandardInput = true;
cmdProcess.StartInfo.Arguments = string.Format("/k cd \"{0}\\BinFolder\"", form.txtLocalRepo);
cmdProcess.StartInfo.Verb = "runas";
cmdProcess.Start();
StreamWriter cmdStreamWriter = cmdProcess.StandardInput;
cmdProcess.BeginOutputReadLine();
cmdStreamWriter.WriteLine("START testing.exe");
}
If this is a Console Application, then you might consider using the new Async Main method. You could combine this with something like TaskCompletionSource so you can flag your app as complete when you're ready, and have it wait until you flag the task as complete.
This question already has answers here:
Elevating process privilege programmatically?
(6 answers)
Execute several elevated commands in shell (or similar)
(1 answer)
How to run multiple lines in cmd as administrator using C#?
(2 answers)
How do I make a console app always run as an administrator?
(3 answers)
Closed 5 years ago.
I have referenced some other topics and it comes up the following coding, yet it doesn't work. I allow it creates command window and use "/k" argument keeping the window opened so that I can trace its output.
However, I can see the warning from the window that the command needs admin rights to execute. How can I execute it in admin rights?
public void ClearArpCache () {
ProcessStartInfo processStartInfo = new ProcessStartInfo("cmd.exe", "/user:Administrator \" cmd /k arp -d \""); // same as "netsh interface ip delete arpcache"
processStartInfo.RedirectStandardOutput = true;
//processStartInfo.CreateNoWindow = true;
//processStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
processStartInfo.UseShellExecute = false;
processStartInfo.StandardOutputEncoding = Encoding.Default;
processStartInfo.Verb = "runas";
Process.Start (processStartInfo);
UnityEngine.Debug.Log ("ARP table cache cleared.");
}
EDITED:
Try changing from "cmd.exe" to "runas.exe"
public void ClearArpCache () {
ProcessStartInfo processStartInfo = new ProcessStartInfo("runas.exe", "/user:Administrator \" cmd /k arp -d \""); // same as "netsh interface ip delete arpcache"
processStartInfo.RedirectStandardOutput = true;
//processStartInfo.CreateNoWindow = true;
//processStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
processStartInfo.UseShellExecute = false;
processStartInfo.StandardOutputEncoding = Encoding.Default;
Process.Start (processStartInfo);
UnityEngine.Debug.Log ("ARP table cache cleared.");
}
Just clarified why it failed using "runas". In order to use "runas" in Verb, UseShellExecute must be firstly turned true.
When executing following function, it will pop a window asking for admin rights, just click 'Yes' to begin the execution. Though it is beyond the scope, if possible I want to skip the pop-up window as well.
public void ClearArpCache () {
ProcessStartInfo processStartInfo = new ProcessStartInfo("cmd.exe", "/k arp -d"); // same as "netsh interface ip delete arpcache"
processStartInfo.CreateNoWindow = true;
processStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
processStartInfo.UseShellExecute = true;
processStartInfo.Verb = "runas";
Process.Start (processStartInfo);
UnityEngine.Debug.Log ("ARP table cache cleared.");
}
I have a batch script that launches the mariadb in console mode, so it looks likes this:
run-mariadb.bat
#echo off
cd /D mariadb-10.1.14-win32\bin && mysqld.exe --console
#pause
Using a C# WinForm app, I'd like to launch this batch script and get all output onto a RichTextBox.
This is the code I have so far:
// Configure db server process
Process dbServer = new Process();
dbServer.StartInfo.FileName = "cmd";
dbServer.StartInfo.Arguments = "/c start /wait run-mariadb.bat";
dbServer.StartInfo.CreateNoWindow = true;
dbServer.StartInfo.UseShellExecute = false;
dbServer.StartInfo.RedirectStandardOutput = true;
dbServer.StartInfo.RedirectStandardError = true;
dbServer.OutputDataReceived += new DataReceivedEventHandler(HandleDBServerOutput);
dbServer.ErrorDataReceived += new DataReceivedEventHandler(HandleDBServerOutput);
These are my helper methods:
string FormatOutput(string message)
{
return string.Format("[ {0} ] : {1}",
DateTime.Now.ToShortTimeString(),
message);
}
void HandleDBServerOutput(object sender, DataReceivedEventArgs e)
{
this.BeginInvoke(new MethodInvoker(() =>
{
if (!string.IsNullOrEmpty(e.Data))
dbLogsRTB.AppendText(FormatOutput(e.Data));
}));
}
I start the process like this:
// Start database server
dbServer.Start();
dbServer.BeginOutputReadLine();
dbServer.WaitForExit();
When the above executes, a new command prompt window is created and the mariadb is running in there. Any ideas why dbServer.StartInfo.CreateNoWindow = true; is being ignored?
The help for start starts off with:
Starts a separate window to run a specified program or command.
In other words, by running start as part of your command, you'll launch a new window. You can verify this by running start /wait run-mariadb.bat from a command prompt. It launches a new window to run the command.
Consider changing your command to be simply:
dbServer.StartInfo.Arguments = "/c run-mariadb.bat";
To just run the command without starting a new console process.