Die Community zu .NET und Classic VB.
Menü

VB 5/6-Tipp 0518: DOS-Programm starten mit Ausgabe in einer Datei

 von 

Beschreibung 

Es ist immer wieder mal nötig einen DOS-Befehl abzusetzen und das Ergebnis auszulesen. Hierfür gibt es mehrere Lösungen, wobei manchmal die einfachste Möglichkeit das Beste ist. Hier wird die Ausgabe eine Batchdatei in eine Datei umgelenkt und anschließend ausgelesen. Das DOS-Fenster wird unsichtbar gestartet und nach Abarbeiten der Befehle mit Terminateprocess geschlossen.
Zum Schluss wird die Ausgabe noch nach ANSI übersetzt, damit auch die Umlaute richtig dargestellt werden. Wer will, kann das übergehen.

Update von Peter Körner (): Diverse kleine Fehler wurden behoben. Außerdem kann nun auch in die Standarteingabe (STDIN) geschrieben werden.

Schwierigkeitsgrad:

Schwierigkeitsgrad 1

Verwendete API-Aufrufe:

CloseHandle, GetShortPathNameA (GetShortPathName), GetTempPathA (GetTempPath), OemToCharA (OemToChar), OpenProcess, TerminateProcess, WaitForSingleObject

Download:

