Win32: Ausgabe einer Konsolenanwendung "einfangen" - WIE?!

Rene Albrecht

Erfahrenes Mitglied
Hi @all,

ich habe folgende Frage:

wie fange ich die Ausgabe z.B. eines "ipconfig /all" über meine Anwendung ein?

Ich hatte vor, über ein CreateProcess das ipconfig zu starten und das Ergebnis von meinem Programm auszuwerten. Dabei fiel mir auf, dass ich in dem Struct STARTUPINFO für das CreateProcess zwei Handles angeben kann, die sich interessant anhören:
HANDLE hStdOutput
HANDLE hStdError

Habt Ihr vielleich einen Source rumliegen, bei dem Ihr das nutzt? Oder wie kann ich es ggf. besser lösen?

Gruß
René
 
Du hast einen möglichen weg schon richtig erkannt, diese HANDLE einträge sind für Pipehandles.

Du erstellt mit CreatePipe() ein pipe dessen Schreibhandle du als hStdOutput übergibst, und aus dem lesehandle kannst du dann die daten lesen, is nicht schwer. Hatte sowas selbst schon mal gemacht.

Diese methode empfehle ich aber nur wenn du bidirektionale kommunikation brauchst, das heißt du dem programm eingaben senden und die ausgaben verarbeiten willst. (hStdInput und hStdOutput belegt).


Alternativ und in dem fall wesentlich einfach ist die verwendung von _popen()
Dabei handelt es sich im eines der "alten" datei komandos. _popen öffnet ein pipe zu einem programm, aber nur unidirektion verwendbar

Anwendung:
FILE* returndata = _popen("ipconfig /all", "r");

returndata enthällt nun alle daten die ipconfig nach stdout schreibt, du brauchst sie jetzt lediglich noch mit fscanf oder fread oder fgets oder ähnlichem auszulesen.
 
Danke für die Info:
Code:
#define BUFSIZE 4096

STARTUPINFO	si;
PROCESS_INFORMATION	pi;
SECURITY_ATTRIBUTES sa;
HANDLE hPipeRead,hPipeWrite;
char cPipeResult[BUFSIZE];
DWORD dwBytes;

//
// Erstellen der Pipe
sa.nLength=sizeof(sa);
sa.bInheritHandle=TRUE;
sa.lpSecurityDescriptor=NULL;
CreatePipe(&hPipeRead,&hPipeWrite, &sa, 0);
//
// Erstellen des Prozesses
si.cb=sizeof(STARTUPINFO);
si.lpReserved=NULL;
si.lpDesktop=NULL;
si.lpTitle=NULL;
si.dwX=0;
si.dwY=0;
si.dwXSize=640;
si.dwYSize=400;
si.dwXCountChars=0;
si.dwYCountChars=0;
si.dwFillAttribute=FOREGROUND_BLUE;
si.dwFlags=STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
si.wShowWindow=SW_HIDE;
si.cbReserved2=0;
si.lpReserved2=NULL;
si.hStdInput=GetStdHandle(STD_INPUT_HANDLE);
si.hStdOutput=hPipeWrite;
si.hStdError=GetStdHandle(STD_ERROR_HANDLE);
CreateProcess(NULL, "ipconfig /all", NULL, NULL, TRUE, CREATE_DEFAULT_ERROR_MODE|NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
CloseHandle(pi.hThread);
WaitForSingleObject(pi.hProcess, 5000);
memset(cPipeResult,0,BUFSIZE);
ReadFile(hPipeRead,cPipeResult,sizeof(cPipeResult),&dwBytes,NULL);
...funktioniert! (Das nur für alle die eine ähnliche Frage haben)

Wo Du schon bei dem Thema bist: Wie bekomme ich jetzt noch eine bidirektionale Kommunikation hin? Nehmen wir an, ich setze anstelle von 'ipconfig /all' ein 'ftp'. Um flexibel zu sein, müßte ich dann in der Lage sein, über 'open' z.B. einen Server anzusprechen o.ä.

Wie bekomme ich das hin?

Danke nochmal
 
Wo Du schon bei dem Thema bist: Wie bekomme ich jetzt noch eine bidirektionale Kommunikation hin? Nehmen wir an, ich setze anstelle von 'ipconfig /all' ein 'ftp'. Um flexibel zu sein, müßte ich dann in der Lage sein, über 'open' z.B. einen Server anzusprechen o.ä.

Wie bekomme ich das hin?

Danke nochmal

Ich erlaube mir mal deinen beispielcode anzuwenden dafür

Code:
...
HANDLE hPipeOutRead,hPipeOutWrite;
HANDLE hPipeInRead, hPipeInWrite;
...
CreatePipe(&hPipeOutRead,&hPipeOutWrite, &sa, 0);
CreatePipe(&hPipeInRead,&hPipeInWrite, &sa, 0);
....
si.hStdInput=hPipeInRead;
si.hStdOutput=hPipeOutWrite;
si.hStdError=GetStdHandle(STD_ERROR_HANDLE);
CreateProcess(NULL, "ipconfig /all", NULL, NULL, TRUE, CREATE_DEFAULT_ERROR_MODE|NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
...

Nun kannst du aus hPipeOutRead die ausgegeben daten lesen und nach hPipeInWrite die einzugebenden daten schreiben und alles is um lot.
 
Das hab ich schon probiert... Mag auch gut funktionieren :) - leider bei mir nicht! :eek: Ich habe da wohl ein eher grundsätzliches Problem: Wie bekomme ich - bzw. meine Anwendung - mit, ob sich der Inhalt der Pipe geändert hat und es die Pipe neu auslesen muß?

Habe vorhin mal ein 'open xyz' mit WriteFile(hPipeInWrite...) in die Pipe geschickt, aber da hat sich nicht viel getan... :mad:
 
also zuerstmal is es so... wenn du die daten aus dem pipe abgerufen hast kannst du sie kein 2. mal bekommen.
Um zu sehen ob neue daten da sind brauchst du einfach nur von dem HANDLE lesen versuchen
If the return value is nonzero and the number of bytes read is zero, the file pointer was beyond the current end of the file at the time of the read operation.
Das heißt so viel wie du machst n ReadFile syscall und wenn die anzahl der gelesenen byte 0 ist dann sind keine daten hinzu gekommen, is sie größer 0 sind welche hinzu gekommen, und werden dir zurückgeben...

Bedenke das ein PIPE nicht seekable is.



Naja was soll sich auch viel tun, du bekommst ne rückgabe von dem programm die du erstmal abfragen musst bevor irgendwas anderes is oder etwa nich?

das hatt jedenfalls super fuktioniert damals... hatte ne Terminal server klasse programmiert der man einen programmnamen und ein socket objekt übergeben hatt, und dann hatt man das programm z.B. über telnet gesteuert.
 
Muß ich direkt mal ausprobieren, wenn ich wieder an MEINEN PC komme... werde das Ergebnis (den Source) bei Erfolg dann hier veröffentlichen. So haben dann Leute - die ähnliche Fragen haben - wenigstens die Möglichkeit, sich mal ein Beispiel dafür anzuschauen (und nicht nur die MSDN-Beiträge dazu durchzulesen)... ;)

