Forum: PC-Programmierung Output einer Konsolenanwendung in GUI


von Karl-Heinz (Gast)


Lesenswert?

Hallo,

ich habe mit Visual Studio 2010 eine GUI erstellt. Durch Knopfdruck 
öffne ich ein Konsolenprogramm, also die Konsole geht ganz normal auf, 
das Programm läuft ab und dann schließt sie sich wieder.
Nun kam ich auf die Idee, dass die Konsole nicht angezeigt werden soll, 
sondern im Hintergrund läuft und die aktuelle Ausgabe in einem CEditfeld 
angezeigt wird. Hierzu rufe ich mein Programm mittels popen() auf und 
sende die Ausgabe an mein CEditfeld.
Allerdings erscheint die Ausgabe erst dann, wenn das Konsolenprogramm 
beendet ist. Weiß jemand wie ich die Ausgabe in Echtzeit in meine GUI 
bekomme?

Grüße

von Peter II (Gast)


Lesenswert?

Karl-Heinz schrieb:
> mittels popen()

open ist ein wenig altmodisch

CreateProcess ist das etwas flexibler:

http://msdn.microsoft.com/en-us/library/windows/desktop/ms682425(v=vs.85).aspx

von Karl-Heinz (Gast)


Lesenswert?

Mit Create Process hab ich es anfangs versucht, hatte eine pipe.h, eine 
pipe.cpp, eine command.h und eine command.cpp und eben die xxxdlg.cpp. 
Allerdings mit gleichem Effekt. Die Ausgabe erschien erst dann im 
Editfeld, wenn der Prozess beendet wurde.

von Dr. Sommer (Gast)


Lesenswert?

Mit CreateProcess gehts auf jeden Fall, man muss sich den stdout/stderr 
auf eine Pipe ausgeben lassen und von da abholen. Bei GUI-Anwendungen 
muss man das GUI-Framework anweisen ein Event (callback) zu generieren 
wenn Daten ankommen. Google einfach mal nach "Capture standard / console 
output" oder so, du bist nicht der erste der sowas macht.

von Peter II (Gast)


Lesenswert?

Karl-Heinz schrieb:
> Die Ausgabe erschien erst dann im
> Editfeld, wenn der Prozess beendet wurde.

dann hast du was falsch gemacht.

du musst die handels wie hStdOutput verwenden. Diese kann man dann 
auslesen.

von Karl-Heinz (Gast)


Lesenswert?

Peter II schrieb:
> Karl-Heinz schrieb:
>> Die Ausgabe erschien erst dann im
>> Editfeld, wenn der Prozess beendet wurde.
>
> dann hast du was falsch gemacht.
>
Ja logisch hab ich was falsch gemacht :D, ich frag mich halt die ganze 
Zeit was. Wenn ich zuhause bin kontrolliere ich mal meine Handles.

von Karl-Heinz (Gast)


Lesenswert?

So, ich hab es mit Createprocess() halbwegs hinbekommen, unter 
Zuhilfenahme einer RT-Konsole von codeproject.

Allerdings verstehe ich beim besten Willen nicht, weshalb dieser Code 
nicht den gewünschten Effekt bringt:
1
FILE *in;
2
char buff[512];
3
   
4
if(!(in = _popen("\"C:\\Users\\admin\\Documents\\Visual Studio 2010\\Projects\\out\\Debug\\out.exe\"", "rt")))
5
{
6
  // fehler
7
}
8
else
9
{
10
  m_out.SetWindowTextW(_T("start..."));
11
  m_out.RedrawWindow();
12
13
  while(fgets(buff, 512, in) != NULL)
14
  {
15
    m_out.SetSel(MAXLONG,MAXLONG);
16
    m_out.ReplaceSel(CString(buff));
17
  }
18
}
19
_pclose(in);

Beim Debuggen sieht man schön, dass die Ausgabe der out.exe schön 
zeilenweise eingelesen wird und immer nur eine Zeile im Puffer steht. 
Wieso wird die im Editfeld nicht direkt dargestellt?

von Michael A. (micha54)


Lesenswert?

Hallo,

kann es sein, dass Du bei der Ausgabe auch mal die Steuerung an die 
Steuerung an das GUI übergeben musst ?

Application.DoEvents fällt mir da spontan ein.....

Gruß,
Michael

von Karl-Heinz (Gast)


Lesenswert?

Wie meinst du das?

von bluppdidupp (Gast)


Lesenswert?

Die Frage ist jetzt, an welcher Stelle du den popen()-Kram aufrufst.
SetText, ReplaceSel, etc. versenden Windows-Nachrichten an das jeweilige 
Control.
Wenn das ganze in einem Button-Click-Handler passiert und nicht in einem 
eigenen Thread, kann es z.B. sein dass die Nachrichtenschleife dadurch 
blockiert wird.

