strange behaviour on another process via Process.Start(startInfo) -- continue - c#

The original location (strange behaviour on another process via Process.Start(startInfo)) doesn't allow me post test code properly. I have to start a new question here.
Our C#(V3.5) needs to call a C++ executable to process a raw data file and generate result files for us.
It worked before with the following code (without ReadToEnd() or ReadLine() call):
startInfo.UseShellExecute = false;
startInfo.RedirectStandardError = true;
startInfo.RedirectStandardOutput = true;
Now the input raw data file changed (bigger than before). Our call to that executable will hang forever. Based on advice, I added ReadToEnd() or ReadLine() but it will hang on those calls. So I have to either use startInfo.UseShellExecute = true; or set
startInfo.UseShellExecute = false;
// and with
startInfo.RedirectStandardError = false;
startInfo.RedirectStandardOutput = false;
Don't know why?
Following are cases I tested:
working case:
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.CreateNoWindow = true;
startInfo.UseShellExecute = false;
startInfo.RedirectStandardError = false;
startInfo.RedirectStandardOutput = false;
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.FileName = path;
startInfo.Arguments = rawDataFileName;
//startInfo.WorkingDirectory = Util.GetParentDirectory(path, 1);
try
{
Process correctionProcess = Process.Start(startInfo);
correctionProcess.WaitForExit();
}
working case:
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.CreateNoWindow = true;
startInfo.UseShellExecute = true;
startInfo.RedirectStandardError = false;//can be commented out
startInfo.RedirectStandardOutput = false;//can be commented out
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.FileName = path;
startInfo.Arguments = rawDataFileName;
startInfo.WorkingDirectory = Util.GetParentDirectory(path, 1);
try
{
Process correctionProcess = Process.Start(startInfo);
correctionProcess.WaitForExit();
}
NOT working case (using ReadLine()):
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.CreateNoWindow = true;
startInfo.UseShellExecute = false;
startInfo.RedirectStandardError = true;
startInfo.RedirectStandardOutput = true;
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.FileName = path;
startInfo.Arguments = rawDataFileName;
//startInfo.WorkingDirectory = Util.GetParentDirectory(path, 1);
try
{
Process correctionProcess = Process.Start(startInfo);
while (!correctionProcess.HasExited)
{
Log.logItem(LogType.Performance, "read errorMsg ####", "DPTM::correctData()", "");
string errorMsg = correctionProcess.StandardError.ReadLine(); // <-- Hangs here
Log.logItem(LogType.Performance, "read errorMsg", "DPTM::correctData()", "errorMsg=" + errorMsg);
string outputMsg = correctionProcess.StandardOutput.ReadLine();
Log.logItem(LogType.Performance, "read outputMsg", "DPTM::correctData()", "outputMsg=" + outputMsg);
Thread.Sleep(100);
}
}
NOT working case (using ReadToEnd()):
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.CreateNoWindow = true;
startInfo.UseShellExecute = false;
startInfo.RedirectStandardError = true;
startInfo.RedirectStandardOutput = true;
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.FileName = path;
startInfo.Arguments = rawDataFileName;
//startInfo.WorkingDirectory = Util.GetParentDirectory(path, 1);
try
{
Process correctionProcess = Process.Start(startInfo);
while (!correctionProcess.HasExited)
{
Log.logItem(LogType.Performance, "read errorMsg ####", "DPTM::correctData()", "");
string errorMsg = correctionProcess.StandardError.ReadToEnd(); // <-- Hangs here!
Log.logItem(LogType.Performance, "read errorMsg", "DPTM::correctData()", "errorMsg=" + errorMsg);
string outputMsg = correctionProcess.StandardOutput.ReadToEnd();
Log.logItem(LogType.Performance, "read outputMsg", "DPTM::correctData()", "outputMsg=" + outputMsg);
Thread.Sleep(100);
}
}
NOT working case:
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.CreateNoWindow = true;
startInfo.UseShellExecute = false;
startInfo.RedirectStandardError = true;
startInfo.RedirectStandardOutput = true;
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.FileName = path;
startInfo.Arguments = rawDataFileName;
//startInfo.WorkingDirectory = Util.GetParentDirectory(path, 1);
try
{
Process correctionProcess = Process.Start(startInfo);
Log.logItem(LogType.Performance, "read errorMsg ####", "DPTM::correctData()", "");
string errorMsg = correctionProcess.StandardError.ReadToEnd(); // <-- Hangs here!
Log.logItem(LogType.Performance, "read errorMsg", "DPTM::correctData()", "errorMsg=" + errorMsg);
string outputMsg = correctionProcess.StandardOutput.ReadToEnd();
Log.logItem(LogType.Performance, "read outputMsg", "DPTM::correctData()", "outputMsg=" + outputMsg);
correctionProcess.WaitForExit();
}

