Laden...

Standard-Input, -Error und -Output gleichzeitig und "in Echtzeit" weiterleiten

Erstellt von B3nj vor 12 Jahren Letzter Beitrag vor 12 Jahren 2.971 Views
B3nj Themenstarter:in
242 Beiträge seit 2006
vor 12 Jahren
Standard-Input, -Error und -Output gleichzeitig und "in Echtzeit" weiterleiten

Hallo,

Ich habe eine exe-Datei, welche in C geschrieben ist. Dieses Programm ist Konsolenbasiert und stellt sowas wie ne primitive Shell zur Verfügung. Einfacher gesagt einige Kommandos. Diese Kommandos können Parameter und eine Ausgabe haben.

Ich möchte in C# nun einen (GUI-)Wrapper für das Ding schreiben.
Ich habe bereits sehr mit der Weiterleitung von StdErr/Out/In gekämpft. Das ganze habe ich mit der Process-Klasse versucht. Insofern ich nur stdout und stderr weitergeleitet habe ging das auch soweit, halt ohne input.

Als ich aber den Input auch weitergeleitet habe ging stderr/stout nicht mehr? Seltsamerweise funktioniert das alles ohne Probleme bei der "cmd.exe".

Bereits mit einem einfachen C-Tool, dass n'Endlosloop mit einem Echo der Eingabe hat gehts nicht mehr (Wieso auch immer??!).

Das Problem scheint bekannt zu sein:
Capture output of process synchronously (i.e. "when it happens")

Mit dem in diesem Thread verlinkten "ProcessRunner" habe ich das ganze für ein Test-Tool (Dummy-Ausgabe) hingekriegt. Jedoch nicht für mein Tool selbst.

In der Konsole "cmd.exe" kann ich das Tool problemlos ausführen. Wieso kann ich den Redirect aber nicht sauber machen?

Das ganze scheint komplizierter zu sein als es sich anhört? Die Problemstellung ist ja relativ einfach: stderr/out/in (in Echtzeit) weiterleiten.

Das Zieltool nutzt mehrere Threads und ich kann auch Einfluss darauf nehmen, falls da etwas geändert werden muss.

mfG
b3nj

-edit

Da die Beschreibung wahrscheinlich nicht optimal ist hier noch eine weitere Darstellung des Probelms.

Ein sinnloses Beispiel-Tool in C, das wahrscheinlich überhaupt nicht schön geschrieben ist:

#include <stdio.h>
#include <process.h>
#include <windows.h>

void __cdecl dummy_thread(void* dummy) {
  int cnt;
  for (cnt = 0;; cnt++) {
    printf("%4d  %4d\n", (int)GetCurrentThreadId(), cnt);
    Sleep(5000);
  }
}

int main() {
  int threads = 10;
  while (--threads) {
    _beginthread(dummy_thread, 0, NULL);
    printf("You typed '%c'.\n", fgetc(stdin));
  }
  printf("Stop typing!\n");
  dummy_thread(NULL);
  return 0;
}

Kompiliert habe ich es mit mingw. Auf jeden Fall enthält es Eingaben, Ausgaben und Threads.

Ein Beispiel-Code um die EIn und Ausgabe zu steuern, auch der ist nicht unbedingt schön, dafür aber sehr einfach:

        private static void PrcoessReader(Process p)
        {
            int readed;
            while ((readed = p.StandardOutput.Read()) > 0)
            {
                Console.Write((char)readed);
                Console.Out.Flush();
           }
        }
        
        static void Main(string[] args)
        {
            ProcessStartInfo startInfo = new ProcessStartInfo("a.exe");
            startInfo.RedirectStandardOutput = true;
            startInfo.RedirectStandardError = true;
            startInfo.RedirectStandardInput = true;
            startInfo.UseShellExecute = false;
            startInfo.CreateNoWindow = true;
            startInfo.ErrorDialog = true;
            Process process = new Process();
            process.StartInfo = startInfo;
            process.Start();
            new Thread(() => PrcoessReader(process)).Start();
            while (true)
            {
                process.StandardInput.WriteLine(Console.ReadLine());
                process.StandardInput.Flush();
                continue;
            }
            Console.ReadKey();
        }

