I'm trying to run dot net console application via Java:
process = Runtime.getRuntime().exec(commandLine);
I get the following output:
Detecting
The handle is invalid.
when running it directly via the console (windows) there is no problem:
Detecting
100%
Done.
100%
I'm running more applications in this form but have no problem .
Got this stack trace:
Detecting at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.Console.GetBufferInfo(Boolean throwOnNoConsole, Boolean& succeeded)
at System.Console.get_CursorTop()
at AutomaticImageOrientation.HelperClasses.General.WriteProgressToConsole(Int32 lastIndex, Int32 totalImages)
at AutomaticImageOrientation.MainManager.DetectImage(String[] files, String outputPath, String& globalErrorMessage, Dictionary`2& foundRotations)
The problem is when the .net app trying to write to the console What is the solution?
found the line that cause the problem:
Console.CursorLeft = 0;
Do you know why?
The console application is trying to set the cursor position for a console. This isn't possible, since there is in fact no console. All operations which don't result in a simple read or write are likely to cause errors when there is no console (since most of them require a console output buffer to work).
It is a bad idea to do stuff like setting the cursor position or clearing the screen in console applications you wish to automate. A basic workaround is to just put the offending statement in a try-catch, and discard the exception. From the MSDN page on System.Console:
You should not use the Console class
to display output in unattended
applications, such as server
applications. Similarly, calls to
methods such as Write and WriteLine
have no effect in Windows
applications.
Console class members that work
normally when the underlying stream is
directed to a console might throw an
exception if the stream is redirected,
for example, to a file. Consequently,
program your application to catch
System.IO.IOException if you redirect
a standard stream.
I ran into the same problem, only it was from running a c# console application via the SQL task scheduler.
I believe the problem is that some console methods and properties (Console.WindowWidth, Console.CursorLeft) are attempting to manipulate the console output, which isn't possible when the console is redirected.
I wrapped the section of the code in a simple try catch block, and it works fine now.
//This causes the output to update on the same line, rather than "spamming" the output down the screen.
//This is not compatible with redirected output, so try/catch is needed.
try
{
int lineLength = Console.WindowWidth - 1;
if (message.Length > lineLength)
{
message = message.Substring(0, lineLength);
}
Console.CursorLeft = 0;
Console.Write(message);
}
catch
{
Console.WriteLine(message);
}
Hard to diagnose without more detail - perhaps permissions... a little bit of exception handling (perhaps writing stack-trace to stderr) would help enormously. but not much help if you don't own the app.
If you don't get anywhere, you could try using reflector to see what the .NET app is doing during "Detecting" - it might help identify the cause.
I don't think there is a problem with your friend's program. You probably need to get the output stream of the process object you receive from Runtime.getRuntime().exec(commandLine), and call a read() method or something. It might work.
Try this to get the output stream of the call command
Runtime r = Runtime.getRuntime();
mStartProcess = r.exec(applicationName, null, fileToExecute);
StreamLogger outputGobbler = new StreamLogger(mStartProcess.getInputStream());
outputGobbler.start();
int returnCode = mStartProcess.waitFor();
class StreamLogger extends Thread{
private InputStream mInputStream;
public StreamLogger(InputStream is) {
this.mInputStream = is;
}
public void run() {
try {
InputStreamReader isr = new InputStreamReader(mInputStream);
BufferedReader br = new BufferedReader(isr);
String line = null;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
That can depend on how your Java application is being run. If your java application doesn't have console, when there can be problems when console presence is necessary for your inner process. Read this.
Related
I have a C# winform application in which an optimization model is solved by OR-Tools. The optimization solver has the capability of sending the whole optimization process as stdout.This is done by:
Slvr.EnableOutput();
Solver.ResultStatus restatus = Slvr.Solve();
However, the solver does not automatically open up the console.
Currently, what I have done is:
Projects properties --> Application --> Output type --> Console Application
and the console is ready from the beginning till the end of the application run. Hence, that process stdout is automatically displayed.
What I want is to open the console exactly when the above part of code is run and display the stdout from the solver. Then wait for a key from the user to close the console and continue with the main application.
I guess your problem is you are trying to run the solver as part of the Winforms application, inside the GUI process right? But Console output is usually disabled in a Winforms application. You have basically two options:
use one of the options described here in this older SO answer to attach a console window for output to a Winforms application
split the application into two exe files: one command line program which runs the solver, and a Winforms part, just containing the UI. Then run the command line part as a separate process by System.Diagnostics.Process.Start, which allows finegrained control about output redirection. You may need the UI to pass parameters to the command line program, for example, by using a temporary file.
The second option is more work, especially for the communication between the GUI and the command line tool, but can be implemented easier in a way the GUI is not blocked, is more robust against bugs / program crashes in the solver part and performs usually better in case you want to introduce parallelization / run multiple solver processes at once.
Doc Brown has already answered your question, I'm only adding this to provide some code of how we implemented it here-- it's exactly what he suggests. We have a separate testPlugin.exe that get's started here. The communication is via files read and written on the file system. The console output gets captured in the "output handlers"
using System;
using System.Diagnostics;
using System.IO;
...
private void startTest()
{
int result = 2;
setFormStatus("working..."); // My method to inform the user with the form to wait.
getFormData(); // My method to get the data from the form
string errorMessage = null;
System.Diagnostics.Process testPlugInProcess = new System.Diagnostics.Process();
try
{
using (testPlugInProcess)
{
testPlugInProcess.StartInfo.UseShellExecute = false;
testPlugInProcess.StartInfo.FileName = System.IO.Path.Combine(assemblyDirectory, TestPlugInExe); // The name of the exe file
testPlugInProcess.StartInfo.CreateNoWindow = false;
testPlugInProcess.StartInfo.Arguments = getModelTestCommandLineArgs(); // My method to create the command line arguments
testPlugInProcess.StartInfo.RedirectStandardError = true;
testPlugInProcess.StartInfo.RedirectStandardOutput = true;
testPlugInProcess.OutputDataReceived += pluginTestOutputHandler;
testPlugInProcess.ErrorDataReceived += pluginTestOutputHandler;
testPlugInProcess.Start();
testPlugInProcess.BeginErrorReadLine();
testPlugInProcess.BeginOutputReadLine();
testPlugInProcess.WaitForExit();
result = testPlugInProcess.ExitCode;
}
setFormStatus("");
}
catch (Exception ex)
{
errorMessage = ex.Message;
}
testPlugInProcess = null;
}
Both the console and error output get written to the same file here, but you could separate them.
The plug-in handler looks like this:
private static void pluginTestOutputHandler(object sendingProcess,
DataReceivedEventArgs outLine)
{
if (!String.IsNullOrEmpty(outLine.Data))
{
for (int i = 0; i < numberOfTriesForWriting; i++)
{
try
{
using (StreamWriter sw = File.AppendText(lastPlugInTestTraceFilePath)) // The file name where the data is written.
{
sw.WriteLine(outLine.Data);
sw.Flush();
return;
}
}
catch (IOException)
{
System.Threading.Thread.Sleep(msToWaitBetweenTries);
}
}
}
}
I'm writing a GUI for a third-party console application and I wanted it to capture the output of the console window and add it to a text box in the GUI. This seemed like it was simple, all I had to do was to redirect the output stream of the target process.
But, when I do that the console application throws the error:
CTextConsoleWin32::GetLine: !GetNumberOfConsoleInputEvents
The current code I have which causes this error is this:
// This gets called once after the application has initialized.
private void StartServer()
{
ProcessStartInfo processStartInfo = new ProcessStartInfo();
processStartInfo.FileName = srcdsExeFile;
processStartInfo.UseShellExecute = false;
processStartInfo.CreateNoWindow = true;
processStartInfo.RedirectStandardOutput = true;
processStartInfo.RedirectStandardError = true;
processStartInfo.RedirectStandardInput = true;
serverProcess = Process.Start(processStartInfo);
serverProcess.EnableRaisingEvents = true;
serverProcess.Exited += new EventHandler(Server_Exited);
serverProcess.OutputDataReceived += ServerProcess_OutputDataReceived;
serverProcess.ErrorDataReceived += ServerProcess_ErrorDataReceived;
serverProcess.BeginOutputReadLine();
serverProcess.BeginErrorReadLine();
}
// This is (like seen above) an event handler for serverProcess.ErrorDataReceived.
private void ServerProcess_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
Console.Output.WriteLine("\n\nServer Error: " + e.Data + "\n\n");
}
// This is (like seen above) an event handler for serverProcess.OutputDataReceived.
private void ServerProcess_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
Console.Output.WriteLine(e.Data);
}
The above code does work for a minute or so while the external application is doing its initialization, but crashes after a specific point in the initialization process.
After doing some research it turns out that the third-party console application relies on the output stream to be a console, which is why it crashes when I try to redirect it. Trying to access the output stream without redirecting it also causes an error saying I have to redirect it first.
Which brings me to my actual question:
Is it possible to read the output of the console application without redirecting the output stream?
So this has been asked several times in the past couple of years.
I just run into the same issue and solved it in C++ but the same technique should apply to any other programming language since this is a WinAPI specific problem.
I've describe a solution for anyone that wishes to create an srcds server using CreateProcess and redirect input & output on windows.
This github repo put together how Console Handls & Standard Handles work together inside windows.
https://github.com/rprichard/win32-console-docs
Also the microsoft documentation of
https://learn.microsoft.com/en-us/windows/console/creation-of-a-console
I highly recommend to read about this as this makes it very obvious why srcds fails when redirecting the standard input.
The problem
a) Windows Console Handle is not equal to StandardInput and Output Handles.
b) And in windows there is no way to redirect Console handles.
c) GetNumberOfConsoleInputEvents requires a valid console handle with an input handle that is not a file, pipe. It must be a ConsoleHandle!
Since no one actually looks at why GetNumberOfConsoleInputEvents fails while it should be obvious after reading the documentation.
https://learn.microsoft.com/en-us/windows/console/getnumberofconsoleinputevents
It clearly states:
hConsoleInput [in]
A handle to the console input buffer. The handle must have the
GENERIC_READ access right. For more information, see Console Buffer
Security and Access Rights.
But here
https://github.com/rprichard/win32-console-docs#allocconsole-attachconsole-modern
it's explained that when you redirect the pipe it pretty much breaks the console input buffer. So you have to actually work with the console input buffer and not with the StdHandles.
The solution
Luckily the WinAPI provides several options for us to get access to an existing process's std handles. It's just very tricky and not well documented!
You can attach to a Console and grab the STDHandles. Duplicate them and do whatever you like.
Note that AttachConsole(ProcessId) requires you that the current process has no console attached so you must call FreeConsole();
Here's a code how to send a single letter to an another application's console using WinAPI. You can also grab the console handle and write to it any time Using GetStdHandle.
int ProcessId = GetProcessId(ProcessInfo.hProcess);
if (ProcessId <= 0)
{
printf("Process terminated.\n");
break;
}
printf("Process Id: %d\n", ProcessId);
FreeConsole();
if (!AttachConsole(ProcessId))
{
printf("Attach failed with error: %d\n", GetLastError());
exit(1);
}
INPUT_RECORD ir[2];
ir[0].EventType = KEY_EVENT;
ir[0].Event.KeyEvent.bKeyDown = TRUE;
ir[0].Event.KeyEvent.dwControlKeyState = 0;
ir[0].Event.KeyEvent.uChar.UnicodeChar = 'u';
ir[0].Event.KeyEvent.wRepeatCount = 1;
ir[0].Event.KeyEvent.wVirtualKeyCode = 'U';
ir[0].Event.KeyEvent.wVirtualScanCode = MapVirtualKey('U', MAPVK_VK_TO_VSC);
ir[1].EventType = KEY_EVENT;
ir[1].Event.KeyEvent.bKeyDown = FALSE;
ir[1].Event.KeyEvent.dwControlKeyState = 0;
ir[1].Event.KeyEvent.uChar.UnicodeChar = 'u';
ir[1].Event.KeyEvent.wRepeatCount = 1;
ir[1].Event.KeyEvent.wVirtualKeyCode = 'U';
ir[1].Event.KeyEvent.wVirtualScanCode = MapVirtualKey('U', MAPVK_VK_TO_VSC);
DWORD dwTmp = 0;
WriteConsoleInputA(GetStdHandle(STD_INPUT_HANDLE), ir, 2, &dwTmp);
FreeConsole();
if (!AttachConsole(ATTACH_PARENT_PROCESS))
{
printf("Attach failed with error: %d\n", GetLastError());
exit(1);
}
So the solution is simply to to write to the Console Input Buffer by attaching to the console of an SRCDS process.
Simply call AttachConsole and FreeConsole. and WriteConsoleInput.
To read the ouptut you can just call ReadConsoleOutput
For further reading visit the documentation:
GetNumberOfConsoleInputEvents
https://learn.microsoft.com/en-us/windows/console/getnumberofconsoleinputevents
AttachConsole
https://learn.microsoft.com/en-us/windows/console/attachconsole
FreeConsole
https://learn.microsoft.com/en-us/windows/console/freeconsole
WriteConsoleInput
https://learn.microsoft.com/en-us/windows/console/writeconsoleinput
ReadConsoleOutput
https://learn.microsoft.com/en-us/windows/console/readconsoleoutput
I have a C# application which in turn loads a C or C++ dll (which in turn loads other C/C++ dlls). Within the C# application I use a log4net logger to capture all the output into a series of log files. My application runs as a windows service so there is no console/output window for normal printfs or output written into stdout/stderr to go to.
Is there a way to setup the C# application to direct stdout/stderr (from the DLLs) and turn each line into a log4net output. Or is there some way within the C/C++ DLL to connect the stdout/stderr streams to the log4net output?
I found some solution (here : http://bytes.com/topic/c-sharp/answers/822341-dllimport-stdout-gets-eaten) that indicated I needed to put a call into my C DLL like this : setvbuf(stdout, NULL, _IONBF, 0); Though, I don't know what that does, it doesn't do what I want. I assume I'd also need a similar line for stderr. In either case, google seemed to think those lines simply take care of buffering and not redirection into log4net.
I assume I need some sort of function override which snags the console writes (from a loaded DLL in another language) and converts them into mLog.InfoFormat("{0}", consoleString); sorts of calls. I'm new to c# and not even sure what terms to google in order to find such an override (if its even possible).
Not sure if this complicates the problem, but my C# application is multithreaded and some of the DLLs have multiple threads as well. I assume that just means I need a lock of some sort inside the method that handles the console output and writes it into the log4net framework(maybe) or maybe the normal serialization of log4net will handle it for me.
Turns out those did the trick once I figured out how to use them. I setup two named pipes(or two ends of the same pipe?). One I connected to stdout and had it do a log message in log4net of whatever came through the pipe.
internal static void InfoLogWriter(Object threadContext)
{
mLog.Info("InfoLogWriterthread started");
int id = Process.GetCurrentProcess().Id; // make this instance unique
var serverPipe = new NamedPipeServerStream("consoleRedirect" + id, PipeDirection.In, 1);
NamedPipeClientStream clientPipe = new NamedPipeClientStream(".", "consoleRedirect" + id, PipeDirection.Out, PipeOptions.WriteThrough);
mLog.Info("Connecting Client Pipe.");
clientPipe.Connect();
mLog.Info("Connected Client Pipe, redirecting stdout");
HandleRef hr11 = new HandleRef(clientPipe, clientPipe.SafePipeHandle.DangerousGetHandle());
SetStdHandle(-11, hr11.Handle); // redirect stdout to my pipe
mLog.Info("Redirection of stdout complete.");
mLog.Info("Waiting for console connection");
serverPipe.WaitForConnection(); //blocking
mLog.Info("Console connection made.");
using (var stm = new StreamReader(serverPipe))
{
while (serverPipe.IsConnected)
{
try
{
string txt = stm.ReadLine();
if (!string.IsNullOrEmpty(txt))
mLog.InfoFormat("DLL MESSAGE : {0}", txt);
}
catch (IOException)
{
break; // normal disconnect
}
}
}
mLog.Info("Console connection broken. Thread Stopping.");
}
Also have a function to push all that to another thread so it doesn't block my main thread when it hits the various blocking calls.
internal static void RedirectConsole()
{
mLog.Info("RedirectConsole called.");
ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(InfoLogWriter));
// TODO enqueue and item for error messages too.
}
I'm having trouble with it disconnecting and have to reconnect the pipes, but I'll figure out a reconnect solution. I'm guessing that happens when DLLs get swapped back out of memory or perhaps when I need to read but there isn't anything currently ready to be read? I've also got to setup another pair to snag stderr and redirect it as well, using Error logs for that one. Probably want to get rid of the magic number (-11) and use the normal enums as well (STD_ERROR_HANDLE, etc)
If I have these sprinkled throughout my code:
MessageBox.Show("See a format exception yet? #1");//todo: remove
(there are 7 of these, numbered from 1..7, most of which display (1,2,5,6,7))
I end up with one err msg ("Exception: Cannot find table 0 Location: frmFunction.SetPlatypus")
If I comment out all of those, I end up with a different err msg ("Exception: FormatException Location frmFunction.getDuckbillRecord")
How could this be? Shouldn't the existence/display of such an information msg have no effect on the way the code executes/the path it takes, etc.?
Note: getDuckbillRecord() is where all of the MessageBoxes are.
UPDATE
Using RT's suggestions as motivation, I came up with this:
public static StringBuilder LogMsgs = new StringBuilder();
public static void ExceptionHandler(Exception ex, string location)
{
try
{
LogMsgs.Append(string.Format("{0}\r\n", ex.Message)); //TODO: Comment out before deploying?
DateTime dt = DateTime.Now;
string timeAsStr = string.Format("{0}_{1}_{2}_{3}.txt", dt.Hour, dt.Minute, dt.Second, dt.Millisecond);
using (StreamWriter file = new StreamWriter(timeAsStr))
{
file.WriteLine(LogMsgs.ToString());
}
. . .
//in the problematic code, replacing the MessageBox.Show() calls:
TTBT.LogMsgs.Append("Made it to location 1 in frmOverFunction.GetDuckbillRecord()\r\n");
...and it did help - the exception is reached right after the first of 7 "code waypoints," so there's something rotten in that particular Denmark, apparently.
UPDATE 2
After the rare experience of being able to run the app without crashing somewhere, I realized I also need that file-writing code in the main form's OnClosing() event - being in the global exception handler had worked for awhile - I wasn't expecting a clean termination of the app to ever occur again, I guess.
I'd strongly encourage you NOT to use MessageBox.Show(...) to log/trace/debug errors, because popping-up a dialog can introduce many subtle bugs and changes in behavior, focus, etc., especially if your UI flow/state is important.
Instead, use System.Diagnostics.Trace for writing tracing code (which you can optionally compile into your production/release code) or System.Diagnostics.Debug to write debug messages which get removed when you build your release version.
For example:
public void DoSomething(int a, int b)
{
Trace.TraceInformation("Starting to do something: a = {0}, b = {1}",
a, b);
try
{
int c = a / b;
}
catch (DivideByZeroException e)
{
Debug.WriteLine("OH NO ... 'b' WAS ZERO!!!! RUN AWAY!!!!");
throw e;
}
Trace.TraceInformation("Done doing something");
}
In the example above, the debug output will be available in debug builds, but will be removed in release builds. Trace output will be available in debug and retail bugs (unless you turn off the TRACE build parameter) but introduces no measurable perf impact unless you hook-up a listener and start writing trace output to a slow medium like spinning disks, ticker-tape printers, etc. ;)
When you run code with Trace and/or Debug builds with Debug messages in Visual Studio, the messages are written to the Output pane so you can watch what's going on inside your app as it runs.
If you want to, you can also write a stand-alone trace listener that listens for Trace and/or Debug messages and log them to a file/database or display them in a console window (or GUI if you want to get fancy).
You can also use tools like log4net which enables you to write log/trace messages to a variety of file formats, databases, etc.
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