you are deadlocked on the error/output buffer. Your second process is waiting for the buffer to free up while you are waiting for that process to finish. You need to asynchronously read output/error streams to prevent that.
Looking at your MSDN sample: wow that sample is really confusing... It would've been easier to follow if they just limited it to std out or std error.
You need to first set data handler
correctionProcess.OutputDataReceived += new DataReceivedEventHandler(OutputDataHandler);
and then call
p.BeginOutputReadLine();
Just like comment [confusingly] advises do no try to read output there. Use handler instead.
private void OutputDataHandler(object sendingProcess, DataReceivedEventArgs data)
{
if (data.Data != null)
{
// append data.Data to your internal buffer (StringBuilder?)
}
}

Related

Problem with running batch files from within my application

I've been trying to create a simple application to backup my Windows Server databases aswell as a whole server backup.
For this I want to use batch files which are being executed by my application.
I tried several approaches but for some reason it always fails so I'd be happy if you could help me out.
Batch file BACKUPSERVER:
wbadmin start backup -backupTarget:D: -include:C: -allCritical -quiet
I have to run the bat as administrator or it fails due to missing permissions.
C# code:
static Task<int> RunProcessAsync(string fileName)
{
............
Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.Verb = "runas";
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.Arguments = "/C \"D:\\SQLBACKUP\\BACKUPSERVER.bat\"";
p.Start();
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
}
Debugging says 'wbadmin wasnt found'. 'runas' activated or not doesn't make any difference.
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = fileName;
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
startInfo.UseShellExecute = true;
startInfo.CreateNoWindow = false;
// startInfo.Verb = "runas";
var process = new Process
{
StartInfo = { FileName = fileName },
EnableRaisingEvents = true
};
process.StartInfo = startInfo;
process.Exited += (sender, args) =>
{
tcs.SetResult(process.ExitCode);
process.Dispose();
};
process.Start();
Also doesn't work.
Any ideas?
EDIT:
I'm able to run commands like shutdown but wbadmin doesn't work whatsoever...
This is how I solved the problem:
Make sure ure compiling for 64bit if u intend to use your application on 64bit system, otherwise it will redirect to different subfolders and wont find 'wbadmin.exe'.
Run wbadmin with ProcessStart or run a batch but without direct cmd input, so use this with filename = batch file or wbadmin with startInfo.Arguments:
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = fileName;
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
startInfo.UseShellExecute = true;
startInfo.CreateNoWindow = false;
// startInfo.Verb = "runas";
var process = new Process
{
StartInfo = { FileName = fileName },
EnableRaisingEvents = true
};
process.StartInfo = startInfo;
process.Exited += (sender, args) =>
{
tcs.SetResult(process.ExitCode);
process.Dispose();
};
process.Start();
Make sure u request administrator rights

running cmd in c# answer yes to prompt

