git.exe writes ErrorOutput even though no error occured [duplicate] - c#

This question already has answers here:
PowerShell displays some git command results as error in console even though operation was successful
(2 answers)
Closed 2 years ago.
I've done a git pull origin develop with help of System.Diagnosticsc.Process like this:
var gitProcess = new Process
{
StartInfo = new ProcessStartInfo
{
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
FileName = "git.exe",
Arguments = "pull origin develop",
WorkingDirectory = #"C:\Workspaces\MyRepo",
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden
}
};
gitProcess.Start();
string outputString = null;
string errorString = null;
gitProcess.OutputDataReceived += (sender, args) => outputString = string.IsNullOrWhiteSpace(outputString) ? args.Data : $"{outputString}\r\n{args.Data}";
gitProcess.ErrorDataReceived += (sender, args) => errorString = string.IsNullOrWhiteSpace(errorString) ? args.Data : $"{errorString}\r\n{args.Data}";
gitProcess.BeginErrorReadLine();
gitProcess.BeginOutputReadLine();
gitProcess.WaitForExit();
The Result is:
gitProcess.ExitCode = 0
outputString = Already up to date.
errorString = From https://mycompany.visualstudio.com/MyProject/_git/MyRepo
* branch develop -> FETCH_HEAD
Now every thing seems okay, but why did the the git.exe put the data into the errorString? It seems like normal information. This makes errorhandling hard. The ExitCode is fine it suggests success.
Any ideas why there is errordata, this happens for several other git - commands in a similar manner.
EXPECTED ANSWER
I don't want to hear about alternatives to do a git-pull in c# that's not my interest. I want to understand the behaviour of the git.exe respectivly the behaviour of System.Diagnostics.Process, basically I want to understand how and why that errordata is produced. Who is responsible for that? Is it the git.exe or the way how System.Diagnostics.Process is doing stuff.
Thank you.

Thank you for your comments, that helped. I especially researched for the main idea of stdout and stderr. The idea of stderr seems not to be only print error messages when the command failes but also to print any diagnostic information that may help understand the process but you may not want that to have in standard output.
This article explains it very well:
https://www.jstorimer.com/blogs/workingwithcode/7766119-when-to-use-stderr-instead-of-stdout
Thank you guys!

Related

Pass args to Main(string[] args)

I am trying to build a Console Application to start my .NetCore Web Applications that I've got built as a .dll
Unfortunately the Args entered in the ProcessStartInfo are not being received by my application, however my application does start and i get a unexpected behaviour in the Console.WriteLine Method.
This code is inside my SocketAPI Project which is a .NetCore 2.2 WebApplication | API Project:
public static void Main(string[] args)
{
// Outputs -> Received :
// ?? Why is args.Length empty? Not even 0 or null??
Console.WriteLine(string.Format("Received : ",args.Length));
CreateWebHostBuilder(args).Build().Run();
}
It gets declared and called by my ProcessRunner which is a Class that holds the current Process:
I am also referring to this documentation:
dotnet command documentation on microsoft.com
Which describes: dotnet [command] [arguments]
This Code is inside the ProcessRunner Constructor
ProcessStartInfo = new ProcessStartInfo
{
FileName = "dotnet",
Arguments = string.Format("BHR.{0}.dll {1}", Name, args),
WorkingDirectory = ".\\",
UseShellExecute = false,
RedirectStandardInput = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
};
Later on I'm calling Process.Start(); inside the ProcessRunner to start the Process.
As said I do get Output but no Args... So why do i see "Received :" but nothing on the end?
Do i have to enable my SocketAPI to receive args when starting them as a process? I've been trying to deal with the problem since 2 days now and I'm completely clueless...
Kind Regards
In your code you are not adding a placeholder for the argument length.
Console.WriteLine(string.Format("Received: {0} ",args.Length));
FYI, This works for me:
var arguments = "myarg1 myarg2";
var dir = #"C:\somedir\somechilddir";
var info = new System.Diagnostics.ProcessStartInfo("dotnet", $"someproject.dll {arguments}");
info.UseShellExecute = false;
info.CreateNoWindow = true;
info.WorkingDirectory = dir;
var process = new System.Diagnostics.Process();
process.StartInfo = info;
process.Start();
process.WaitForExit();
Perhaps, try resolving your working directory using Path.GetFullPath();
Big thank you to #AlexanderHiggins for showing me how dull i should feel now!
In your code you are not adding a placeholder for the argument length.
Console.WriteLine(string.Format("Received: {0} ",args.Length));
Sorry for bothering!