Download des Beispielprojektes [4,77 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 "txtCommand"
' Steuerelement: Textfeld "txtOutput"
' Steuerelement: Schaltfläche "cmdGo"


'
' Autor K. Langbein (Klaus@ActiveVB.de)
' Beschreibung
' Es ist immer wieder mal nötig einen DOS-Befehl abzusetzen und das
' Ergebnis auszulesen. Hierfür gibt es einige Lösungen, aber
' manchmal ist eine einfache Möglichkeit das beste.
' Hier wird die Ausgabe eine Batchdatei in eine Datei umgelenkt
' und anschließend ausgelesen. Das DOSfenster wird unsichtbar gestartet
' und nach Abarbeiten der Befehle mit Terminateprocess geschlossen.
' Zum Schluss wird die Ausgabe noch nach ANSII übersetzt, damit auch
' die Umlaute richtig dargestellt werden. Wer will, kann das übergehen.

Option Explicit

Private Sub cmdGo_Click()
    txtOutput = ShellDos(txtCommand)
End Sub

Private Sub txtCommand_KeyDown(KeyCode As Integer, Shift As Integer)
    If KeyCode = 13 Then
        Call cmdGo_Click
    End If
End Sub


'---------- Ende Formular "Form1" alias Form1.frm  ----------
'--- Anfang Modul "modExecDosCommand" alias modExecDosCommand.bas ---
Option Explicit

'Er wurde Teilweise von mir (Peter Körner) verändert.


'
' Autor K. Langbein (Klaus@ActiveVB.de), Peter Körner (koerner-familie@t-online.de)
' Beschreibung
' Es ist immer wieder mal nötig einen DOS-Befehl abzusetzen und das
' Ergebnis auszulesen. Hierfür gibt es einige Lösungen, aber
' manchmal ist eine einfache Möglichkeit das beste.
' Hier wird die Ausgabe eine Batchdatei in eine Datei umgelenkt
' und anschließend ausgelesen. Das DOS-Fenster wird unsichtbar gestartet
' und nach Abarbeiten der Befehle mit Terminateprocess geschlossen.
' Zum Schluss wird die Ausgabe noch nach ANSII übersetzt, damit auch
' die Umlaute richtig dargestellt werden. Wer will, kann das übergehen.


Private Declare Function GetShortPathName Lib _
        "kernel32" Alias "GetShortPathNameA" (ByVal _
        lpszLongPath As String, ByVal lpszShortPath As String, _
        ByVal cchBuffer As Long) As Long

Private Declare Function CloseHandle Lib "kernel32" (ByVal _
        hObject As Long) As Long
        
Private Declare Function OpenProcess Lib "kernel32" (ByVal _
        dwDesiredAccess As Long, ByVal bInheritHandle As Long, _
        ByVal dwProcessId As Long) As Long
        
Private Declare Function TerminateProcess Lib "kernel32" (ByVal _
        hProcess As Long, ByVal uExitCode As Long) As Long

Private Declare Function WaitForSingleObject Lib "kernel32" _
       (ByVal hHandle As Long, ByVal dwMilliseconds As Long) _
       As Long
       
Private Declare Function OemToChar Lib "user32" Alias "OemToCharA" _
        (ByVal lpszSrc As String, ByVal lpszDst As String) As Long
        
Private Declare Function GetTempPath Lib "kernel32" Alias "GetTempPathA" _
        (ByVal nBufferLength As Long, ByVal lpBuffer As String) As Long
       
Private Const PROCESS_TERMINATE = &H1
Private Const BUFFER_LENGTH = 512
Private Const INFINITE = -1&
Private Const SYNCHRONIZE = &H100000

Public Function ShellDos(ByVal Cmd As String, Optional ByVal WorkingDir As String = ".", _
    Optional ByVal STDIN As String = "") As String

    Dim errflag As Long   ' verwenden wir um der Fehlerbehandlungs-
                          ' routine zu sagen, wo wir gerade sind
    
    Dim Batfile$          ' Unser Batchfile
    Dim DataFile$         ' Unser STDIN-DataFile
    Dim ReplyFile$        ' Unsere Ausgabedatei
    Dim t As Single       ' Allgemeine Zeitabfrage
    Dim l As Long         ' Dateilänge
    Dim Task As Long      ' TaskID
    Dim Result As Long    ' Für Rückgabewerte aus API-Funktionen
    Dim fno As Long       ' Dateinummer
    Dim TaskID As Long    ' Task-ID des DOS-Fensters
    Dim ProcID As Long    ' Prozess-ID des DOS-Fensters
    Dim TmpDir As String  ' Temporärer Ordner
    Dim tmp As String     ' Temporärer String
    
    TmpDir = String(BUFFER_LENGTH, 0)
    l = GetTempPath(BUFFER_LENGTH, TmpDir)
    TmpDir = Left(TmpDir, l)
    
    ReplyFile = TmpDir & "DOSReply.txt"
    DataFile = TmpDir & "DOSSTDIN.txt"
    
    ' Die Datei muss existieren, damit
    ' GetShortPathName Funktioniert.
    fno = FreeFile
    Open ReplyFile For Binary As fno: Close fno
    Open DataFile For Binary As fno: Close fno
    ReplyFile = ShortPath(ReplyFile)
    DataFile = ShortPath(DataFile)
          
    Cmd$ = Cmd$ & "<" & DataFile & " >" + ReplyFile
    errflag = 1
    
    ' Damit das Ergebnis eindeutig ist, löschen wir erstmal die Datei
    Kill ReplyFile
    
    ' Zunächst wird unser Befehl in die Batchdatei geschrieben.
    Batfile$ = TmpDir & "Batch.bat"
    
    Open Batfile$ For Output As #fno
    Print #fno, RootFromPath(WorkingDir)
    Print #fno, "cd " & WorkingDir
    Print #fno, Cmd$
    Close #fno
    DoEvents
    
    ' DOS wird mit der Batchdatei aufgerufen
    tmp = String(BUFFER_LENGTH, 0)
    l = GetShortPathName(Batfile$, tmp, BUFFER_LENGTH)
    Batfile$ = Left(tmp, l)
    TaskID = Shell(Batfile$, vbHide)
    
    DoEvents
    errflag = 2
    
    ProcID = OpenProcess(SYNCHRONIZE, False, TaskID)
    Call WaitForSingleObject(ProcID, INFINITE)
    
   
terminate:
    ' Hier wird DOS beendet
    Result = TerminateProcess(ProcID, 1&)
    Result = CloseHandle(Task)
    
    errflag = 3
    l = FileLen(ReplyFile)
    tmp = String(l, 0)
    Open ReplyFile For Binary As fno
    Get fno, , tmp
    Close fno
    ' ANSI -> ASCII
    Call OemToChar(tmp, tmp)
    ShellDos = tmp
    
    
    Kill Batfile
    Kill ReplyFile
    Kill DataFile
    
    errflag = 4
    
    Exit Function
    
err1:
    Select Case Err
    
    Case 53
    
        Select Case errflag
        
        Case 1
            Resume Next
        Case 3
            ShellDos = "<ERROR>"
            Exit Function
        Case Else
            Goto err_else
        End Select
        
    Case Else
    
err_else:
        MsgBox Error$
        
    End Select
End Function

Private Function RootFromPath(ByVal Path As String) As String
    RootFromPath = Mid(Path, 1, InStr(Path, ":"))
End Function

Private Function ShortPath(ByVal Path As String) As String
    Dim tmp As String     ' Temporärer String
    Dim l As Long         ' Länge des Strings
    
    tmp = String(256, 0)
    l = GetShortPathName(Path, tmp, Len(tmp))
    ShortPath = Left(tmp, l)
End Function
'--- Ende Modul "modExecDosCommand" alias modExecDosCommand.bas ---
'-------------- 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 6 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 am 05.01.2010 um 16:38

schönes beispiel, blickt aber keine sau durch

Kommentar von susowa am 03.05.2006 um 08:24

Wenn man die Zeile:

TaskID = Shell(Batfile$, vbHide)

durch folgende ersetzt:

TaskID = Shell(Environ$("COMSPEC") & " /C " & Batfile$, vbHide)

funktioniert der Tip reibungslos auch unter W98.
Getestet mit VB6 und W98SE, dort hing der Aufruf, da sich das DOS-Fenster defaultmäßig nicht wieder automatisch schließt.

Kommentar von CW am 17.12.2005 um 11:53

Mit befehl "ping -t localhost" hängt! :(

Kommentar von Patrick am 16.05.2005 um 11:21

@Protogenes: Hab es mal rasch ausprobiert, funktioniert.

Kommentar von Jonathan am 12.08.2004 um 15:10

Bei mir bleibt bei diesem tipp nur das gesammte programm hängen, wenn der befehl ausgeführt wird.

Kommentar von Protogenes am 12.01.2004 um 13:19

in DOS lässt sich die Ausgabe in eine Datei auch regeln, wenn man an den Befehl " >> [Datei]" anhängt
[Datei] ist die Zieldatei.
Ich habe es im DOS 6.22 schon so gemacht, geht bis ins 2k, in XP wahrscheinlich auch, konnte ich noch nicht testen.