I can't be sure if this is an issue specific to Accurev, or is more to do with calling the command line from C#. I'll make the question and its tags more specific when I work out which it is.
I've got the following two calls that I can successfully make using command prompt :
accurev stat -d -s "ProductionSupport" -fx
accurev hist -s "ProductionSupport" -ftx -e "1570567"
And in both cases I can see xml as a result.
However I want to make these calls from a C# program, and the first one of those works great but inexplicably the second one returns an empty string.
public string CallAccurev(string arguments)
{
Process cmdUtility = new Process
{
StartInfo =
{
FileName = "C:\\Program Files (x86)\\AccuRev v5.5\\bin\\accurev.exe",
Arguments = arguments,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true
}
};
cmdUtility.Start();
var s = cmdUtility.StandardOutput.ReadToEnd();
cmdUtility.WaitForExit();
return s;
}
My calls look like this :
var checkInComment = CallAccurev("hist -s " + _streamName + " -e " + Convert.ToString(processedFile.TransId) + " -ftx");
var filesString = CallAccurev("stat -d -s " + _streamName + " -fex");
As an attempt to get around this I've tried putting my "hist" call and its parameters into a .Bat file. I can see that double clicking that file gives the intended behaviour but when I call it from code I get empty strings again.
Any answers or clues on how to debug what is happening during my hist call are greatly appreciated!
I would think you would need to read the standard output AFTER the process has completed.
cmdUtility.WaitForExit();
var s = cmdUtility.StandardOutput.ReadToEnd();
However, reading Accurev output directly can sometimes overflow the buffer. You might want to investigate using asynchronous reading. How to spawn a process and capture its STDOUT in .NET?
Related
I've got a command I'm trying to run through C# to get a list of tasks in CSV format from a bunch of computers.
To do this, I am using the SCHTASKS command with command redirection to a CSV. So I wrote this code in C# to do this for me:
string commandGetStatus = "schtasks";
string argumentsGetStatus = "/s:" + CompName +
" /fo:csv" +
" > \"" + #"\\BIG\OL\UNC\PATH\"+CompName+".csv" + "\"";
getLatestTaskStatus.StartInfo.FileName = commandGetStatus;
getLatestTaskStatus.StartInfo.Arguments = argumentsGetStatus;
getLatestTaskStatus.StartInfo.CreateNoWindow = true;
getLatestTaskStatus.StartInfo.RedirectStandardOutput = true;
getLatestTaskStatus.StartInfo.RedirectStandardInput = true;
getLatestTaskStatus.StartInfo.RedirectStandardError = true;
getLatestTaskStatus.StartInfo.UseShellExecute = false;
getLatestTaskStatus.Start();
It returns the output:
ERROR: Invalid syntax.
Type "SCHTASKS /?" for usage.
So I used StartInfo.FileName + " " + StartInfo.Arguments to print out the exact command that should be being executed. From there, I copy and pasted the command into CMD. Where it worked without a problem. This was the command + args:
schtasks /s:COMPUTERNAME /fo:csv > "\\BIG\OL\UNC\PATH\COMPUTERNAME.csv"
I'm not sure what the problem is at this point.
My Solution
Luaan was absolutely correct about the problem. The command prompt redirection operator,>, is not available without using Command Prompt. Fortunately, the solution was quote simple and easy to implement. I reduced the argument variable to:
"/s:" + CompName + " /fo:csv"
And with standard output being redirected, I simply used:
string output = process.StandardOuptut.ReadToEnd();
File.WriteAllText(#"\\UNC\File\Path\" + myfile + ".csv", output);
You explicitly disabled UseShellExecute - > is a shell operator, not something inherent to processes. It definitely isn't an argument. You need to read the output and write it out manually :)
I am using psexec.exe to install some software on a large amount of computers. I would like to be able to catch the %ERRORLEVEL% (or its c# equivalent) and write them to a TXT file. ( cmd.exe exited on 6440-nbpb00f51w with error code 0.)
After extensive research, I am finding several possibilities. My end-goal will be to have a .CSV file. Each row would have the computer name and the output of the psexec command. Using this data I will be able to determine which computers the update was successful on.
string[] pcnamearray = new string[] {"COMPUTERNAME1", "COMPUTERNAME2"};
foreach (string i in pcnamearray)
{
Process p = new Process();
string psexeclaunch = "psexec.exe";
p.StartInfo.FileName = psexeclaunch;
p.StartInfo.RedirectStandardOutput = false;
p.StartInfo.UseShellExecute = true;
p.StartInfo.CreateNoWindow = false;
string arg = "/silent";
p.StartInfo.Arguments = #" -i \\" + i + #" -u mydomain\admin -p mypassword -h -e cmd.exe /c start \\networkserver\sw\myfile.exe" + arg;
p.Start();
}
I am not here looking for someone to write the code for me. I am here to ask people for their advice, or past experiences with this. I know there are several different methods available. Ideally, I would prefer to have a short sweet line for each result, but if i have to take the entire output and use macros to shave them down to a more readable form, that is fine too.
So far, I have tried:
Console.WriteLine(p.StandardOutput.ReadToEnd());
And...
System.IO.File.WriteAllLines(#"C:\Users\areu2447\Desktop\UpdateDAT\Final\out.txt", lines);
And...
FileStream filestream = new FileStream(#"C:\Users\areu2447\Desktop\UpdateDAT\Final\out.txt", FileMode.Append);
var streamwriter = new StreamWriter(filestream);
streamwriter.AutoFlush = true;
Console.SetOut(streamwriter);
Console.SetError(streamwriter);
The last one seems to me like the best one but cannot seem to get it to work. I have tried including it in my foreach loop, but file is in use by another process, so after looking into it i found that it needed to be on its own. Even with it alone, i still cannot get it to work.
Please help/advise!
I was able to get it to actaully modify my TXT file, even though there was nothing added to it, by using the following:
System.IO.StreamReader reader = p.StandardOutput;
String sRes = reader.ReadToEnd();
StreamWriter SW;
SW = File.CreateText(#"C:\Users\areu2447\Desktop\UpdateDAT\Final\out.txt");
SW.WriteLine(sRes);
SW.Close();
Console.WriteLine("File Created Successfully");
reader.Close();
I would recommend using System.Management.Automation to create a PowerShell pipeline and run the script in process.
Executing PowerShell scripts from C#
Im recently trying to execute the following line ;
string strCmdText;
strCmdText = "netstat -np TCP | find " + quote + number + quote + "";
System.Diagnostics.Process.Start("netstat.exe", strCmdText);
Logs.Write("LISTEN_TO(" + Registry_val1.Text + ")", strCmdText);
now what this has to do is basicly find all TCP ports that contain '80' in them and show them up in my custom-made log system that will make a logbook in my folder called;
LISTEN_TO(80)-{date_time}.txt
inside this .txt it should contain the command issued text, however all i get is a time.
i debugged this command as above, and unfortunately all i know is that the CMDtext is set correctly, and that my logging system works correctly, leaving me with no choice that NETSTAT may be closed as soon as the query is launched?
hopefully i provided anough information, as this is my first post.
Regards,
Co
Due to vague description, here's an other-sort same code i tried to do, however still remain getting only a time.
const string quote = "\"";
Process p = new Process();
p.StartInfo.FileName = "cmd";
p.StartInfo.Arguments = "netstat -np TCP | find " + quote + number + quote + "";
p.StartInfo.CreateNoWindow = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.UseShellExecute = false;
p.Start();
String output = p.StandardOutput.ReadToEnd();
Logs.Write("LISTEN_TO(" + Registry_val1.Text + ")", output);
basicly, you could see this as; textbox1.text = output; execpt now the output is being putten to a log file.
I don't understand why you use netstat in the first place. The .Net framework has a load of classes that give all kind of data, in this case IPGlobalProperties has the method you need.
var ip = System.Net.NetworkInformation.IPGlobalProperties.GetIPGlobalProperties();
foreach(var tcp in ip.GetActiveTcpConnections()) // alternative: ip.GetActiveTcpListeners()
{
if (tcp.LocalEndPoint.Port == number
|| tcp.RemoteEndPoint.Port == number)
{
Logs.Write(
String.Format(
"{0} : {1}",
tcp.LocalEndPoint.Address,
tcp.RemoteEndPoint.Address));
}
}
The benefit of using the build-in classes is the ease of shaping and selecting whatever you need and most important: you spare yourself and your user an out-of-process call and parsing of output.
You may try this:
strCmdText = "cmd /c \"netstat -np TCP | find " + quote + number + quote + "\"";
if this does not work, try first to use the command in a cmd prompt to make sure it returns data.
cmd /c "netstat -an | find "80"
I have an exe file in which I am trying to pass arguments through c#. the code is as follows
class Class1
{
static void Main()
{
string[] arg;
arg = new string[3];
Process p = new Process();
p.StartInfo.FileName = #"D:\xxx.exe";
for (int i = 0; i < 3; i++)
{
arg[i] = Console.ReadLine();
}
p.StartInfo.Arguments = arg[0] + " " + arg[1] + " " + arg[2];
p.Start();
}
}
I open up a console and then write the arguments there. As soon as I am finished typing 3 arguments, I make a string out of the 3 arguments and then call Process.Start() with the arguments in the p.StartInfo.Arguments string. The exe file loads but it does not generate any output. The strange thing is that if I open the exe file from my computer and then write
Arg1.txt Arg2.txt Arg3.txt
and press enter the exe file generates the output. However the same arguments in the same style are currently being passed through C# code and it is not generating any output. I donot understand what I am doing wrong. There are multiple questions on StackOverflow about this, I know that, however they all suggest the same procedure as what I have done here. I have also tried giving arguments as
p.StartInfo.Arguments = "\"arg[0]\"\"arg[1]\"\"arg[2]\"";
but this also has not worked.
Try this:
p.StartInfo.Arguments = "\"" + arg[0] + " " + arg[1] + " " + arg[2] + "\"";
p.Start();
It is recomended to use "" when you use several parameters between gaps.
EDIT: No "\" have to be included if you type it ok. It is the escape character. See picture below.
I'm trying to create a small program to run on a centralized device. This program will run
"psexec \server(s) netstat -na | findstr "LISTENING""
to collect netstat data from remote nodes (should redirect output to string), then parse the data and compare against a known list. I can run the psexec cmd above without any issues from the cmd line, but when I try to run the same command as a process within my C# program, no data is returned to be parsed. I can see that the netstat is being run (cmd window flashes with netstat results), but the process.standardoutput is not catching the stream. If I use ping or pretty much anything other than psexec as an argument, the stream is caught and the results are shown in my text box. I've also tried setting the filename to psexec.exe and specifying the arguments but I get the same results. Last but not least, if I run psexec without any arguments, I get the help kickback info returned in my textbox. This is true if I'm running psexec.exe as the filename OR if I run cmd.exe as filename with "/c psexec" specified as args.
I'm just trying to get psexec output to be caught when executing locally at this point. I'll worry about psexec to remote machines later. Any help would be MUCH appreciated.
Here's the code:
System.Diagnostics.Process pProcess = new System.Diagnostics.Process();
pProcess.StartInfo.FileName = "cmd.exe";
pProcess.StartInfo.Arguments = "/c psexec netstat";
pProcess.StartInfo.UseShellExecute = false;
pProcess.StartInfo.RedirectStandardOutput = true;
pProcess.Start();
string strOutput = pProcess.StandardOutput.ReadToEnd();
pProcess.WaitForExit();
if (pProcess.HasExited)
{
textBox1.Text = strOutput;
}
else
{
textBox1.Text = "TIMEOUT FAIL";
}
I would recommend also capturing the standard error output in case anything is being reported there.
Also, you may have a disconnect between "bitness" of psexec and your application if you are running on a 64-bit OS. If this is the case, change the platform for the project to match that of psexec rather than building as Any CPU.
Came across a few things to be changed but your recommendation of capturing standard error output was dead on and a good start. Turns out some info was being sent to the error output (even though really wasn't error, just run status 0 from psexec) so I knew at that point psexec wasn't just eating ALL the output. Once I started trying to pass remote hosts as args, I started getting user/pass error data back. Also needed to catch standard input if I wanted to supply credentials for proc run. Threw in some str literals and credentials for the remote exec, works perfectly. Thanks for the help. Here is the updated code--
System.Diagnostics.Process pProcess = new System.Diagnostics.Process();
pProcess.StartInfo.Domain = "domain";
pProcess.StartInfo.UserName = "user with priv";
pProcess.StartInfo.Password = new System.Security.SecureString();
char [] pass = textBox3.Text.ToArray();
for (int x = 0; x < pass.Length; ++x)
{
pProcess.StartInfo.Password.AppendChar(pass[x]);
}
pProcess.StartInfo.FileName = #"psexec.exe";
pProcess.StartInfo.Arguments = #"\\remoteHost netstat -ano";
pProcess.StartInfo.UseShellExecute = false;
pProcess.StartInfo.RedirectStandardInput = true;
pProcess.StartInfo.RedirectStandardOutput = true;
pProcess.StartInfo.RedirectStandardError = true;
pProcess.Start();
pProcess.WaitForExit(30000);
if (!pProcess.HasExited)
{
pProcess.Kill();
}
string strOutput = pProcess.StandardOutput.ReadToEnd();
string errOutput = pProcess.StandardError.ReadToEnd();
textBox1.Text = strOutput;
textBox2.Text = errOutput;