Get and Set the position in Process.StandardOutput

See as an example I open CMD and navigate to the folder I want to get the data from then I use it to open the app with arugents with standard input(all synchronously) the code so far
public static Process Start(bool DoNotShowWindow = false)
{
ProcessStartInfo cmdStartInfo = new ProcessStartInfo
{
FileName = "cmd.exe",
RedirectStandardOutput = true,
RedirectStandardError = true,
RedirectStandardInput = true,
UseShellExecute = false,
CreateNoWindow = DoNotShowWindow
};
Process cmdProcess = new Process
{
StartInfo = cmdStartInfo,
EnableRaisingEvents = true
};
return cmdProcess;
}
//in other method
Process cli = InteractWithConsoleApp.Start();
cli.Start();
cli.StandardInput.WriteLine("cd /");
cli.StandardInput.WriteLine("cd " + path);
cli.StandardInput.WriteLine("fantasygold-cli getbalance abc");
Thread.Sleep(5000);
Problem
Now when I use StandardOutput.Readline, it starts from the beginning and returns me everything like first two lines of copyright,empty lines and even the input which in my case, after waiting 5 secs for the result I want to read line or to the end depending the input from where I had inputted.
possible solution
One solution I found was to change the position but it turned out it doesn't support it and even copying to another stream reader doesn't works(the position is not by line).
Well I can use filters like check a double or for an address starts with F and has a length of 36. The problem comes when I want to get the whole JSON say for like the past transactions, for which I think using filters like '{' and then check for '}'Caveat in this would be bad code, which I don't want.
TLDR
So, what could be the solution to my problem here :)
I found the answer, to open the file in subdirectory just use this cli.StartInfo.FileName = Directory.GetCurrentDirectory() + #"\SubfolderName\fantasygold-cli";
and the arguements like getbalance as cli.StartInfo.Arguments = "getbalance amunim"

C# Program automation - Program hangs/doesn't work