Der funktioniert jedoch nicht.

Dann habe ich noch einen Anlauf-Versuch mit dem "ProcessRunner" gemacht:

        static void Main(string[] args)
        {
            ProcessRunner runner = new ProcessRunner("a.exe");
            runner.OutputReceived += (s, e) => 
                Console.Write(e.Data);
            runner.Start();
            while (true)
                runner.StandardInput.WriteLine(Console.ReadLine());
        }

Leider auch ohne Erfolg.

Wichtig ist noch, dass das ganze unter Windows XP laufen muss.

mfG

-edit 2-

Das quelloffene Programm Console für Windows, welches in C++ geschrieben ist verhält sich korrekt.

Nur leider bin ich in C++ nicht so der Hammer. Naja, ich versuche mal rauszufinden wies das macht. Bzw. was ich falsch mache.

Für Hilfe bin ich aber weiterhin offen und ich würde mich natürlich auch sehr freuen.

Vielen Dank.

mfG

B3nj Themenstarter:in
242 Beiträge seit 2006
vor 12 Jahren

Sorry für den Push, aber ich beisse mir noch immer die Zähne an dem Thema aus. Vielleicht suche ich viel zu weit weg?...

Das zuletzt genannte OSS-Projekt hat mir leider auch nicht wirklich weitergeholfen...

😦Naja, falls jemand ne andere einfache Lib kennt (c, c++, whatever) mit der ich einen Wrapper schreiben kann den ich dann in C# nutzen kann wäre ich auch schon zufrieden.

Einen weiteren push werde ich selbstverständlich nicht mehr machen.

Vielen Dank

mfG
b3nj

R
100 Beiträge seit 2009
vor 12 Jahren

@B3nj:

Insofern ich nur stdout und stderr weitergeleitet habe ging das auch soweit, halt ohne input.

Als ich aber den Input auch weitergeleitet habe ging stderr/stout nicht mehr? Seltsamerweise funktioniert das alles ohne Probleme bei der "cmd.exe".

Dann starten das Programm mit cmd.exe und In/Out/Error Kanal weiterleiten. Es sieht ungefähr so


cmdProc = new Process();
cmdProc.StartInfo.FileName = Environment.GetEnvironmentVariable("COMSPEC");
cmdProc.StartInfo.RedirectStandardInput = true;
cmdProc.StartInfo.RedirectStandardOutput = true;
cmdProc.StartInfo.RedirectStandardError = true;
//bla..bla....

System.IO.StreamWriter myWriter;
myWriter = cmdProc.StandardInput;
myWriter.AutoFlush = true;

myWriter.WriteLine("ipconfig.exe /all"); //Hier soll das Programm mit Parameters
myWriter.Close();

//bla...bla...

Ich habe nur ein asynchrones Beispiel C# – Read console output asynchronously . Vielleicht hilft es dir.

B3nj Themenstarter:in
242 Beiträge seit 2006
vor 12 Jahren

Hallo,

Danke für die Antwort. Funktionieren tut es aber leider nicht, vor allem darf ich den stdin nicht schliessen, da die Kommunikation dynamisch ist. Dafür habe ich eine andere einfache Lösung gefunden:

C equivalent of autoflush?

bzw.

setvbuf(stdout, NULL, _IONBF, 0)

Das bewirkt, dass stdout nicht gebuffert wird und dadurch funktioniert es. Natürlich funktioniert das nur wenn man das Zielprogramm beeinflussen kann, was ich glücklicherweise bis zu einem gewissen Grad kann.

Da stellt sich mir aber auch noch die Frage wie 'cmd.exe' das macht. Offensichtlich sind die Ausgaben erst mal in einem Buffer (welcher auch mit \n oder ähnlichem nicht geleert wird). Leert 'cmd.exe' den stdout-Buffer der aufzurufenden Programme selbst oder wie kommt das an den Inhalt? Darf ein programm überhaupt einen Filebuffer eines anderen Programmes einfach so flushen?

Es interessiert mich zwar, aber unbedingt wissen muss ich es nicht. =)

🙂Danke für die Hilfe. & schönes Wochenende

mfG