Sollte es nicht funktionieren, folgt der nächste "Request" auf dem Fuße... :p
 
Zusatzfrage!

Ist es möglich, ein Event in meinem Programm zu erzeugen, wenn der durch CreateProcess erzeugte Kindprozess Daten in die Pipe geschrieben hat? (Ich möchte die Pipe nicht sequentiell nach X Sekunden auslesen/beschreiben, sondern vom Programm nur Daten aus der Pipe lesen lassen, wenn der aufgerufene Kindprozess dort auch wirklich welche hinterlegt hat)

Ich denke da an eine Methodik ähnlich den WinSocks. Wenn irgendwas auf dem erzeugten Socket passiert erzeugt Windows ein Event und gibt dieses an meine Anwendung weiter...

Sorry, dass ich das jetzt doppelt und dreifach versucht habe zu erklären... Brauche allerdings dringend Unterstützung und deshalb sollte die Fragestellung (grundsätzlich) eindeutig sein. :-(

Gruß
 
du meinst sowas wie assynchronmode bei sockets?

nein gibtes nicht soweit ich weiß. aber es gibt eine einfache möglichkeit das manuell zu realisieren.

erstell dir einen 2. thread der eine endlosschleife macht wo er abfragt ob schon was da is, und das pipe im synchronen blokierenden modus anspricht, das müsste wenn ich richtig informiert bin nämlich gehen, dann kann dir der thread ne nachricht geben.
 
ich bin noch ein wenig ungeschickt im umgang mit threads! habe erstmal folgendes problem:

Code:
switch (msg) {
case WM_INITDIALOG:
	SetWindowText(hwndDlg,"CreateProcess-Test");
	/*
	   Erstellen der Pipe
	*/
	sa.nLength=sizeof(sa);
	sa.lpSecurityDescriptor=NULL;
	sa.bInheritHandle=TRUE;
	CreatePipe(&hPipeOutRead,&hPipeOutWrite, &sa, 0);
	return TRUE;
case WM_COMMAND:
	switch (LOWORD(wParam)) {
	case IDOK:
		/*
		   Erstellen des Prozesses
		*/
		si.cb=sizeof(STARTUPINFO);
		si.lpReserved=NULL;
		si.lpDesktop=NULL;
		si.lpTitle=NULL;
		si.dwX=0;
		si.dwY=0;
		si.dwXSize=640;
		si.dwYSize=400;
		si.dwXCountChars=0;
		si.dwYCountChars=0;
		si.dwFillAttribute=FOREGROUND_BLUE;
		si.dwFlags=STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
		si.wShowWindow=SW_HIDE;
		si.cbReserved2=0;
		si.lpReserved2=NULL;
		si.hStdInput=GetStdHandle(STD_INPUT_HANDLE);
		si.hStdOutput=hPipeOutWrite;
		si.hStdError=GetStdHandle(STD_ERROR_HANDLE);
		CreateProcess(NULL, "ipconfig /all", NULL, NULL, TRUE, CREATE_DEFAULT_ERROR_MODE|NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
		/*
		   Auslesen der Pipe
		*/
		CloseHandle(pi.hThread);
		WaitForSingleObject(pi.hProcess, 5000);
		memset(cPipeResult,0,BUFSIZE);
		ReadFile(hPipeOutRead, cPipeResult, sizeof(cPipeResult), &dwBytes, NULL);
		SetDlgItemText(hwndDlg,IDTEXT,cPipeResult);
		break;
	case IDCANCEL:
		EndDialog(hwndDlg,0);
		return 1;
	}
	break;
case WM_CLOSE:
	EndDialog(hwndDlg,0);
	return TRUE;
}

ich war der Meinung, dass ich beim WM_INITDIALOG bereits die pipe erstellen kann. allerdings scheint das nicht zu funktionieren, denn das programm gibt nach Klick auf OK (IDOK) nichts aus.

erstelle ich die pipe direkt vor dem prozess (also mit im eventhandler für IDOK) funktioniert es...

hat jemand einen schimmer?
 
Zuletzt bearbeitet:
Zurück