I've been trying to make a program to automate the process of running different processes on my computer. So far I've got the following program running a console version of BleachBit(It's like CCleaner), the process appears in task manager, it hits around 25kb process RAM then CPU usage goes to 0% and just sits there doing nothing for ages and never quits.
Is there something wrong I'm doing in my code that could cause this to happen?
I've tried editing the app.manifest to make sure the program has to be run as admin in case it needed more privileges
Also when running similar code in a bat file to run the program, it's opens its own windows and runs fine, so I'm not sure. Any help in the right direction would be fantastic.
The code I'm running is below.
static void Main(string[] args)
{
string Log = "";
if (File.Exists(Environment.CurrentDirectory + "\\BleachBit\\bleachbit_console.exe"))
{
Log += "File exists";
Log += RunProgramCapturingOutput("\\BleachBit\\bleachbit_console.exe", "--preset --clean");
}
else
Log += "Program not found. Please place at \\BleachBit\\bleachbit_console.exe";
File.WriteAllText("log.txt", Log);
Console.ReadLine();
}
public static string RunProgramCapturingOutput(string filename, string arguments)
{
ProcessStartInfo processInfo = new ProcessStartInfo()
{
FileName = Environment.CurrentDirectory + filename,
Arguments = arguments,
CreateNoWindow = false,
UseShellExecute = false,
WorkingDirectory = Path.GetDirectoryName(Environment.CurrentDirectory + filename),
RedirectStandardError = false,
RedirectStandardOutput = true
};
Process process = Process.Start(processInfo);
process.WaitForExit();
string output = output = process.StandardOutput.ReadToEnd();
Console.WriteLine("Output: " + output);
process.Close();
return output;
}
Switching these lines to this:
string output = output = process.StandardOutput.ReadToEnd();
process.WaitForExit();
allows to avoid deadlocks. The program seems to be a relatively slow running program due to hard-drive I/O, just give it time and you'll see it complete.
I found this deadlock issue from https://msdn.microsoft.com/en-us/library/system.diagnostics.process.standardoutput(v=vs.110).aspx
Where it states in a code block: "// To avoid deadlocks, always read the output stream first and then wait."

How to redirect STDIN .NET Process before Starting the process

Im trying to make C# application that uses hunpos tagger.
Runing hunpos-tag.exe requires three input arguments: model, inputFile, outputFile
In cmd it would look something like this:
hunpos-tag.exe model <inputFile >outputFile
If I just run it with model it writes something and waits for end command. When i tried using standard redirect I either get an exception (i solved this the code was off by a braceket i just get the or scenario now) or I get the results of running the tagger with just model argument. Here's the code:
string inputFilePath = path + "\\CopyFolder\\rr";
string pathToExe = path + "\\CopyFolder\\hunpos-tag.exe";
ProcessStartInfo startInfo = new ProcessStartInfo
{
FileName = pathToExe,
UseShellExecute = false,
RedirectStandardInput = true,
WorkingDirectory = Directory.GetDirectoryRoot(pathToExe),
Arguments = path + "\\CopyFolder\\model.hunpos.mte5.defnpout",
};
try
{
Process _proc = new Process();
_proc.StartInfo.FileName = pathToExe;
_proc.StartInfo.UseShellExecute = false;
_proc.StartInfo.RedirectStandardInput = true;
_proc.StartInfo.Arguments = path + "\\CopyFolder\\model.hunpos.mte5.defnpout";
//Magic goes here
_proc.Start();
_proc.WaitForExit();
}
catch (Exception e)
{
Console.WriteLine(e);
}
Any ideas how can I redirect input before starting my process?
It is not only required to set RedirectStandardInput to true but also you need to use the input stream to write the text you want:
_proc.StandardInput.WriteLine("The text you want to write");
There's no need for that ProcessStartInfo if you're setting the info later on. Just get rid of that. And it seems you are already doing what you want. Just creating the process object doesn't start the process, Process.Start does. Just make a new StreamWriter and pass it Process.StandardInput (I think that's right, it may be something else)

.NET Process unpredicably hangs. Need opinions on alternate solutions

