VB 5/6-Tipp 0272: Ein Konsolenfenster auslesen
von ActiveVB
Beschreibung
Hiermit kann die Standardausgabe einer Konsolenanwendung mittels einer sogenannten Pipe nach VB in einen String umgeleitet werden. Bei Anwendungen mit Standarteingabe versagt dieses Beispiel allerdings.
Update am 8. Februar 2004 nach einem Tipp von Achim Neubauer: Das Programm bleibt nun nicht mehr hängen nachdem eine etwas zeitaufwendigere Anwendung (z.B. ping.exe) gestartet wurde.
Update am 24. Februar 2004 von Jochen Wierum: Existiert eine Datei nicht bleibt das Programm nun nicht mehr hängen.
Schwierigkeitsgrad: | Verwendete API-Aufrufe: CloseHandle, CreatePipe, CreateProcessA, PeekNamedPipe, ReadFile, WaitForSingleObject | Download: |
'Dieser Quellcode stammt von http://www.activevb.de 'und kann frei verwendet werden. Für eventuelle Schäden 'wird nicht gehaftet. 'Um Fehler oder Fragen zu klären, nutzen Sie bitte unser Forum. 'Ansonsten viel Spaß und Erfolg mit diesem Source! '------------- Anfang Projektdatei Project1.vbp ------------- '--------- Anfang Formular "Form1" alias Form1.frm --------- ' Steuerelement: Textfeld "Text1" ' Steuerelement: Schaltfläche "Command1" Option Explicit Private Declare Function CreatePipe Lib "kernel32" (phReadPipe _ As Long, phWritePipe As Long, lpPipeAttributes As Any, _ ByVal nSize As Long) As Long Private Declare Function ReadFile Lib "kernel32" (ByVal hFile _ As Long, ByVal lpBuffer As String, ByVal _ nNumberOfBytesToRead As Long, lpNumberOfBytesRead As _ Long, ByVal lpOverlapped As Any) As Long Private Declare Function CreateProcessA Lib "kernel32" (ByVal _ lpApplicationName As Long, ByVal lpCommandLine As _ String, lpProcessAttributes As Any, lpThreadAttributes _ As Any, ByVal bInheritHandles As Long, ByVal _ dwCreationFlags As Long, ByVal lpEnvironment As Long, _ ByVal lpCurrentDirectory As Long, lpStartupInfo As Any, _ lpProcessInformation As Any) As Long Private Declare Function CloseHandle Lib "kernel32" (ByVal _ hObject As Long) As Long Private Declare Function PeekNamedPipe Lib "kernel32" (ByVal _ hNamedPipe As Long, lpBuffer As Any, ByVal nBufferSize _ As Long, lpBytesRead As Long, lpTotalBytesAvail As Long, _ lpBytesLeftThisMessage As Long) As Long Private Declare Function WaitForSingleObject Lib "kernel32" ( _ ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long Private Type SECURITY_ATTRIBUTES nLength As Long lpSecurityDescriptor As Long bInheritHandle As Long End Type Private Type STARTUPINFO cb As Long lpReserved As Long lpDesktop As Long lpTitle As Long dwX As Long dwY As Long dwXSize As Long dwYSize As Long dwXCountChars As Long dwYCountChars As Long dwFillAttribute As Long dwFlags As Long wShowWindow As Integer cbReserved2 As Integer lpReserved2 As Long hStdInput As Long hStdOutput As Long hStdError As Long End Type Private Type PROCESS_INFORMATION hProcess As Long hThread As Long dwProcessID As Long dwThreadID As Long End Type Const NORMAL_PRIORITY_CLASS = &H20& Const STARTF_USESTDHANDLES = &H100& Private Sub Command1_Click() Dim OutText As String Dim WithUml As String 'Hier den Pfad einer Batchdatei oder einer Konsolenanwendung 'die eine Ausgabe hat eintragen Text1.Text = ExecCmd("ipconfig.exe") End Sub Private Function ExecCmd(cmdline$) As String Dim proc As PROCESS_INFORMATION Dim start As STARTUPINFO Dim sa As SECURITY_ATTRIBUTES Dim hReadPipe As Long, hWritePipe As Long Dim L As Long, Result As Long, bSuccess As Long Dim Buffer As String Dim retText As String sa.nLength = Len(sa) sa.bInheritHandle = 1& sa.lpSecurityDescriptor = 0& Result = CreatePipe(hReadPipe, hWritePipe, sa, 0) If Result = 0 Then MsgBox "CreatePipe failed Error!" Exit Function End If start.cb = Len(start) start.dwFlags = STARTF_USESTDHANDLES start.hStdOutput = hWritePipe Result = CreateProcessA(0&, cmdline$, sa, sa, 1&, _ NORMAL_PRIORITY_CLASS, 0&, 0&, start, proc) If Result <> 0 Then '*** Anfang der Verbesserung Achim Neubauer*** Dim lPeekData As Long Do Call PeekNamedPipe(hReadPipe, ByVal 0&, 0&, ByVal 0&, _ lPeekData, ByVal 0&) If lPeekData > 0 Then Buffer = Space$(lPeekData) bSuccess = ReadFile(hReadPipe, Buffer, Len(Buffer), L, 0&) If bSuccess = 1 Then retText = retText & Left(Buffer, L) Else MsgBox "ReadFile failed!" End If Else bSuccess = WaitForSingleObject(proc.hProcess, 0&) If bSuccess = 0 Then Exit Do End If End If DoEvents Loop '*** Ende der Verbesserung Achim Neubauer *** Else MsgBox "Error while starting process!" End If Call CloseHandle(proc.hProcess) Call CloseHandle(proc.hThread) Call CloseHandle(hReadPipe) Call CloseHandle(hWritePipe) ExecCmd = retText End Function Private Sub Form_Load() Command1.Caption = "Start" End Sub '---------- Ende Formular "Form1" alias Form1.frm ---------- '-------------- Ende Projektdatei Project1.vbp --------------
Tipp-Kompatibilität:
Windows/VB-Version | Win32s | Win95 | Win98 | WinME | WinNT4 | Win2000 | WinXP |
VB4 | |||||||
VB5 | |||||||
VB6 |
Ihre Meinung
Falls Sie Fragen zu diesem Artikel haben oder Ihre Erfahrung mit anderen Nutzern austauschen möchten, dann teilen Sie uns diese bitte in einem der unten vorhandenen Themen oder über einen neuen Beitrag mit. Hierzu können sie einfach einen Beitrag in einem zum Thema passenden Forum anlegen, welcher automatisch mit dieser Seite verknüpft wird.
Archivierte Nutzerkommentare
Klicken Sie diesen Text an, wenn Sie die 34 archivierten Kommentare ansehen möchten.
Diese stammen noch von der Zeit, als es noch keine direkte Forenunterstützung für Fragen und Kommentare zu einzelnen Artikeln gab.
Aus Gründen der Vollständigkeit können Sie sich die ausgeblendeten Kommentare zu diesem Artikel aber gerne weiterhin ansehen.
Kommentar von Saldiray am 18.11.2009 um 14:01
Wenn der Consolenfenster nicht angezeigt werden soll muss ergänzt werden
start.dwFlags = STARTF_USESTDHANDLES Or _ STARTF_USESHOWWINDOW
start.wshowWindow=vbHide
Kommentar von cw2k am 12.11.2009 um 16:48
{Update: Bitte meinen vorherigen Kommentar komplett löschen}
E R G Ä N Z U N G :
WaitForSingleObject() blockiert leider das Programm, solang die ConsoleAPP zu tun hat. Für den User hat das dann den Anschein, das Programm hat sich aufgehängt.
Damit geht es besser:
...
Else
' bSuccess = WaitForSingleObject(proc.hProcess, 0&)
'
' If bSuccess = 0 Then
' Exit Do
' End If
Dim ExitCode&,GetExitCode_RetVal&
bSuccess = GetExitCodeProcess(proc.hProcess, ExitCode)
End If
DoEvents
' Loop
Loop While GetExitCode_RetVal And (ExitCode = STILL_ACTIVE)
... und noch die API's
Private Const STATUS_PENDING As Long = &H103
Private Const STILL_ACTIVE As Long = STATUS_PENDING
Private Declare Function GetExitCodeProcess Lib "kernel32.dll" (ByVal hProcess As Long, ByRef lpExitCode As Long) As Long
Und btw.
Call CloseHandle(proc.hProcess)
Call CloseHandle(proc.hThread)
ist unnötig (tut aber auch nicht weh) - ist halt eben nur vom Stil her nicht schön.
Kommentar von cw2k am 12.11.2009 um 15:49
E R G Ä N Z U N G :
WaitForSingleObject() blockiert leider das Programm, solang die ConsoleAPP zu tun hat. Für den User hat das dann den Anschein, das Programm hat sich aufgehängt.
Damit geht es besser:
' Waits until procress finishes and get its ExitCode
Do
Dim ExitCode&,
bSuccess = GetExitCodeProcess(proc.hProcess, ExitCode)
DoEvents
Loop While bSuccess And (ExitCode = STILL_ACTIVE)
... und noch die API's
Private Const STATUS_PENDING As Long = &H103
Private Const STILL_ACTIVE As Long = STATUS_PENDING
Private Declare Function GetExitCodeProcess Lib "kernel32.dll" (ByVal hProcess As Long, ByRef lpExitCode As Long) As Long
Und btw.
Call CloseHandle(proc.hProcess)
Call CloseHandle(proc.hThread)
ist unnötig (tut aber auch nicht weh) - ist halt eben nur vom Stil her nicht schön.
Kommentar von Ricky am 15.11.2007 um 13:18
Hi,
hab mich schon beinah tot gesucht nach so einer Funktion. @Tobi: Wie bekomme ich es denn noch hin, dass der Process nicht modal ausgeführt wird. Bisher liegt er ja noch in der Taskleiste.
Gruß
Ricky
Kommentar von Tobias Soltermann am 14.08.2007 um 21:35
Hi zusammen.
Für den Fall, dass das Konsolenfenster gar nicht angezeigt werden soll, kann "wShowWindow" auf False gesetzt werden.
Denkt daran, bei "dwFlags" das STARTF_USESHOWWINDOW ebenfalls hinzuzufügen, am besten mit einem der Operatoren "Or" oder "+"
Dazu muss man die Konstante STARTF_USESHOWWINDOW = &H1 definieren.
Hoffe, das nützt jemandem was.
Gruss Tobi
Kommentar von Philipp Stephani am 21.07.2007 um 11:36
Hallo Bruno,
der Code sollte so stimmen.
1. Die Handles werden doch geschlossen, ganz am Ende con ExecCmd. Ein Schließen direkt nach CreateProcess wäre unsinnig, weil man die Handles danach ja benutzt.
2. Eine Pipe hat immer ein Lese-Ende und ein Schreib-Ende, die durch zwei verschiedene Handles repräsentiert werden. Der gestartete Prozess muss in das Schreib-Ende schreiben und der Auslesecode muss aus dem Lese-Ende lesen.
Kommentar von Bruno M. am 20.07.2007 um 10:01
Hallo,
ich habe den Code in BlitzMax konvertieren wollen, bin aber auf einiges gestoßen.
1: muss man nicht, nachdem man CreateProcessA gemacht hat, die Pipes schließen? (CloseHandle(hReadPipe) und CloseHandle(hWritePipe)).
2: Das hier hat gar keinen Sinn:
PeekNamedPipe(hReadPipe, ByVal 0&, 0&, ByVal 0&, _
lPeekData, ByVal 0&)
, weil "ipconfig.exe" überhaupt nicht weiss, dass es in hReadPipe schreiben soll. Man hat nur das hier angegeben:
start.hStdOutput = hWritePipe
Demnach müsste man auch aus hWritePipe auslesen.
Mit freundlichen Grüßen,
Bruno M.
Kommentar von Philipp Stephani am 19.02.2006 um 18:49
Hallo, wenn du das in einem VB-Programm verwenden willst, musst du so etwas wie
Shell "cmd /c mem>C:\out.txt"verwenden.
Kommentar von stefan am 07.11.2005 um 12:52
hallo,
der Tip von Michel hört sich nach genau dem an, wonach ich suche, output in ein File.
kann mir allerdings jemand genau erklären, wohin genau ich das
cmd /c mem > LW:\out.txt
setzen muss, damit der output von mem nach out.txt geschrieben wird?
danke
Kommentar von MirekOh am 11.08.2005 um 19:01
Hallo,
der Tip funktioniert ganz gut...
Aber ist es möglich, das das Konsolenfenster nicht angezeigt wird! Bei mir gibt es einen kurzen fpacker effekt...
Kommentar von Michel am 24.02.2005 um 19:39
Hi , ich schreibe zwar nicht in vb aber ich hatte auch dieses Problem des auslesend eines konsolenfensters.Pipe habe ich versucht und gebastelt. Lesen und schreiben ist dabei eben nicht voll systemkonform und führt ggf. zu Problemen. Vielleicht zu einfach für unsere denke aber effektvoll, den command selber benutzen !
<= Win98 : command /c mem >LW:\DIRECTORY\mymem.txt
=> Win2000 : cmd /c mem >LW:\DIRECTORY\mymem.txt
Natürlich kann man diesen Befehl einbinden in CreateProcess, damit kein einziges fenster aufpoppt. Prozess beendet sich dann auch selber. Danach hat man ein wunderbares file zum auslesen und bearbeiten. Vorteil ist, auch wesentlich grössere konsolen wie zB. CabArc L test.cap etc. Lassen sich auf diese art&weise sehr schön bearbeiten und es muss nicht jedesmal nachgerechnet werden wieviel buffer noch auszulesen ist etc.
Tschü mit ü...
Kommentar von Olli am 09.08.2004 um 11:15
Hallo,
hat prima funktioniert. Nur wie kann ich verhindern, dass das Konsolenfenster immer kurz in den Vordergrund springt?
Am liebsten würde ich es garnicht sehen. *g*
Grütze,
Olli
Kommentar von Mave am 22.04.2004 um 09:57
Hallo Leute,
die Funktion ist echt super, hab aber ein Problem:
Das is mein Befehl:
ping heise.de | find "Mittelwert"
in der Eingabeaufforderung funktioniert es einwandfrei, aber in der Funktion funktioniert es nicht mehr, die Rückgabe ist immer leer.
Hab es folgendermaßen ausprobiert:
Text1.Text = ExecCmd("ping heise.de | find ""Mittelwert"" ")
und auch so:
Text1.Text = ExecCmd("ping heise.de | find " & chr$(34) & "Mittelwert" & chr$(34))
auch die OemToChar Funktion hat nichts gebracht.
Kommentar von Yammi900 am 26.02.2004 um 11:37
Das CMD-Fenster arbeitet ASCII-Zeichen, VB mit ANSI / Unicode, diesterhalb und desterwegen sollte man die zurückgelieferte Zeichenkette noch - wegen unserer Umlaute - konvertieren.
nötige Deklaration:
Private Declare Function OemToChar Lib "user32" Alias "OemToCharA" (ByVal lpszSrc As String, ByVal lpszDst As String) As Long
Aufruf:
OemToChar retText, retText
ExecCmd = retText
End Function
Kommentar von Hubert Weiskopf am 07.02.2004 um 17:50
Hallo,
gibt es eine möglichkeit stdin auszulesen.
Ich brauche den Fortschritt eines dos tools
pscp das den Fortschritt des Transfers in stdin schreibt.
Danke
Kommentar von MVXA am 28.12.2003 um 23:43
Hi
I know that named pipe need Windows NT based System like Win XP or other NT systems
Kommentar von Mario am 19.12.2003 um 22:56
Hi, sorry to writing in english, but i have a problem. When i execute this procedure in Windows 98, it don't work. I am trying install MSDE through VB6 and i want know when the instalation have finished. Can you help me?
Thanks....
Kommentar von condi am 30.10.2003 um 16:36
betreffend ä,ö,ü,|, etc...
probiert mal die CharToOem-api
Kommentar von condi am 29.10.2003 um 13:59
wenn ihr eigene consolen-programme als pipe-enden haben wollt, darf man nicht AllocConsole aufrufen sondern direkt GetStdHandle. Ich habs wie folgt realisiert (eh simpel :)
hOut = GetStdHandle(STD_OUTPUT_HANDLE)
hIn = GetStdHandle(STD_INPUT_HANDLE)
If hOut = INVALID_HANDLE_VALUE Then
IsCon = True
AllocConsole
hOut = GetStdHandle(STD_OUTPUT_HANDLE)
hIn = GetStdHandle(STD_INPUT_HANDLE)
End If
COut "Hallo Welt!" & Chr(13) & Chr(10)
If IsCon Then FreeConsole
ps: ReadConsole uä funktionieren der doku zufolge auch nicht dh: nur Read/WriteFile (eh wie im tip) verwenden.
Kommentar von Jan am 24.09.2003 um 06:43
Tipp:
Wenn ihr kein aufpoppendes Konsolenfenster wollt,
setzt
start.dwFlags = STARTF_USESTDHANDLES Or STARTF_USESHOWWINDOW
(USESHOWWINDOW hat den Wert &H1), und start.wShowWindow = 0
Kommentar von Dominic Wyss am 16.09.2003 um 19:59
Ich habe mir diesen Tipp angeschaut, und er funktioniert auch ganz nett, nur ein Problem habe ich noch:
Sondertzeichen wie ä,ö,ü,|,etc werden nicht richtig angezeigt, sondern nur als schwarze Kästchen.
Kommentar von Jonathan Haas am 25.08.2003 um 17:19
Sehr gut, der Tipp. Allerdings hab ich immernoch Probleme:
Meine DOS-EXE schreibt Buchstaben in einer Zeile als eine art Fortschrittsanzeige, und führt Berechnungen dazwischen durch. Irgendwie kriegt das Programm das nicht mit und gibt die Zeile an der grade gearbeitet wird erst aus, wenn das Programm mit der nächsten Zeile anfängt.
Wie kann ich es so machen, dass der Fortschrittsbalken richtig ausgelesen wird?
Kommentar von Jacek Łaniecki am 01.08.2003 um 13:13
I have modified the project in such a way, that in Command1_Click() I have entered the following command:
ExecCmd ("nbtstat -A " & txtIP.Text)
This is because I wanted to receive MAC address, which is the last line of the nbtstat output.
Then, I check if the answer really have "MAC" string.
It is strange, but sometimes it works, while sometimes not.
Do you have any ideas why?
Regards
Kommentar von webhuhn am 09.02.2003 um 17:21
Hallo!
ich hab das gleiche problem , mittendrin bricht die textausgabe auf einmal ab!
nur das andere (von dominik) tuts noch weniger, das läuft überhaupt nicht...!
wer kann mir was funktionieren code per mail schicken?
danke
Kommentar von Fipes am 02.12.2002 um 12:25
An sich funktioniert es gut, aber wenn ich einen ping befehl gebe bekomme ich nicht das, was mir cmd wiedergegeben hätte..also ich bin am router und der gibt mir meine ip und so'n scheiss aber der pingt nicht die ip, die ich angegeben habe, und genau das suche ich....
thx
Fipes
Kommentar von Mario Ariyoshi am 20.08.2002 um 13:31
Als totaler VB Einsteiger sagt mir "WaitForSingleObject"
natürlich nichts.
Toll wäre der dazu vollständige Code
oder eine Erklärung.
Ich versuche mit dem
Beispiel eine Archiv zu entpacken und den
Vorgang im Textfenster
darzustellen. Irgendwann zwischendrin endet die Textübergabe an das Textfeld.
Vielen Dank
Kommentar von Lightbringer am 14.06.2002 um 08:55
Es funktioniert zwar aber bei einigen Befehlen wie z.B arp stürtzt das Programm häufig ab
Kommentar von Marc Ermshaus am 26.03.2002 um 01:09
Ist es auch möglich einen erweiterten Kommandozeilenaufruf à la "Starte Datei x mit Programm y und kompiliere sie nach Datei z, wobei du die Optionen -a und -b verwendest" einzubauen? Also praktisch so wie mit ShellExecute (Arbeitsverzeichnis, usw.)?
Kommentar von Piggebu am 13.01.2002 um 12:47
Also das mit WaitForSingleObject ging gut, ich hatte jedoch ein ganz anderes Problem. Bei den meisten Befehlen gings reibungslos, aber z.B. bei "net view" gabs im DOS-Fester immer eine Fehler Meldung wie "Diese Anwendung verursache eine Schutzverletzung. Alle Programme schliessen und Computer neu Starten...etc" Da ich relativ ratlos war spielte ich ein wenig im Code rum und als ich "sa.bInheritHandle" auf False gesetzt habe gings. Bis jezt jedenfalls...
Für Erklärungen bin ich dankbar.
Kommentar von PhilippVB am 16.11.2001 um 22:49
Bei mir funtkioniert es mit WaitForSingleObject perfekt. Ich verwende sogar eine Puffergröße von 4096 Bytes. Natürlich muss WaitForSingleObjet vor ReadFile/ReadConsole ausgeführt werden, falls das irgendwem unklar war.
Kommentar von ALX am 29.08.2001 um 17:08
Der Unterschied ist klar: Im ActiveVB-Tip versucht man 200000 Bytes auf einmal auszulesen; in Deinem Link macht er das in der Schleife mit Portionen à 256 Bytes. Danke für den Hinweis!!
Kommentar von Dominik Deobald am 16.08.2001 um 16:47
Ich hab nicht genau rausgefunden, wo der Unterschied liegt, aber hier gibt's eine 100% funktionierende Variante, die alles einfängt:
http://pdhut.www5.50megs.com/vbzone/articles/capturing_output_of_shelled_program/capturing_output_of_shelled_program.htm
CU,
Dominik
Kommentar von Dominik Deobald am 16.08.2001 um 16:38
Hi!
Gleiches Problem mit der ersten Zeile nochmal, allerdings von mir mit mehr Infos: Die erste Zeile wird immer zurückgeliefert. Scheinbar ist die Sub dann aber schon wieder zu Ende, der Task wird nicht weiter umgepiped. Wenn man im Singlestep-Modus durchgeht, dann hat das Programm genug Zeit, um mehr Output zu produzieren. Das ergebnis: Der komplette Output wird abgefangen und kann weiterverwendet werden.
Wenn ich jetzt immer das gleiche Programm ausführen wollte, dann wäre das okay, dann könnte ich einfach eine Zeit lang warten - aber ich weiß nicht, wie lange meine Exec laufen sollen, daher entfällt diese Möglichkeit. Ich habe es statt dessen mit WaitForSingleObject(proc.hProcess, INFINITE) probiert, aber das hat gar nichts geändert. Irgendwelche sachdienlichen Hinweise?
CU,
Dominik
Kommentar von helmut am 09.08.2001 um 11:07
hallo,
ich bin ein super anfänger was vb angeht und habe folgende frage:
wie kann man einem dos-programm werte schicken und ein ergebnis zurückbekommen, z. b. "c:\prog1.exe" addiert wert1 und wert2 und liefert summe1 zurück. leider krieg ich das nicht hin! für jede hilfe wäre ich dankbar!!
helmut