Die Community zu .NET und Classic VB.
Menü

VB 5/6-Tipp 0272: Ein Konsolenfenster auslesen

 von 

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:

Schwierigkeitsgrad 3

Verwendete API-Aufrufe:

CloseHandle, CreatePipe, CreateProcessA, PeekNamedPipe, ReadFile, WaitForSingleObject

Download:

Download des Beispielprojektes [3,21 KB]

'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-VersionWin32sWin95Win98WinMEWinNT4Win2000WinXP
VB4
VB5
VB6

Hat dieser Tipp auf Ihrem Betriebsystem und mit Ihrer VB-Version funktioniert?

Ja, funktioniert!

Nein, funktioniert nicht bei mir!

VB-Version:

Windows-Version:

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