I'm running the below command from c#. There is a prompt that will be shown that I want to answer "yes" to how can I do this with the current code
If I run this as a batch script I can just do
echo y | pscp.exe -batch -pw password E:\\Certs\\client.conf me#<ip>:/home/user
which works - but unsure how I can replicate this using the below
string pscpPath="-batch -pw password E:\\Certs\\client.conf me#<ip>:/home/user";
ExecuteCopyCerts("pscp.exe", pscpPath);
Function:
public Boolean ExecuteCopyCerts(string fileName, string arguments)
{
txtLiveHubStatus.Text = "";
try
{
System.Diagnostics.ProcessStartInfo procStartInfo = new System.Diagnostics.ProcessStartInfo(fileName, arguments);
procStartInfo.RedirectStandardOutput = true;
procStartInfo.UseShellExecute = false;
procStartInfo.CreateNoWindow = true;
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo = procStartInfo;
proc.Start();
string result = proc.StandardOutput.ReadToEnd();
return proc.ExitCode == 0;
}
}
Set RedirectStandardInput to true
procStartInfo.RedirectStandardInput = true
and then write to StandardInput
proc.StandardInput.WriteLine("yes");
To reiterate what Hesam said though the prompt is Y, not yes. This is the prompt for the cert, which only occurs on the first call to each new linux machine. I use this code today in one of our applications.
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = "pscp";
psi.RedirectStandardInput = true;
psi.RedirectStandardOutput = false;
psi.Arguments = $"-r -p -pw {passWord} \"{localFileNamePath}\" {userName}#{hostName}:{remotePath}";
psi.UseShellExecute = false;
psi.CreateNoWindow = true;
using (Process process = new Process())
{
process.StartInfo = psi;
process.Start();
process.StandardInput.WriteLine("Y");
process.WaitForExit();
}

working directory for cygwin using it in c#

I have a problem, I start cygwin commands with c#, what is working fine. The Problem is to set the working directory.
pathANDcommand = C:\cygwin\bin\ls.exe";
arg = " -lisa";
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
startInfo.FileName = pathANDcommand;
startInfo.Arguments = arg;
output.Enqueue(arg + "\n" + "\n");
startInfo.WorkingDirectory = #"\cygdrive\c\Users\jhb\Source\Repos\myprogramm";
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
startInfo.UseShellExecute = false;
startInfo.CreateNoWindow = true;
startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
process.StartInfo = startInfo;
process.Start();
If I set startInfo.WorkingDirectory to a windows path the program will run, but the command does not run in the path I set.

Start hidden process C#

Code:
System.Diagnostics.Process.Start("rundll32.exe", "InetCpl.cpl,ClearMyTracksByProcess 4351");
I want start it hidden, how I can do it?
You could set the WindowStyle property to Hidden like this:
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = "rundll32.exe";
startInfo.UseShellExecute = false;
startInfo.RedirectStandardOutput = true;
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
//Start the process
Process proc = Process.Start(startInfo);
Goodluck.
one can do like the code below:
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = ....
psi.RedirectStandardInput = true;
psi.RedirectStandardOutput = false;
psi.Arguments =...
psi.UseShellExecute = false;
psi.CreateNoWindow = true; // <- key line

Reading output from another running application

