I'm trying to write an application that provides my own UI for tf.exe (in TFS 2010). (yes, I know VS already does this but this app is custom). For some commands, like properties, I want to capture output from stdout. Also, when a command fails, I want to capture its error output from stderr. I'm using .NET's Process class to launch tf.exe, like so:
try
{
sccProcess.StartInfo.FileName = "tf.exe";
sccProcess.StartInfo.Arguments = arguments;
sccProcess.StartInfo.CreateNoWindow = true;
sccProcess.StartInfo.UseShellExecute = false;
sccProcess.StartInfo.RedirectStandardOutput = true;
sccProcess.StartInfo.RedirectStandardError = true;
sccProcess.Start();
output = sccProcess.StandardOutput.ReadToEnd();
// Redirecting synchronously from output and error streams may cause a deadlock.
// To prevent that, we always read the error stream asynchronously.
sccProcess.ErrorDataReceived += (sender, args) => errorStringBuilder.Append(args.Data);
sccProcess.BeginErrorReadLine();
sccProcess.WaitForExit();
exitCode = sccProcess.ExitCode;
error = errorStringBuilder.ToString();
}
catch (Win32Exception)
{
// The file name for the process couldn't be found.
exitCode = -1;
}
finally
{
sccProcess.Close();
}
However, when I run a command, say "tf checkin", I notice that when standard error is redirected, I no longer see the checkin dialog box that normally opens. Instead, the checkin just happens. Thus, it seems like redirecting stderr for tf.exe behaves as if I set the noprompt flag. Does anyone know how I can capture stderr data without stopping tf.exe dialog prompts?
Thanks,
-Craig
You might be better off calling the TFS API directly rather than calling tf.exe from code. That way you'll have much more control over the behaviour of the application and all the benefits of exceptions rather than stderr. The API also exposes a lot more functionality than is available from tf.exe tfpt.exe and visual studio.
Finally, after months of being frustrated about this I found the solution.
Apparently there's a hidden parameter to tf.exe called /prompt, which forces the UI to be shown even though you've redirected stdout.
Source: http://www.wintellect.com/cs/blogs/jrobbins/archive/2006/12/26/working-offline-with-tfs.aspx
Related
I have a C# Console application which I am trying to execute from another WinForm application just like batch runner by giving the console application's .exe file like below.
Process.Start("Path of Console application exe to execute")
However I need to wait and handle the output and display the output in WinForm's richtextbox from console application once it has completed the execution. How can I achieve this?
Update
I have changed the code to Start a Process and Read using StandardOutput and BeginOutputReadLine() to Read the output asynchronously, but not able to see output in console window, instead console window is getting closed. Not sure how to solve this.
p.StartInfo.UseShellExecute = false;
// p.StartInfo.CreateNoWindow = True
p.StartInfo.RedirectStandardOutput = true;
string #out = null;
p.StartInfo.RedirectStandardError = true;
p.OutputDataReceived += new DataReceivedEventHandler((sender, e) =>
{
#out += e.Data;
});
p.StartInfo.FileName = currentTest;
p.Start();
p.BeginOutputReadLine();
// string output = p.StandardError.ReadToEnd();
// p.WaitForExit()
while (!p.HasExited)
Application.DoEvents();
//Console.WriteLine($#"Output \n'{output.Substring(output.Length - 50)}'");
Console.WriteLine($#"\n Error stream: {#out}");
Console.ReadLine();
You need to redirect stdout (and probably stderr) so that any output comes to you, instead of a console; you may also want to redirect stdin. All of these things are available via ProcessStartInfo, with an example on MSDN. Note that if you want to display updates while the exe is running, you may need a worker thread to read incrementally from StandardOutput and StandardError, rather than ReadToEnd() - which won't return anything at all until the associated output pipe is closed.
However! If the console exe is "yours", it may be simpler to just expose the functionality you want in a library, and invoke it directly in-process. There are times when out-of-process is actively preferred, such as when you need to allow that process to go catastrophically wrong in some scenarios - but usually in-process is preferable, given free rein.
EDIT 2
I "solved" the problem, but I don't want to post it as an answer b/c it doesn't explain what actually happened. In the code for the .NET resourceReader.exe I use
Console.OutputEncoding = System.Text.Encoding.UTF8;
to output the internationalized resources to stdout in unicode. If I reset the encoding at the end of my program with
Console.OutputEncoding = System.Text.Encoding.Default;
then I don't get any errors in Node. If I don't reset it, I get the error described in the original question. It seems that .NET is somehow messing up some of the output encoding settings on the cmd.exe and causing the subsequent node run to fail!
EDIT
I narrowed down the error to being caused by resourceReader.exe. It's a.NET program which reads some resource streams out of the .NET assembly and prints them to the stdout using Console.WriteLine. I added Console.OutputEncoding = System.Text.Encoding.UTF8 to resourceReader.exe because some of the resources are in non ASCII letters and that's whats causing the crash in grunt!
If I take that line out, the task doesn't crash, but the resources show up in non printable ASCII characters! Also, the crash only happens if I actually print non-ASCII to sdtout. If I don't print them, it doesn't error.
ORIGINAL
I added a step to my Gruntfile which uses child_process.execFile to run an read some data from an external program and uses it in the build. Now whenever I run my build, it runs fine the first time, but crashes the second time!
Here's the output from the crash (this is during the uglify task):
File build/Scripts/NDB.contacts.min.js created: 16.85 kBevents.js:85
throw er; // Unhandled 'error' event
^
Error: This socket is closed.
at WriteStream.Socket._writeGeneric (net.js:656:19)
at WriteStream.Socket._write (net.js:709:8)
at doWrite (_stream_writable.js:301:12)
at writeOrBuffer (_stream_writable.js:288:5)
at WriteStream.Writable.write (_stream_writable.js:217:11)
at WriteStream.Socket.write (net.js:634:40)
at Log._write (C:\...\node_modules\grunt\node_modules\grunt-legacy-log\index.js:161:26)
at Log.wrapper [as _write] (C:\...\node_modules\grunt\node_modules\grunt-legacy-log\node_modules\lodash\index.js:3095:19)
at Log._writeln (C:\...\node_modules\grunt\node_modules\grunt-legacy-log\index.js:166:8)
at Log.wrapper [as _writeln] (C:\...\node_modules\grunt\node_modules\grunt-legacy-log\node_modules\lodash\index.js:3095:19)
at Log.writeln (C:\...\node_modules\grunt\node_modules\grunt-legacy-log\index.js:177:8)
at Log.wrapper (C:\...\node_modules\grunt\node_modules\grunt-legacy-log\node_modules\lodash\index.js:3095:19)
at writeln (C:\...\node_modules\grunt\lib\grunt\fail.js:30:13)
at Object.fail.fatal (C:\...\node_modules\grunt\lib\grunt\fail.js:46:3)
at process.uncaughtHandler (C:\...\node_modules\grunt\lib\grunt.js:121:10)
at process.emit (events.js:129:20)
at process._fatalException (node.js:236:26)
at Task.runTaskFn (C:\...\node_modules\grunt\lib\util\task.js:250:7)
at Task.<anonymous> (C:\...\node_modules\grunt\lib\util\task.js:293:12)
at C:\...\node_modules\grunt\lib\util\task.js:220:11
at process._tickCallback (node.js:355:11)
Here's the code for the task which uses child_process.
function readAllCultures() {
var readDeferred = q.defer();
childProc.execFile("../tools/resourceReader.exe", function (err, stdout, stderr) {
if (err) throw new Error(err);
var cultures = JSON.parse(stdout);
readDeferred.resolve(cultures);
});
return readDeferred.promise;
}
Here's some things I discovered debugging that might be helpful
If I redirect the output of grunt (using either > filename or | process) it runs fine
When I redirect output, I never see the message from uglify that it created the main output only that it created the source map.
If I close and reopen my command prompt (cmd.exe) it works fine
I added a listener to the exit and close events of the child process using rdr.on("close", function() { console.log("close"); }); and the same for exit. Both events fire as expected in the first run.
Using Process Explorer, I can see node.exe open under the command prompt and close again when my command finishes running. There are no processes visibly "left open" under the command prompt.
The fact that the stack trace is ultimately showing a socket.write error is interesting. Nothing in the code you've provided suggests you are trying to write to a socket.
If we follow the stack down, we can see that it's actually grunt that is trying to write to a socket, as it's attempting to log an uncaught exception.
So, first your grunt task throws a currently unknown error, and then grunt itself errors because it can't tell you about it.
I would first try and log the error that occurs in your child process. Currently if there is an error present, you simply throw it without finding out what it is. It's likely that this is what grunt is trying and failing to log.
Replace
if (err) throw new Error(err);
With
if (err) console.error(err);
This will hopefully avoid the socket.write problem and give you specific information about what error is happening on the child process. What do you see?
Second, I would try using child_process.exec instead of child_process.execFile. This will spawn a shell and run resourceReader.exe inside of it (instead of running it directly). This might help avoid any problems you are encountering with the command still running/failing in the background, which could be the cause of the socket.write error.
I am aware that this may on first glance look like a question you have seen before:
Knowing when an external process' window shows
But this is slightly different.
I have an C# asp.net web application, for helping people create an installer for their programs. (The developers here are mostly mechanical engineers scripting equations in some calculation tools, they are not software people, so we don't want them spending time learning wix, debugging the installers, maintaing GUID's between releases, and so on..)
The serverside will be running the console application "heat.exe" (a tool that is shipped with the wix tools), to harvest information on how to register dll's etc., if and only if they have a dll in their repository..
I do it like this:
public int runHeat(string filePath, string outputFile, ref string response)
{
response += "run heat.exe to harvest file data" + '\r' + '\n';
string args = "file " + '"' + filePath + '"' + " -srd -out" + '"' + outputFile + '"';
string command = Path.Combine(WixBinariesPath, "heat.exe");
string workPath = Path.GetDirectoryName(filePath);
StringBuilder outputBuilder;
ProcessStartInfo processStartInfo;
Process process;
outputBuilder = new StringBuilder();
processStartInfo = new ProcessStartInfo();
processStartInfo.CreateNoWindow = true;
processStartInfo.RedirectStandardOutput = true;
processStartInfo.RedirectStandardInput = true;
processStartInfo.UseShellExecute = false;
processStartInfo.WorkingDirectory = workPath;
processStartInfo.Arguments = args;
processStartInfo.FileName = command;
processStartInfo.ErrorDialog = false;
//create the process handler
process = new Process();
process.StartInfo = processStartInfo;
// enable raising events because Process does not raise events by default
process.EnableRaisingEvents = true;
// attach the event handler for OutputDataReceived before starting the process
process.OutputDataReceived += new DataReceivedEventHandler
(
delegate(object sender, DataReceivedEventArgs e)
{
// append the new data to the data already read-in
outputBuilder.AppendLine(e.Data);
}
);
// start the process
// then begin asynchronously reading the output
// then wait for the process to exit
// then cancel asynchronously reading the output
process.Start();
process.BeginOutputReadLine();
process.WaitForExit();
// use the output
response += outputBuilder.ToString();
if (process.ExitCode != 0)
response += '\r' + '\n' + "heat.exe exited with code: " + process.ExitCode;
process.CancelOutputRead();
return process.ExitCode;
}
I thought this worked..
It passed tests, it's been running for a while without problems, and then suddenly, a developer called, that the webtool I made, no longer produces wix xml for him..
When I logged into the server, I found this dialog:
and then clicked [OK] - the web application then continued, and produced the xml, and stuff worked..
I have now found the dll that, makes heat throw this error. It doesn't really need registering (typical right?). So I could probably just write a timeout thing, to kill heat.exe if it takes to long, and thus unlock the waiting script, (and basicly fix the issue untill it happens again with a dll that actually needs registering) But that is not really detecting the error, that is just detecting that stuff takes time...
On this error, I would like to continue the script, but present a warning to the user, that heat.exe failed to run on that particular file. But to do this I need my asp.net application to know that this error was invoked, and dispose it, so that the script can continue..
how the *? do I get information that this runtime error occurred, so I can handle it from the server script?
Have you tried using the -sreg command line option to heat?
I now have, and as a result, heat.exe no longer chrashes, but this is not a solution, as heat also avoids harvesting the registry information that I need for autoregistering the dll's shipped with the code in question.
Working with external "uncooperative" executables often requires some trickery. I'd try the following:
Start the program on the command line, and check if there is any output when the error occurs. Probably it writes to standard error, and you could use RedirectStandardError, read the stream and hopefully get a clue when the error occurs.
Check if there is any logging-possibility in heat.exe that you could enable, and use this to detect the error-case. Maybe a verbose setting, or a log-file...
If none of the above worked, I'd use process monitor (e.g. https://technet.microsoft.com/de-at/sysinternals/bb896645.aspx). Start process monitor and then your application and bring it to the point of error. Filter the enormous output in process monitor to just your application (which is still quite a lot) and search at the end, whether there is any access where the programm might log the error. Maybe some log-files, or a logging-service. You could check this file after your timeout.
But something the would work in any case is the hack you already suggested in your question. To detect whether the dialog opened. There are also possibilities to browse through the content of the dialog box, so you could also read the text and check which kind of error it is. I used this once in production code to get the progress of an external program, which was written in a text field inside the form. I used spy++ (bundled with Visual Studio) to get the name/id of the text field, and accessed it using the (native) windows API. An ugly hack, but worked fine unless the external program's UI is changed.
In your case it is a standard Error Dialog, so it should stay quite consistent.
I am creating a wpf application that needs a prerequisite. If that prerequisites is not met then I ask the user if he will like to install the prerequisite that happens to be: Microsoft Visual C++ 2010 SP1 Redistributable Package.
So if the user chose to install the prerequisite I will execute vcredist_x86.exe (that is the file that get's downloaded from the first link that I provided).
Then on my application I will be able to tell when the installation is complete by doing something like:
ProcessStartInfo psi = new ProcessStartInfo(#"vcredist_x86.exe");
var p = new Process(); p.StartInfo = psi;
p.Start(); //start the process
p.WaitForExit(); // wait for the installation to finish
// installation should be done now
Ok everything works great so far. The problem is that I have a progress bar in my wpf application and I will like to show the progress in there.
I have been able to show the progress of the installation by doing the following:
There is a program called AutoIt that it is great for automating simple tasks. For example I can easily detect if a window exists with autoit by using something like:
I could then compile that script and create a very small executable. In that executable I will return 1 if the specified window exists or 0 otherwise.
When the user moves to the next window my script might return 2 because that is a different window. AutoIt can also see the progress of a progress bar of a window!!! so if that script returns 80 for example then I will update my progress to 80% for instance.
I do that by doing something like:
// start the autoitExecutable....
// wait for executable to exit usually takes 10 miliseconds it is fast
if (autoitProcess.ExitCode == 1)
{
// do somthing
}else if(autoitProcess.ExitCode == 2)
{
// do something else
} //etc....
As you can see I have to execute that script every 1 second to detect what changes have been done in order to update my progress bar in WPF. That works but every time I execute that executable with c# I get the:
cursor for about 500 milliseconds then on the next second it appears again. That becomes annoying even though no windows show up. It will be nice if I could get rid of that cursor and execute that executable silently somehow. when I execute the autoit executable there are no windows that show up nor nothing else.
P.S.
I know I could use c# to check for the existance of a window and maybe see the value of other window's handler's just like autoit is able to do it but it is so simple to create those programs with AutoIt and it will be nice if I could use AutoIt instead of C# for this kind of taks
I saw this behavior when the exe was set to "windows application" rather than "console application".
Changing the type to console no longer gives a busy cursor at launch.
You could add an event handler as well for example
System.Diagnostics.ProcessStartInfo p = new
System.Diagnostics.ProcessStartInfo(#"vcredist_x86.exe") ;
p.Arguments="-RunForever";
proc = new System.Diagnostics.Process();
proc.StartInfo = p;
proc.EnableRaisingEvents = true;
proc.Exited += new EventHandler(myProcess_Exited);
proc.Start();
inside the event if I wanted to do something like this
// Inside the form class:
private System.Diagnostics.Process proc;
private void myProcess_Exited(object sender, System.EventArgs e)
{
button3.BackColor=Color.LightGreen; //success indicator
}
if you wanted to do this in a While Loop you could also do something like this for example
but you would have to change the params to fit your case
example code you can utilize
while(!autoitProcess.WaitForExit(someTimeout))
{
if(ShouldCancel)
{
break;
}
}
does this make sense or help out...?
I want to run a console application (eg app.exe) from a windows form load event.
I'v tried System.Diagnostics.Process.Start(), But after it opens app.exe, it closes it immidiately.
Is there any way that I can run app.exe and leave it open?
If you are just wanting the console window to stay open, you could run it with something like this command:
System.Diagnostics.Process.Start( #"cmd.exe", #"/k c:\path\my.exe" );
Try doing this:
string cmdexePath = #"C:\Windows\System32\cmd.exe";
//notice the quotes around the below string...
string myApplication = "\"C:\\Windows\\System32\\ftp.exe\"";
//the /K keeps the CMD window open - even if your windows app closes
string cmdArguments = String.Format("/K {0}", myApplication);
ProcessStartInfo psi = new ProcessStartInfo(cmdexePath, cmdArguments);
Process p = new Process();
p.StartInfo = psi;
p.Start();
I think this will get you the behavior you are trying for. Assuming you weren't just trying to see the output in the command window. If you just want to see the output, you have several versions of that answer already. This is just how you can run your app and keep the console open.
Hope this helps. Good luck.
If app.exe does nothing, or finishes its work quickly (i.e. simply prints "Hello World" and returns), it will behave the way you just explained. If you want app.exe to stay open after its work is done, put some sort of completion message followed by Console.ReadKey(); in the console application.
If you can change the code of app.exe, just add Console.In.Read() to make it wait for a key press.
app.exe can end with Console.ReadLine() assuming it too is a C# application where you control the source code.
You have one of two problems, given your master/slave application setup:
Your master app is opening, displaying a form, that form runs the slave app and closes immediately, even though the slave app is still running.
Your master app is opening, displaying a form, that form runs the slave app which closes immediately.
For the first problem, you need to wait/block for the process to complete (i.e. Process.WaitForExit().
For the second problem, it sounds like the slave app has done what it needs to (or thrown an exception) and is closing immediately. Try running it with the same parameters from a command prompt and check the output.
If you have control over app.exe, you should be aware of how it functions so I will assume that you do not have control over it's inner workings. In that case, you can try passing a help flag which may or may not give you more info on how to call app.exe. Try something like this:
private startApp()
{
string command = " -h"; //common help flag for console apps
System.Diagnostics.Process pRun;
pRun = new System.Diagnostics.Process();
pRun.EnableRaisingEvents = true;
pRun.Exited += new EventHandler(pRun_Exited);
pRun.StartInfo.FileName = "app.exe";
pRun.StartInfo.Arguments = command;
pRun.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal
pRun.Start();
pRun.WaitForExit();
}
private void pRun_Exited(object sender, EventArgs e)
{
//Do Something Here
}
Create a new text file, name it app.bat and put this in there:
app.exe
pause
Now have your form point to that bat file.
In your console application, type:
Console.ReadLine(); - Use this piece of code to wait until you press enter
Console.ReadKey(); - Use this code to wait until you press a key