von Karl-Heinz (Gast)


Lesenswert?

Ok danke. Mit Threads habe ich noch nie gearbeitet. Wie würde ein 
eigener Thread denn aussehen?

von Vlad T. (vlad_tepesch)


Lesenswert?

1
DWORD WINAPI deineFunktion( LPVOID i_par )
2
{
3
   ParameterFueDeineFunktion* par = (ParameterFueDeineFunktion*)i_par;
4
  // mach irgendwas
5
}
6
7
8
...
9
DWORD threadId;
10
ParameterFueDeineFunktion parameterFueDeineFunktion;
11
CreateThread(NULL, 0, &deineFunktion, &parameterFueDeineFunktion, 0, &threadId);

: Bearbeitet durch User
von Karl-Heinz (Gast)


Lesenswert?

Vlad Tepesch schrieb:
> DWORD WINAPI deineFunktion( ParameterFueDeineFunktion par)
> {
>   // mach irgendwas
> }
>
> ...
> DWORD threadId;
> ParameterFueDeineFunktion parameterFueDeineFunktion;
> CreateThread(NULL, 0, &deineFunktion, &parameterFueDeineFunktion, 0,
> &threadId);

Ok danke, kann ich den Thread dann ganz normal in meinem Button-Handler 
aufrufen?

von Vlad T. (vlad_tepesch)


Lesenswert?

hab noch ein paar Edits gemacht. Die Typen haben vorher nicht gestimmt.

Karl-Heinz schrieb:
> Ok danke, kann ich den Thread dann ganz normal in meinem Button-Handler
> aufrufen?
du kannst es probieren, sollte klappen. kann aber sein, dass du probleme 
bekommst, wenn du aus dem Thread versuchst an der Gui rumzufummeln.

: Bearbeitet durch User
von Karl-Heinz (Gast)


Lesenswert?

Also ich das hier so in die dlg.cpp eingefügt. Leider wird m_out als 
undefined deklariert:
1
DWORD WINAPI deineFunktion( LPVOID i_par )
2
{
3
  FILE *in;
4
  char buff[512];
5
6
  if(!(in = _popen("\"c:\\users\\admin\\documents\\visual studio 2010\\projects\\out\\debug\\out.exe\"", "r")))
7
  {
8
    // fehler
9
  }
10
  else
11
  {
12
    m_out.SetWindowTextW(_T("start..."));
13
    m_out.RedrawWindow();
14
15
    while(fgets(buff, 512, in))
16
    {
17
      m_out.SetSel(MAXLONG,MAXLONG);
18
      m_out.ReplaceSel(CString(buff));
19
20
      if (!AfxGetThread()->PumpMessage()) return;  
21
    }
22
  }
23
  _pclose(in);
24
}

von Vlad T. (vlad_tepesch)


Lesenswert?

is ja auch klar.