I'm working on a custom IDE in C# for a scripting language, and I have a problem.
I'm trying to start the compiler process (pawncc.exe) and pass arguments to it. I've done that, and now I have a problem. When I want to display the output from the compiler application, it only displays some parts of it. It should output this (got this from the command prompt):
Pawn compiler 3.2.3664 Copyright (c) 1997-2006, ITB CompuPhase
newGM.pwn(0) : fatal error 100: cannot read from file: "includes/main_include.inc"
Compilation aborted.
1 Error.
But it doesn't. It outputs this (in the application, using the same command/arguments):
Pawn compiler 3.2.3664 Copyright (c) 1997-2006, ITB CompuPhase
1 Error.
I just don't get it! It's a really weird thing. It might be something simple but I've been looking at it, and researching for hours now! Here's my code:
public Form3(string path)
{
InitializeComponent();
this._path = path;
Process myProcess = new Process();
ProcessStartInfo startInfo = new ProcessStartInfo("pawncc.exe");
startInfo.CreateNoWindow = true;
startInfo.UseShellExecute = false;
startInfo.RedirectStandardOutput = true;
startInfo.Arguments = path + " -r -d2";
myProcess.StartInfo = startInfo;
myProcess.Start();
while (true)
{
string myString;
byte[] buffer = new byte[512];
var ar = myProcess.StandardOutput.BaseStream.BeginRead(buffer, 0, 512, null, null);
ar.AsyncWaitHandle.WaitOne();
var bytesRead = myProcess.StandardOutput.BaseStream.EndRead(ar);
if (bytesRead > 0)
{
myString = Encoding.ASCII.GetString(buffer, 0, bytesRead);
}
else
{
myProcess.WaitForExit();
break;
}
richTextBox1.Text = myString;
}
}
!!EDIT:
It does the same thing with this code:
public Form3(string path)
{
InitializeComponent();
this._path = path;
Process myProcess = new Process();
ProcessStartInfo startInfo = new ProcessStartInfo("pawncc.exe");
startInfo.CreateNoWindow = true;
startInfo.UseShellExecute = false;
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
startInfo.Arguments = path + " -r -d2";
myProcess.StartInfo = startInfo;
myProcess.Start();
using (StreamReader reader = myProcess.StandardOutput)
{
string result = reader.ReadToEnd();
richTextBox1.Text = result;
}
}
You need to redirect the standard error stream as well:
startInfo.RedirectStandardError = true;
Edit: I just reviewed the code and discovered that you are only readonly the StandardOutput stream.
I generally monitor the process for both the standard and error output streams using the DataReceived events on the process and adding the results into a stringbuilder, then storing the StringBuilder content in the UI element:
private static System.Text.StringBuilder m_sbText;
public Form3(string path)
{
InitializeComponent();
this._path = path;
Process myProcess = new Process();
ProcessStartInfo startInfo = new ProcessStartInfo("pawncc.exe");
startInfo.CreateNoWindow = true;
startInfo.UseShellExecute = false;
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
startInfo.Arguments = path + " -r -d2";
myProcess.StartInfo = startInfo;
m_sbText = new System.Text.StringBuilder(1000);
myProcess.OutputDataReceived += ProcessDataHandler;
myProcess.ErrorDataReceived += ProcessDataHandler;
myProcess.Start();
myProcess.BeginOutputReadLine();
myProcess.BeginErrorReadLine();
while (!myProcess.HasExited)
{
System.Threading.Thread.Sleep(500);
System.Windows.Forms.Application.DoEvents();
}
RichTextBox1.Text = m_sbText.ToString();
}
private static void ProcessDataHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
// Collect the net view command output.
if (!String.IsNullOrEmpty(outLine.Data))
{
// Add the text to the collected output.
m_sbText.AppendLine(outLine.Data);
}
}
There are obviously variations on this, but this should get you started.
I dont have the pawnCC application so I cant try but it appears they restrict the verbosity of debugging information to external applications - apart from the command prompt.
Can you try spawning the pawncc.exe via cmd:
"cmd.exe \c CommandParameterToLaunchPawnCCwithArguments"
I've noticed some sporadic issues when dealing with the raw output/error streams from spawned processes in the past, hence why I usually deal with captured output via eventing:
Process myProcess = new Process();
ProcessStartInfo startInfo = new ProcessStartInfo("pawncc.exe");
startInfo.CreateNoWindow = true;
startInfo.UseShellExecute = false;
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
startInfo.Arguments = path + " -r -d2";
myProcess.EnableRaisingEvents = true;
myProcess.OutputDataReceived += OnOutputDataReceived;
myProcess.ErrorDataReceived += OnErrorDataReceived;
myProcess.StartInfo = startInfo;
myProcess.Start();
myProcess.BeginOutputReadLine();
myProcess.BeginErrorReadLine();

Categories

Resources