I have two weird problems running an app on Windows 8 (which works fine on Windows 7)
I'm running external "a.exe" app.
First issue is that when I run "a.exe" using Process - I don't get any output.
If I run a batch file which executes "a.exe" and write output to a file - there is an output.
The second issue is that in both cases (Batch and Process) "a.exe" fails. But from command line it works.
Here is the code:
proc = new Process();
proc.StartInfo.FileName = Path;
proc.StartInfo.Arguments = args;
proc.StartInfo.WorkingDirectory = GetDirectoryName(Path);
proc.StartInfo.CreateNoWindow = true;
proc.StartInfo.RedirectStandardError = true;
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.Verb = "runas administrator";
proc.OutputDataReceived += (sendingProcess, line) =>
{
string s = line.Data;
if (!string.IsNullOrWhiteSpace(s))
{
_sbStdout.AppendLine(line.Data);
}
};
proc.ErrorDataReceived += (sendingProcess, line) =>
{
string s = line.Data;
if (!string.IsNullOrWhiteSpace(s))
{
_sbStderr.AppendLine(line.Data);
}
};
proc.Start();
proc.BeginOutputReadLine();
proc.BeginErrorReadLine();
// Wait for exit or timeout
bool res = true;
if (timeout <= 0 || timeout == null)
proc.WaitForExit();
else
res = proc.WaitForExit((int)timeout);
ExitCode = proc.ExitCode;
What is wrong?
Related
i am trying to call and collect the data returned by the CMD command query user.
Calling this via cmd from the Windows-startbar gives me a normal result.
Calling this via this c# function give 0 output.
public void callQueryUser()
{
ProcessStartInfo psi = new ProcessStartInfo("cmd.exe");
Process p = Process.Start(psi);
string cmd = string.Format(#"/c query user");
psi.Arguments = cmd;
psi.RedirectStandardOutput = true;
psi.UseShellExecute = false;
psi.CreateNoWindow = true;
psi.WaitForExit();
string result = p.StandardOutput.ReadToEnd();
MessageBox.Show(result);
}
I checked and the Window says command cant befound... I also check if they are both the same cmd.exe and thats also true. It seems like calling the cmd.exe via C# makes somewhat of a differences.
Anyone any idea what i could check next ?
It's not necessary to use cmd to retrieve the information you want using Process. However, if your OS is 64-bit, your program is running as 32-bit, and you're trying to access %windir%\System32\query.exe, you need to use %windir%\Sysnative\query.exe instead.
Try the following:
Option 1:
public void callQueryUser()
{
string queryPath = string.Empty;
//use 'Sysnative' to access 64-bit files (in System32) if program is running as 32-bit process
//use 'SysWow64' to access 32-bit files on 64-bit OS
if (Environment.Is64BitOperatingSystem && !Environment.Is64BitProcess)
queryPath = System.IO.Path.Combine(Environment.GetEnvironmentVariable("windir"), "Sysnative", "query.exe");
else
queryPath = System.IO.Path.Combine(Environment.GetEnvironmentVariable("windir"), "System32", "query.exe");
Debug.WriteLine("queryPath: " + queryPath);
// create new instance
ProcessStartInfo startInfo = new ProcessStartInfo(queryPath);
startInfo.Arguments = "user"; //arguments
startInfo.CreateNoWindow = true; //don't create a window
startInfo.RedirectStandardError = true; //redirect standard error
startInfo.RedirectStandardOutput = true; //redirect standard output
startInfo.UseShellExecute = false; //if true, uses 'ShellExecute'; if false, uses 'CreateProcess'
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
//create new instance
using (Process p = new Process { StartInfo = startInfo, EnableRaisingEvents = true })
{
//subscribe to event and add event handler code
p.ErrorDataReceived += (sender, e) =>
{
if (!String.IsNullOrEmpty(e.Data))
{
//ToDo: add desired code
Debug.WriteLine("Error: " + e.Data);
}
};
//subscribe to event and add event handler code
p.OutputDataReceived += (sender, e) =>
{
if (!String.IsNullOrEmpty(e.Data))
{
//ToDo: add desired code
Debug.WriteLine("Output: " + e.Data);
string result = e.Data;
MessageBox.Show(result);
}
};
p.Start(); //start
p.BeginErrorReadLine(); //begin async reading for standard error
p.BeginOutputReadLine(); //begin async reading for standard output
//waits until the process is finished before continuing
p.WaitForExit();
}
}
Option 2:
public void callQueryUser()
{
string queryPath = string.Empty;
//environment variable windir has the same value as SystemRoot
//use 'Sysnative' to access 64-bit files (in System32) if program is running as 32-bit process
//use 'SysWow64' to access 32-bit files on 64-bit OS
if (Environment.Is64BitOperatingSystem && !Environment.Is64BitProcess)
queryPath = System.IO.Path.Combine(Environment.GetEnvironmentVariable("windir"), "Sysnative", "query.exe");
else
queryPath = System.IO.Path.Combine(Environment.GetEnvironmentVariable("windir"), "System32", "query.exe");
Debug.WriteLine("queryPath: " + queryPath);
// create new instance
ProcessStartInfo startInfo = new ProcessStartInfo(queryPath);
startInfo.Arguments = "user"; //arguments
startInfo.CreateNoWindow = true; //don't create a window
startInfo.RedirectStandardError = true; //redirect standard error
startInfo.RedirectStandardOutput = true; //redirect standard output
startInfo.UseShellExecute = false; //if true, uses 'ShellExecute'; if false, uses 'CreateProcess'
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
//create new instance
using (Process p = new Process { StartInfo = startInfo, EnableRaisingEvents = true })
{
p.Start(); //start
//waits until the process is finished before continuing
p.WaitForExit();
string result = p.StandardOutput.ReadToEnd();
MessageBox.Show(result);
}
}
Resources:
Accessing files from System32 directory using 32 bit application on 64 bit machine
Process Class
ProcessStartInfo Class
Environment.GetEnvironmentVariable Method
I'm trying to start a process to excute a shell script with C# in Unity3D on MacOS, and I write the code below.
[MenuItem("Test/Shell")]
public static void TestShell()
{
Process proc = new Process();
proc.StartInfo.FileName = "/bin/bash";
proc.StartInfo.WorkingDirectory = Application.dataPath;
proc.StartInfo.Arguments = "t.sh";
proc.StartInfo.CreateNoWindow = false;
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.RedirectStandardOutput = true;
proc.OutputDataReceived += new DataReceivedEventHandler((sender, e) =>
{
if (!string.IsNullOrEmpty(e.Data))
{
Debug.Log(e.Data);
}
});
proc.Start();
proc.BeginOutputReadLine();
proc.WaitForExit();
proc.Close();
}
Shell script::
echo "1"
sleep 2s
open ./
echo "4"
When I run this code, Unity3D get stuck until the shell script excute complete.
I tried to uncommit "proc.WaitForExit();", it did open the finder and not stuck anymore, but output nothing.
So how can I start a process in Unity3D and get the output of the shell script immediately?
As said simply run the entire thing in a separate thread:
[MenuItem("Test/Shell")]
public static void TestShell()
{
var thread = new Thread(TestShellThread);
thread.Start();
}
private static void TestShellThread ()
{
Process proc = new Process();
proc.StartInfo.FileName = "/bin/bash";
proc.StartInfo.WorkingDirectory = Application.dataPath;
proc.StartInfo.Arguments = "t.sh";
proc.StartInfo.CreateNoWindow = false;
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.RedirectStandardOutput = true;
proc.OutputDataReceived += new DataReceivedEventHandler((sender, e) =>
{
if (!string.IsNullOrEmpty(e.Data))
{
Debug.Log(e.Data);
}
});
proc.Start();
proc.BeginOutputReadLine();
proc.WaitForExit();
proc.Close();
}
Note though in general: If you want to use the result in any Unity API related things besides logging you will need to dispatch them back into the main thread!
I am getting wired problem with a process that is started by my c# application,in my application i start a process that runs a ffmpeg command which executes well but when in any how if i closes my application then that process still continues to executes.
private static bool RunRecordProcess(string command)
{
Process process = new Process();
try
{
ProcessStartInfo processStartInfo = new
ProcessStartInfo(Application.StartupPath +
FFMPEG_EXE_FILE_PATH);
processStartInfo.UseShellExecute = false;
processStartInfo.Arguments = "-hide_banner -loglevel 8 " +
command;
processStartInfo.RedirectStandardError = true;
processStartInfo.RedirectStandardOutput = true;
processStartInfo.RedirectStandardInput = true;
processStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
processStartInfo.CreateNoWindow = true;
processStartInfo.Verb = "";
process.EnableRaisingEvents = true;
process.StartInfo = processStartInfo;
process.Start();
string error = process.StandardError.ReadToEnd();
if (error.Length > 0)
{
if (!process.HasExited)
{
process.Kill();
//throw new Exception("Failure some error occured");
}
}
process.WaitForExit();
}
catch(Exception ex)
{
process.Kill();
ExceptionHandler.handleException(ex);
return false;
}
return true;
}
What i want is when my application exits my process that is started by the application will automatically exit so that no useless process will be using my system Cpu.
Thankyou!
You should create job object (CreateJobObject function in kernel32). There is more description:
https://tulisanlain.blogspot.com/2016/08/kill-child-process-when-parent-exit-c.html
I'm trying to use "multi-step" command in a c# script, for example the command "net user usrname *" contains 3 steps to enter a password and then validate, i don't know if it is possible to send extra arguments while the Process is running
My code:
Process p = new Process();
p.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.Arguments = "/C " + command;
p.StartInfo.WorkingDirectory = startupFolder;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.UseShellExecute = false;
p.Start();
string output = p.StandardOutput.ReadToEnd();
string error = p.StandardError.ReadToEnd();
You would concatenate each command with "&". For example, "cmd /k echo Test 1 & echo test 2".
Edit:
I created a remote control/remote admin solution a while back that uses this same technique to allow you to run batch and PowerShell scripts against remote computers via the web portal. As shown in the below screenshot, it works.
The C# that executes the command can be found here: https://github.com/Jay-Rad/InstaTech_Client/blob/master/InstaTech_Service/Socket.cs#L614
if (cmdProcess == null || cmdProcess.HasExited)
{
var psi2 = new ProcessStartInfo("cmd.exe", "/k " + command);
psi2.RedirectStandardOutput = true;
psi2.RedirectStandardInput = true;
psi2.RedirectStandardError = true;
psi2.UseShellExecute = false;
psi2.WorkingDirectory = Path.GetPathRoot(Environment.SystemDirectory);
cmdProcess = new Process();
cmdProcess.StartInfo = psi2;
cmdProcess.EnableRaisingEvents = true;
cmdProcess.OutputDataReceived += async (object sender, DataReceivedEventArgs args) =>
{
jsonMessage.Status = "ok";
jsonMessage.Output = args.Data;
await SocketSend(jsonMessage);
};
cmdProcess.ErrorDataReceived += async (object sender, DataReceivedEventArgs args) =>
{
jsonMessage.Status = "ok";
jsonMessage.Output = args.Data;
await SocketSend(jsonMessage);
};
cmdProcess.Start();
cmdProcess.BeginOutputReadLine();
cmdProcess.BeginErrorReadLine();
}
else
{
cmdProcess.StandardInput.WriteLine(command);
}
What I'm trying to do
Launch PSExec to open CMD on a remote computer passing "Net Use * \Server\Share" command
Launch PSExec again and remove the share i just created.
I can't seem to figure out how to get the drive letter that the wild card used.
Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.FileName = #"\\Server\PStools\PSExec.exe";
p.StartInfo.Arguments = #"\\ComputerName -e -s cmd.exe ""/C Net USE * \\Server\Share /Persistent:NO""";
p.Start();
The net use command with a wildcard will pick the first available drive letter in the sequence from Z to A. It reports the selected drive letter in the console output like so:
C:\>net use * \\server\share
Drive Z: is now connected to \\server\share.
The command completed successfully.
C:\>_
So what you need is to capture the output of the PSExec command and parse it to find the allocated drive letter.
I haven't tried this with PSExec as yet, but this is the code I use for capturing the output of commands via cmd.exe:
static class CommandRunner
{
static StringBuilder cmdOutput = new StringBuilder();
public static string Run(string command)
{
if (string.IsNullOrWhiteSpace(command))
return null;
using (var proc = new Process())
{
proc.StartInfo.FileName = "cmd.exe";
proc.StartInfo.Arguments = "/c " + command;
proc.StartInfo.LoadUserProfile = false;
proc.StartInfo.CreateNoWindow = true;
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.RedirectStandardError = true;
proc.StartInfo.UseShellExecute = false;
proc.EnableRaisingEvents = true;
proc.OutputDataReceived += proc_DataReceived;
proc.ErrorDataReceived += proc_DataReceived;
try
{
proc.Start();
proc.BeginErrorReadLine();
proc.BeginOutputReadLine();
proc.WaitForExit();
}
catch (Exception e)
{
cmdOutput.AppendLine("***Exception during command exection***");
cmdOutput.AppendLine(e.Message);
cmdOutput.AppendLine("*** ***");
}
}
return cmdOutput.ToString();
}
static void proc_DataReceived(object sender, DataReceivedEventArgs e)
{
if (e.Data != null)
cmdOutput.AppendLine(e.Data);
}
}
To get the output of a command on the local machine call it like this:
string output = CommandRunner.Run("net use");
Shouldn't be too hard to add a method that executes commands on a remote PC using PSExec instead of the local cmd.exe. Something similar to the following:
public static string Remote(string target, string command, string peFlags = "-e -s")
{
if (string.IsNullOrWhiteSpace(command))
return null;
using (var proc = new Process())
{
proc.StartInfo.FileName = #"C:\PSTools\PSExec.exe";
proc.StartInfo.Arguments = string.Format(#"\\{0}{1} cmd.exe ""/c {2}""", target, peFlags == null ? "" : " " + peFlags, command);
proc.StartInfo.LoadUserProfile = false;
proc.StartInfo.CreateNoWindow = true;
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.UseShellExecute = false;
proc.EnableRaisingEvents = true;
proc.OutputDataReceived += proc_DataReceived;
try
{
proc.Start();
proc.BeginOutputReadLine();
proc.WaitForExit();
}
catch
{ }
}
return cmdOutput.ToString();
}
NOTE: I removed the stderr redirection here because I want only the output of the remote program, not the various lines added to the output by PSExec.