deineFunktion ist keine memberfunktion deines Objektes.
wenn du Daten des Objektes brauchst, musst du das Objekt (den 
this-Zeiger ) als Parameter übergeben. das hat natürlich auch zur folge, 
dass du nicht auf private member zugreifen kannst, sondern nur auf 
public (es sei denn du definierst die Funktion als friend.

Edit:
aber wie gesagt:
Zugriffe auf die Gui aus dem anderen Thread können problematisch sein.

: Bearbeitet durch User
von Karl-Heinz (Gast)


Lesenswert?

Ich bekomm ich nicht zum laufen. Wenn das die einzige Möglichkeit war, 
siehts schlecht aus. Trotzdem danke!

von bluppdidupp (Gast)


Lesenswert?


von Karl-Heinz (Gast)


Lesenswert?

Danke das Beispiel hat wirklich geholfen. Allerdings immer noch das 
gleiche Ausgangeproblem. Der Text wird erst angezeigt, nachdem die 
Konsolenanwendung beendet ist...

von Thomas M. (thomaswm)


Lesenswert?

Du kannst doch den "Standardoutput" eines Prozesses einfach "umbiegen".
1
private Process myProgram = new Process();
2
3
myProgram.StartInfo.UseShellExecute = false;
4
myProgram.StartInfo.RedirectStandardOutput = true;
5
myProgram.StartInfo.RedirectStandardError = true;
6
myProgram.StartInfo.RedirectStandardInput = true;
7
myProgram.StartInfo.CreateNoWindow = true;
8
...


dann einen Lesethread starten, der die Ausgabe immer zB in einer 
"TextBoxConsole" ausgibt.

Lesethread:
1
StreamReader myStreamReader = starterBatch.StandardOutput;
2
3
            try
4
            {
5
                string l;
6
                reader_running = true;
7
                while ((((l = myStreamReader.ReadLine()) != null)) && (reader_running==true) )
8
                {
9
                    textBoxConsole.AppendText(l + "\r\n");
10
                }
11
12
            }



...so habe ich das mal gemacht.

von bluppdidupp (Gast)


Lesenswert?

Das ist genau das was Peter II oben schon mit CreateProcess erwähnt hat.
Die .NET-Klasse Process ist ein Wrapper um CreateProcess (oder auch 
ShellExecuteEx), hier wird aber scheinbar kein .net-Framework sondern 
MFC eingesetzt.

Der CreateProcess-Weg ist auch hier beschrieben:
http://msdn.microsoft.com/de-de/library/ms682499.aspx

--

Hmm, also du kannst z.B. mit nem OutputDebugString() in der 
while-Schleife sehen, dass die while-Schleife auch läuft - aber auf der 
UI werden keine Änderungen sichtbar?

Was noch auffällt: Laut _popen-msdn-seite:
"If used in a Windows program, the _popen function returns an invalid 
file pointer that causes the program to stop responding indefinitely. 
_popen works properly in a console application."
Keine MS-Runtime oder Projekt auf Konsolenanwendung stehen? - Oder wieso 
kann das überhaupt funktionieren :D

von Karl-Heinz (Gast)


Lesenswert?

Da bekomme ich nur Fehlermeldungen, ist das eine Syntax für MFC?

von Karl-Heinz (Gast)


Lesenswert?

bluppdidupp schrieb:
> Hmm, also du kannst z.B. mit nem OutputDebugString() in der
> while-Schleife sehen, dass die while-Schleife auch läuft - aber auf der
> UI werden keine Änderungen sichtbar?

Genau, ein Breakpoint an die while-Bedingung, und bei jedem Durchlauf 
steht in buff die nächste Zeile...

Mit CreateProcess hab ich es die ganze schon versucht, aber auch ohne 
Erfolg.

von Karl-Heinz (Gast)


Lesenswert?

bluppdidupp schrieb:
> Hmm, also du kannst z.B. mit nem OutputDebugString() in der
> while-Schleife sehen, dass die while-Schleife auch läuft - aber auf der
> UI werden keine Änderungen sichtbar?

Achso Moment, nein du hast recht, mit OutputDebugString(CString(buff)) 
seh ich in meinem VS Output die Werte auch erst, wenn die 
Konsolenanwendung beendet ist.

von Karl-Heinz (Gast)


Lesenswert?

Kann es vielleicht daran liegen, das während die Konsolenanwendung 
läuft, die GUI nicht aktiv ist?

von bluppdidupp (Gast)


Lesenswert?

Sofern die Message-Loop des GUI-Threads (in der Regel der 1. Thread des 
Prozesses) nicht blockiert wird, wird auch die GUI "laufen"

Wird sie blockiert merkt man das unter neueren Windows-Versionen z.B. 
wenn man im Fenster rumklickt und Windows dann nach kurzer Zeit einen 
grauen Schleier über das Fenster legt, "(Keine Rückmeldung)" in der 
Titelzeile ergänzt und ggf. nach einer Weile ein Fensterchen mit "XYZ 
reagiert nicht mehr, [Programm schließen], [Auf Antwort des Programms 
warten]" anzeigt.
So wie z.B. hier zu sehen: 
http://www.drwindows.de/attachments/30674d1303736312-geloest-explorer-reagiert-nicht-mehr-unbenannt2.png

Das verhindert man in der Regel in dem man länger laufende Operationen 
in einem zweiten Thread unabhängig vom GUI-Thread laufen lässt und die 
GUI nur alle paar ms mal aktualisiert.

-------

Karl-Heinz schrieb:
> Achso Moment, nein du hast recht, mit OutputDebugString(CString(buff))
> seh ich in meinem VS Output die Werte auch erst, wenn die
> Konsolenanwendung beendet ist.

Ich hätte erstmal weiterhin die _popen()-Funktion in Verdacht der 
Übeltäter zu sein ;D

von Karl-Heinz (Gast)


Lesenswert?

Hab die popen() Funktion rausgeschmissen, war mir zu undurchsichtig, 
gehe jetzt wieder über CreateProcess und anschließend Readfile. 
Natürlich immer noch keine Rückmeldung der GUI solange die 
Konsolenanwendung läuft. Wenn du willst schick ich dir mal das Projekt, 
vllt fällt dir was auf. Bin mit meinem Latein echt am Ende -.-

von bluppdidupp (Gast)


Lesenswert?

vllt. einfach ne Mini-Version davon hier anhängen, dann könnte man mal 
drüberschauen.
(Ich hab aktuell leider keine C/C++ Umgebung installiert. C#/.net'ler 
;D)

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.