I am trying to reach one simple requirement. I would like to create a C# library that talks to the git executable. I am writing a version control tool for my team that will allow access to git commands for non tech-savvy individuals. Unfortunately, I can not use any third party DLL's (I am using Unity and I do not want to push the requirement for Unity pro due to plugins), otherwise I would use GitSharp or something along those lines.
Currently, I have a function called RunGitCommand that is meant to do all my bidding. This snippet is as follows:
private void RunGitCommand(string executablePath, string arguments, int maxCommandDurationMilliseconds)
{
using (AutoResetEvent outputWaitHandle = new AutoResetEvent(false))
using (AutoResetEvent errorWaitHandle = new AutoResetEvent(false))
{
try
{
CommandOutput = string.Empty;
CommandError = string.Empty;
ProcessStartInfo processStartInfo = new ProcessStartInfo();
processStartInfo.CreateNoWindow = true;
processStartInfo.FileName = executablePath;
processStartInfo.Arguments = arguments;
processStartInfo.UseShellExecute = false;
processStartInfo.RedirectStandardError = true;
processStartInfo.RedirectStandardOutput = true;
int processedTime = 0;
using (Process process = new Process())
{
StringBuilder outputData = new StringBuilder();
StringBuilder errorData = new StringBuilder();
process.StartInfo = processStartInfo;
process.OutputDataReceived += (sender, e) => {
outputWaitHandle.Set();
if (e.Data == null)
{
//outputWaitHandle.Set();
}
else
{
outputData.AppendLine(e.Data);
}
};
process.ErrorDataReceived += (sender, e) =>
{
errorWaitHandle.Set();
if (e.Data == null)
{
//errorWaitHandle.Set();
}
else
{
errorData.AppendLine(e.Data);
}
};
process.Start();
if(process.Id == 0)
{
Environment.LogError("Process id is 0. Aborting.");
return;
}
RunningProcessDescriptor processDescriptor = new RunningProcessDescriptor(maxCommandDurationMilliseconds, process.Id);
ProcessIds.Add(processDescriptor);
Thread.Sleep(200);
int newMaxTime = maxCommandDurationMilliseconds - 100;
process.BeginOutputReadLine();
process.BeginErrorReadLine();
if (process.WaitForExit(newMaxTime) && outputWaitHandle.WaitOne(newMaxTime) && errorWaitHandle.WaitOne(newMaxTime))
{
process.CancelOutputRead();
process.CancelErrorRead();
CommandOutput = outputData.ToString();
CommandError = errorData.ToString();
string combinedOutput = string.Join(System.Environment.NewLine, new string[]{ CommandError, CommandOutput }).Trim();
BatchOutput = string.Join(System.Environment.NewLine, new string[]{ BatchOutput, string.Format("----------// {0} {1} //----------", executablePath, arguments), CommandOutput }).Trim();
BatchError = string.Join(System.Environment.NewLine, new string[]{ BatchError, string.Format("----------// {0} {1} //----------", executablePath, arguments), CommandError }).Trim();
InterpretErrorsAndAddToLists(combinedOutput);
}
else
{
process.Close();
process.WaitForExit();
}
ProcessIds.Remove(processDescriptor);
}
}
catch(Exception genericException)
{
Environment.LogError(genericException.Message);
Environment.LogError(genericException.StackTrace);
}
}
}
Another thing to note is that I'm using this to run these commands so that they don't execute on the main thread:
ThreadPool.QueueUserWorkItem
As you should be able to identify by my commenting and layout, this function is the result of hours of troubleshooting and I am willing to try anything at this point to make it work. I've already moved to using asynchronous calls for receiving the output/error streams, and even added AutoResetEvent objects to cause my threads to wait for each other (although I am not completely familiar with them and might be doing something wrong).
No matter what I try, it seems to randomly hang and not allow the process to exit. When I manually kill the git process, the output is spit out (and is usually right) and the process exits and everything works as normal.
I'm at the point in troubleshooting and frustration where I need professional input. Here are my questions:
Is there something blatantly or obviously wrong with my code below? If I'm doing it wrong, please advise me how to properly execute this code.
Is there another solution where I do not need to include third party DLL files and can just use raw .NET to grab the git console process and interact with it (on mac and pc)?
Other alternatives to these approaches, such as one i've been considering, that uses a "client/server" architecture. I can use third party dll's and whatnot in a separate downloadable program that communicates to the git plugin via TCP to send and receive output/input to the git process. This one is much more work, but would potentially take less time than troubleshooting the git only version.
Just let me know what your professional opinions are so that I can rest at night :).
I look forward to talking with everyone!
-Zack
I tend to use a very simple piece of code for executing a non-interactive command, and getting the standard output from the result. I would suggest starting from something like this, and checking it doesn't hang. Then build any additional logic from there.
private static string ExecuteCommand(string command, string arguments)
{
command = System.Environment.ExpandEnvironmentVariables(command);
var process = new Process
{
StartInfo =
{
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
FileName = command,
Arguments = arguments
}
};
process.Start();
process.WaitForExit();
return process.StandardOutput.ReadToEnd();
}
It's often hard to isolate the problem when there's so much 'dead wood' in the code. Strip it back to the bare bones, and see if you can isolate the problem from there.

Categories

Resources