I have a third party DOS process which writes data about its progress to the command line.
I want to react on the progress. Normally I would use a Process with RedirectStandardOutput = true and RedirectStandardError = true and then
.OutputDataReceived +=xyzOutputDataReceived;
.ErrorDataReceived += xyzErrorDataReceived;
.Start();
.BeginOutputReadLine();
.BeginErrorReadLine();
Normally this works. and I got what i need as DataReceivedEventArg.
In this case the process seems to update the same line it has written (how is that possible?), so it writes 15 %, 15% changes to 18% and so on. Only at the end of execution it seems that the data is flushed to StandardOutput.
Also if i just try to pipe data to a text file (eg odb.exe >> output.txt) it shows nothing.
Is there any way to get the temporary data?
The question is not about getting the Standard Output, this works fine (synchronously and asynchronously). It is about how to get output from a process which I cannot change, and which does not seem to flush it's output to the standard stream.
Like juharr says, you need to use Win32 to screen scrape the console.
Fortunately you don't need to write that code yourself. You can use the buffer-reader from this post: https://stackoverflow.com/a/12366307/5581231
The BufferReader reads from standardout. I suppose you are writing a wpf or winforms application so we'll also have to get a reference to the console window of the DOS application. For this, we will use the Win32 API call AttachConsole.
[System.Runtime.InteropServices.DllImport("kernel32.dll")]
private static extern bool AttachConsole(int pid);
I wrote a small example program that demonstrates the usage. It starts the exe and attaches to its console. It then scrapes the entire window once a second, and dumps the output to the debugger output window. You should be able to modify this to search the console content for any keywords etc. that you can use to track the progress of the program. Or you could dump it to a textfield or something in your UI, possibly after comparing it for changes?
var process = Process.Start(#"..path to your exe....");
//Wait for the DOS exe to start, and create its console window
while (process.MainWindowHandle == IntPtr.Zero)
{
Thread.Sleep(500);
}
//Attach to the console of our DOS exe
if (!AttachConsole(process.Id))
throw new Exception("Couldn't attach to console");
while (true)
{
var strings = ConsoleReader.ReadFromBuffer(0, 0,
(short)Console.BufferWidth,
short)Console.BufferHeight);
foreach (var str in strings.
Select(s => s?.Trim()).
Where(s => !String.IsNullOrEmpty(s)))
{
Debug.WriteLine(str);
}
Thread.Sleep(1000);
}
Good Luck!
Related
I am developing a C# component for Grasshopper for Rhino. As I am running some pretty heavy iterative analysis I would like to output results continuously to a cmd window just to make sure that the analysis is actually running.
Here's what I tried:
using System.Diagnostics;
Result results = new Result();
Process cmd = new Process();
cmd.StartInfo.FileName = "cmd.exe";
cmd.StartInfo.RedirectStandardInput = true;
cmd.StartInfo.RedirectStandardOutput = true;
cmd.StartInfo.CreateNoWindow = false;
cmd.StartInfo.UseShellExecute = false;
cmd.Start();
do {
results = RunHeavyOperation(results);
cmd.StandardInput.WriteLine("echo " + results.usefulInfo);
} while (!results.conditionForEnd);
cmd.WaitForExit();
Result RunHeavyOperation(Result previousResults) {
Result res = doHeavyStuff(previousResults);
return res;
}
I realise that I am missing part, but what is it?
Your approach is wrong: You currently don't write to a console window. Instead you created a process by starting cmd.exe and write to the standard input pipe of that process. cmd.exe is not aware of that. It's not the same as typing in a console via your keyboard, and even that can have strange effects.
Imagine you output a new line character, so cmd.exe might try to "execute" what you output before as a command.
The correct way is to invoke AllocConsole. With this call you can create a console window for your process and use it simply via Console.WriteLine().
When you finished your work and logging, you'll eventually need to close and free this console again via FreeConsole.
So import these two native methods:
internal sealed class NativeMethods
{
[DllImport("kernel32.dll")]
public static extern bool AllocConsole();
[DllImport("kernel32.dll")]
public static extern bool FreeConsole();
}
And use them in your code:
NativeMethods.AllocConsole();
// start work
Console.WriteLine("log messages...");
// finished work
NativeMethods.FreeConsole();
Note that FreeConsole() will close the console window, so all your log messages get lost. And a console only has a so large buffer and you can't scroll back to older messages if the leave the buffer.
So it may be a better idea to simply write your log messages into a file that you can analyze later.
Create a Console App in Visual Studio. In VS 2017 it looks like this (on the start page):
Console apps open a command console automatically. Then you can write to it with
Console.WriteLine("hello");
The console closes automatically when the program terminates. So make sure to call Console.ReadKey(); at the end, if you want to keep it open.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How do I show console output/window in a forms application?
Is there a way for a c# winforms program to write to the console window?
There are basically two things that can happen here.
Console output
It is possible for a winforms program to attach itself to the console window that created it (or to a different console window, or indeed to a new console window if desired). Once attached to the console window Console.WriteLine() etc works as expected. One gotcha to this approach is that the program returns control to the console window immediately, and then carries on writing to it, so the user can also type away in the console window. You can use start with the /wait parameter to handle this I think.
Start commsnd syntax
Redirected console output
This is when someone pipes the output from your program somewhere else, eg.
yourapp > file.txt
Attaching to a console window in this case effectively ignores the piping. To make this work you can call Console.OpenStandardOutput() to get a handle to the stream that the output should be piped to. This only works if the output is piped, so if you want to handle both of the scenarios you need to open the standard output and write to it and attach to the console window. This does mean that the output is sent to the console window and to the pipe but its the best solution I could find. Below the code I use to do this.
// This always writes to the parent console window and also to a redirected stdout if there is one.
// It would be better to do the relevant thing (eg write to the redirected file if there is one, otherwise
// write to the console) but it doesn't seem possible.
public class GUIConsoleWriter : IConsoleWriter
{
[System.Runtime.InteropServices.DllImport("kernel32.dll")]
private static extern bool AttachConsole(int dwProcessId);
private const int ATTACH_PARENT_PROCESS = -1;
StreamWriter _stdOutWriter;
// this must be called early in the program
public GUIConsoleWriter()
{
// this needs to happen before attachconsole.
// If the output is not redirected we still get a valid stream but it doesn't appear to write anywhere
// I guess it probably does write somewhere, but nowhere I can find out about
var stdout = Console.OpenStandardOutput();
_stdOutWriter = new StreamWriter(stdout);
_stdOutWriter.AutoFlush = true;
AttachConsole(ATTACH_PARENT_PROCESS);
}
public void WriteLine(string line)
{
_stdOutWriter.WriteLine(line);
Console.WriteLine(line);
}
}
I have a simple console app that writes out status of various resources in a nicely formatted way.
This app is using some 3rd party components to connect to places.
Those components unfortunately do a lot Console.Writes (or some sort of logging) which all ends up intertwined with my messages.
I tried redirecting Console output (in hopes that i could filter non-mine messages), but that seems to work ONLY on my messages.
var sb = new StringBuilder();
TextWriter tw = new StringWriter(sb);
Console.SetOut(tw);
So this works in redirecting console writes, but only on the ones i did.. output from 3rd party components is still streaming to my screen.. any other ways to supress that?
The reason you're not able to redirect the output of the third party components is because you're calling redirect on your process, not theirs. To do this, loop over the processes, find the ones that are writing to the console and redirect their output.
using System.Diagnostics;
Process[] processlist = Process.GetProcesses();
foreach(Process theprocess in processlist){
// you'll actually need to use something like the process name or id here.
if (theprocess == MyThirdPartyComponentsProcess)
{
theprocess.StartInfo.UseShellExecute = false;
theprocess.StartInfo.RedirectStandardOutput = true;
}
}
You'll have to be running as administrator for this work.
I've thought of one fairly hacky way:
Capture the existing Console.Out so you can reuse it in your "real" writer
Create your own TextWriter implementation, which...
... when it's asked to write, checks whether the calling code is in your assembly. See Assembly.GetCallingAssembly
So long as you don't have inlining problems, you should be able to check the context, and drop any output you don't want. You'd probably have to call Console.Out.WriteLine directly instead of Console.WriteLine, mind you - otherwise the caller will be the Console class.
A better solution would be to change your code to use log4net or something similar, so you could get your output in a more configurable way, not on the console - assuming your own use is also logging. (If it's for interactive output, something like the above may be the only way...)
Since, there doesn't seem to be a reasonable quick way to suppress these, I went with plan B, that is deferring my messages to the end. This way all the logging gets through, and than i write out all the information i am interested in.
I broke it down into Success/Warn/Error methods
public static void WriteError(string message, params string[] args) {
writerTasks.Add(() => {
var c = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(message, args);
Console.ForegroundColor = c;
});
}
and at the end of the execution, i call this method to flush it out.
private static void WriteResults() {
foreach (var t in writerTasks)
t();
}
i do it this way, because i am changing text color depending on message type, which is contained within each action.
in my case this works great, since the information scrolls up hiding all the logging.. which in fact is also beneficial.. just didn't want it dispersed between the main output
What I want to have happen is that the console window just goes away, or better yet that it is hidden, but I want my application to keep running. Is that possible? I want to be able to use Console.WriteLine and have the console serve as an output window. I want to be able to hide and show it, and I don't want the whole app to die just because the console was closed.
EDIT
Code:
internal class SomeClass {
[DllImport("kernel32")]
private static extern bool AllocConsole();
private static void Main() {
AllocConsole();
while(true) continue;
}
}
EDIT 2
I tried the accepted solution here [ Capture console exit C# ], per the suggestion in the comments on this question. The example code is bugged in that the DLLImport needs to be "kernel32.dll" or "kernel32", not "Kernel32". After making that change, I'm getting a message to my handler for CTRL_CLOSE_EVENT when I click the X on the console window. However, calling FreeConsole and/or returning true doesn't prevent the application from terminating.
Ah, yes, this is one of the caveats of using the Windows console subsystem. When the user closes the console window (regardless of how the console was allocated), all of the processes that are attached to the console are terminated. That behavior makes obvious sense for console applications (i.e., those that specifically target the console subsystem, as opposed to standard Windows applications), but it can be a major pain in cases like yours.
The only workaround that I know of is to use the SetConsoleCtrlHandler function, which allows you to register a handler function for Ctrl+C and Ctrl+Break signals, as well as system events like the user closing the console window, the user logging off, or the system shutting down. The documentation says that if you're only interested in ignoring these events, you can pass null for the first argument. For example:
[DllImport("kernel32")]
static extern bool SetConsoleCtrlHandler(HandlerRoutine HandlerRoutine, bool Add);
delegate bool HandlerRoutine(uint dwControlType);
static void Main()
{
AllocConsole();
SetConsoleCtrlHandler(null, true);
while (true) continue;
}
That works perfectly for Ctrl+C and Ctrl+Break signals (which would have otherwise caused your application to terminate as well), but it doesn't work for the one you're asking about, which is the CTRL_CLOSE_EVENT, generated by the system when the user closes the console window.
Honestly, I don't know how to prevent that. Even the sample in the SDK doesn't actually allow you to ignore the CTRL_CLOSE_EVENT. I tried it in a little test app, and it beeps when you close the window and prints the message, but the process still gets terminated.
Perhaps more worryingly, the documentation makes me think it is not possible to prevent this:
The system generates CTRL_CLOSE_EVENT, CTRL_LOGOFF_EVENT, and CTRL_SHUTDOWN_EVENT signals when the user closes the console, logs off, or shuts down the system so that the process has an opportunity to clean up before termination. Console functions, or any C run-time functions that call console functions, may not work reliably during processing of any of the three signals mentioned previously. The reason is that some or all of the internal console cleanup routines may have been called before executing the process signal handler.
It's that last sentence that catches my eye. If the console subsystem starts cleaning up after itself immediately in response to the user attempting to close the window, it may not be possible to halt it after the fact.
(At least now you understand the problem. Maybe someone else can come along with a solution!)
Unfortunately there's nothing you can do to really alter this behaviour.
Console windows are "special" in that they're hosted by another process and do not allow sub-classing. This limits your ability to modify their behaviour.
From what I know, your two options are:
1. Disable the close button altogether. You can do this with the following code fragment:
HWND hwnd = ::GetConsoleWindow();
if (hwnd != NULL)
{
HMENU hMenu = ::GetSystemMenu(hwnd, FALSE);
if (hMenu != NULL) DeleteMenu(hMenu, SC_CLOSE, MF_BYCOMMAND);
}
2. Stop using consoles altogether, and implement your own text output solution.
Option #2 is the more complicated option but would provide you the greatest control. I found an article on CodeProject that implements a console-like application using a rich edit control to display the text (rich edit controls have the ability to stream text like the console, so they are well suited to this sort of application).
On closing the console window obtained using AllocConsole or AttachConsole, the associated process will exit. There is no escape from that.
Prior to Windows Vista, closing the console window would present a confirmation dialogue to the user asking him whether the process should be terminated or not but Windows Vista and later do not provide any such dialogue and the process gets terminated.
One possible solution to work around this is avoiding AttachConsole altogether and achieving the desired functionality through other means.
For instance in the case described by OP, console window was needed to output some text on Console using Console static class.
This can be achieved very easily using inter-process communication. For example a console application can be developed to act as an echo server
namespace EchoServer
{
public class PipeServer
{
public static void Main()
{
var pipeServer = new NamedPipeServerStream(#"Com.MyDomain.EchoServer.PipeServer", PipeDirection.In);
pipeServer.WaitForConnection();
StreamReader reader = new StreamReader(pipeServer);
try
{
int i = 0;
while (i >= 0)
{
i = reader.Read();
if (i >= 0)
{
Console.Write(Convert.ToChar(i));
}
}
}
catch (IOException)
{
//error handling code here
}
finally
{
pipeServer.Close();
}
}
}
}
and then instead of allocating/attaching a console to the current application, the echo server can be started from within the application and Console's output stream can be redirected to write to the pipe server.
class Program
{
private static NamedPipeClientStream _pipeClient;
static void Main(string[] args)
{
//Current application is a Win32 application without any console window
var processStartInfo = new ProcessStartInfo("echoserver.exe");
Process serverProcess = new Process {StartInfo = processStartInfo};
serverProcess.Start();
_pipeClient = new NamedPipeClientStream(".", #"Com.MyDomain.EchoServer.PipeServer", PipeDirection.Out, PipeOptions.None);
_pipeClient.Connect();
StreamWriter writer = new StreamWriter(_pipeClient) {AutoFlush = true};
Console.SetOut(writer);
Console.WriteLine("Testing");
//Do rest of the work.
//Also detect that the server has terminated (serverProcess.HasExited) and then close the _pipeClient
//Also remember to terminate the server process when current process exits, serverProcess.Kill();
while (true)
continue;
}
}
This is just one of the possible solutions. In essence the work around is to allot the console window to its own process so that it can terminate without affecting the parent process.
You can do this by disabling keyboard mouse input by external program called Keyfreez.
you can use it multiple times in your program where no user input required. And if any user input require u can add a process Takskkill /f /IM .
https://www.sordum.org/7921/bluelife-keyfreeze-v1-4-block-keyboard-and-mouse/
Hope this helps all of you
I'm trying to put together a wrapper around a console application using StandardInput and StandardOutput. I'm getting stuck where the console application would prompt for input such as a password.
I'd like to read from StandardOutput, prompt the user using the read text, and write the user's input back to the console application using its StandardInput. Seems easy enough, here's what I have currently:
Process process = new Process()
{
StartInfo =
{
FileName = "bin\\vpnc.exe",
Arguments = "--no-detach --debug 0",
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardInput = true,
RedirectStandardOutput = true,
}
};
process.OutputDataReceived += (s, args) =>
{
textBlock1.Dispatcher.Invoke(new Action(() =>
{
textBlock1.Text += args.Data;
}));
};
process.Start();
process.BeginOutputReadLine();
The problem is that BeginOutputReadLine() is doing just that...waiting for a line ending. In this case it just sits, and sits, and sits because there is no line to read...the console application has written out text with no line ending and is waiting for input. Coincidentally, when I manually kill the process the event fires and I get the text.
Is there a way to tell that the process is waiting for StandardInput? Or am I missing a completely obvious way to accomplish the goal?
Unless you need something asynchronous you probably want ReadToEnd.
Here is a list of all StreamReader Methods
process.StandardOutput.BaseStream.BeginRead(...) is a potential substitute for your readline, and that will not wait for a line ending however you'd need to know what terminates the output to figure out when not to start wait for the next chunk of data
As Rune said, you can access directly to the output stream of the process (process.StandardOutput) and read from there (if you don't want to wait until a line break is entered into the console app), but this means that you need to check periodically for new data.
To interact with the application, you can just write to the StandardInput of the process (create a StreamWriter that writes to the process.StandardInput).
A nice sample of writing to it is on the MSDN documentation (http://msdn.microsoft.com/en-us/library/system.diagnostics.process.beginoutputreadline.aspx).
Hope this helps
You need to use the synchronous read method and handle any necessary threading yourself. The below code won't tell you that input is expected, but you will be able to detect that a prompt is displayed.
char[] b = new char[1024];
while (!process.HasExited) {
int c = process.StandardOutput.Read(b, 0, b.Length);
context.Response.Write(b, 0, c);
Thread.Sleep(100);
}