6

I'm trying to write a wrapper for an interactive console-based application. For this I use C# and the Process class. I'm trying to redirect stdin/out/err, but it doesn't work.

Example code:

ProcessStartInfo startInfo = new ProcessStartInfo("admin.exe");
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
startInfo.RedirectStandardInput = true;
startInfo.UseShellExecute = false;

Process process = Process.Start(startInfo);
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.OutputDataReceived += (s, e) => Console.WriteLine(e.Data);
process.ErrorDataReceived += (s, e) => Console.WriteLine(e.Data);

while (true)
{
    process.StandardInput.Write("uptime" + Environment.NewLine);
    process.StandardInput.Flush();
    Thread.Sleep(1000);
}
Console.ReadKey();

Nothing happens. But if I start admin.exe and write uptime, output is printed.

All solutions in the internet use ReadToEnd, but I can't use this because i have a dynamic communication on which I have to read stdout/err and write to stdin.

Has anyone an idea?

Update

I played with the posted zip on the linked thread. And then i tried to create a small 'proof-of-concept'-code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.IO;
using System.Threading; 

namespace ConsoleApplication3
{
    class Program
    {
        private static void Read(StreamReader reader)
        {
            new Thread(() =>
                {
                    while (true)
                    {
                        int current;
                        while ((current = reader.Read()) >= 0)
                            Console.Write((char)current);
                    }
                }).Start();
        }

        static void Main(string[] args)
        {
            ProcessStartInfo startInfo = new ProcessStartInfo(@"cmd.exe");
            startInfo.CreateNoWindow = true;
            startInfo.ErrorDialog = false;
            startInfo.RedirectStandardError = true;
            startInfo.RedirectStandardInput = true;
            startInfo.RedirectStandardOutput = true;
            startInfo.UseShellExecute = false;
            startInfo.CreateNoWindow = true;
            Process process = new Process();
            process.StartInfo = startInfo;
            process.Start();
            Read(process.StandardOutput);
            Read(process.StandardError);
            while (true)
                process.StandardInput.WriteLine(Console.ReadLine());
        }
    }
}

It works perfectly:-)

But with the admin.exe it doesn't work? The admin.exe doesn't use any tricky input-method and it doesn't need an inputed password.

I know the admin.exe is written in c and compiled with mingw on linux. So i created a small dummy-tool:

#include <stdio.h>

int main() {
        int readed;
        while ((readed = fgetc(stdin)) >= 0)
                fputc((char)readed, stdout);
}

This tool does only echo the inputed text/line. I compiled it with i586-mingw32msvc-gcc and copied it to my windows machine. There i used the program on the top of this post to communicate with the dummy.exe. It doesn't work. No echo is shown. But why?

I compiled the dummy-code also with the Microsoft C++ Compiler, same effect.

Update2

(btw: Thanks to Tim Post)

I'm trying and trying. I tried to create a c-Tool, which does the same as my c# tool. I used _popen, but the effect was, that the output were shown at the end of the process. Hm, not good.

I found this alternative command shell for windows:

https://stackoverflow.com/questions/440269/whats-a-good-alternative-windows-console

http://sourceforge.net/projects/console/

It seems to work. It gets the stdout/err in realtime, can redirect the stdin and admin.exe works. And it is opensource. May be i'll find the solution inside the C++-Code.

I'm not well in C++, so it's hard, but i'll try it. May be i have to write a "clear" redirect-wrapper in C/C++ and use it in C#.

If someone has an idea please say it, because the other way can be very hard (for me^^):-)

Thanks.

best regards

Update 3

Hm, i think this happens because the child-process (admin.exe) uses a few threads... But how to solve it?

Community
  • 1
  • 1
Stefan Hasler
  • 125
  • 1
  • 2
  • 5
  • I had exactly this same problem, and came up with a (somewhat kludgy) solution that you can find here: http://stackoverflow.com/questions/6655613/net-why-does-process-standardoutput-read-block-when-i-set-startinfo-redirectsta/6656360#6656360 . I believe that's got a link to a .ZIP with the full solution as well. – shelleybutterfly Aug 15 '11 at 21:53
  • Some programs are just cranky about input/output redirection. Admin.exe is a strong cue, it will want to read passwords from the true console. – Hans Passant Aug 16 '11 at 00:11
  • 1
    I've merged your unregistered accounts and converted your answer as an edit to your question, please check for accuracy. You should now have full control over your questions and answers. – Tim Post Aug 16 '11 at 20:13
  • You might be interested in [this post](http://www.codeducky.org/process-handling-net), which covers many of the intricacies of working with .NET processes, particularly around working with input and output. It recommends the [MedallionShell](https://github.com/madelson/MedallionShell) library, which simplifies dealing with process io streams – ChaseMedallion Aug 29 '14 at 11:29
  • What if you readonce char at a time? ReadLine will require pressing enter. while (true) { p.StandardInput.Write(Console.ReadKey()); } – osexpert Apr 05 '18 at 21:54

1 Answers1

1

The problem could be because of you are using Readline, where the data are output from admin.exe application are sequentially and not in new lines.., try to use Read instead, and build the desirable string from it..

Also you don't have to use Environment.NewLine to write string followed by new line, use WriteLine instead, so:

process.StandardInput.WriteLine("uptime");
Jalal Said
  • 15,906
  • 7
  • 45
  • 68
  • There is no ReadLine() in the snippet. – Hans Passant Aug 16 '11 at 00:10
  • Yes, I was referring to `BeginOutputReadLine` and so the `e.Data` in `OutputDataReceived` will just read lines, I think the same also apply to `BeginErrorReadLine` here.. – Jalal Said Aug 16 '11 at 22:20
  • 1
    You need to set UseShellExecute to true for the Verb to be respected and it must be set to 'false' to redirect standard output. You can't do both. I'm pretty sure Windows also won't allow you to redirect standard input/output/error across the admin/non-admin security boundary. You'll have to find a different way to get output from the program running as admin - Reference: http://stackoverflow.com/a/8690661 – Kiquenet Aug 28 '14 at 06:35