Zurück zur Homepage

DOS-Programme an der Console starten

und Ausgaben der Programme von der Console lesen.
Um die Ausgaben von DOS-Programmen in einem VBA-Programm auszuwerten, wird normalerweise die Ausgabe in eine Datei umgeleitet, die man dann per VBA auswertet.
Das geht aber auch ohne diesen Umweg mit der API.
Zu beachten ist vielleicht noch beim Testen, dass der Kommandozeileninterpreter von NT und Win9x in unterschiedlichen Dateien steckt. Bei NT (XP) ist es die cmd.exe, unter 9x ist es die Command.com. Ich habe die Timeoutzeit, vor der das aufgerufene Programm beendet sein muss, auf 60 Sekunden gesetzt. Danach wird der Prozess rigoros gekillt. Bei länger dauernden Programmen muss deshalb die Zeit entsprechend hochgesetzt werden.
Das Beispiel listet Dateien mittels Dir und den Befehlszeilenparametern /S (für alle Unterverzeichnisse) und /B (für nur Dateinamen) in einem Tabellenblatt auf.

Beispieldatei (dateiliste_dos_dir.zip 24 KB)

 

Option Explicit
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

Private Declare Function TerminateProcess Lib "kernel32" _
    (ByVal hProcess As Long, ByVal uExitCode As Long) As Long

Private Declare Function CreateProcess Lib "kernel32" _
    Alias "CreateProcessA" (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 CreatePipe Lib "kernel32" ( _
    phReadPipe As Long, _
    phWritePipe As Long, _
    lpPipeAttributes As Any, _
    ByVal nSize As Long) As Long

Private Declare Function CopyFromPipe Lib "kernel32" _
    Alias "PeekNamedPipe" _
    (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 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 GetExitCodeProcess Lib "kernel32" _
    (ByVal hProcess As Long, lpExitCode As Long) As Long

Private Declare Function CloseHandle Lib "kernel32" (ByVal _
    hObject As Long) As Long

Private Declare Function CharToOem Lib "user32" Alias "CharToOemA" _
    (ByVal lpszSrc As String, ByVal lpszDst As String) As Long

Private Declare Function OemToChar Lib "user32" Alias "OemToCharA" _
    (ByVal lpszSrc As String, ByVal lpszDst As String) As Long

Private Const NORMAL_PRIORITY_CLASS = &H20&
Private Const STARTF_USESTDHANDLES = &H100&
Private Const STILL_ACTIVE = &H103&
Private Const SW_HIDE = 0
Private Const SW_MAX = 10
Private Const SW_MAXIMIZE = 3
Private Const SW_MINIMIZE = 6
Private Const SW_NORMAL = 1
Private Const SW_SHOWDEFAULT = 10
Private Const SW_SHOWMAXIMIZED = 3
Private Const SW_SHOWMINIMIZED = 2
Private Const SW_SHOWMINNOACTIVE = 7
Private Const SW_SHOWNA = 8
Private Const SW_SHOWNOACTIVATE = 4
Private Const SW_SHOWNORMAL = 1

Private Sub DosCommandTest()
'    MsgBox DosCommandResult("command.com  dir c:") '<> NT
    MsgBox DosCommandResult("cmd.exe /c dir c:") 'NT
End Sub

Public Function DosCommandResult(Kommando As String) As String
Dim MyProc As PROCESS_INFORMATION
Dim StartInf As STARTUPINFO
Dim SecAtt As SECURITY_ATTRIBUTES
Dim MyWritePipe As Long, MyReadPipe As Long
Dim ExitCode As Long, Verfügbar As Long
Dim Buffer As String
Dim MyTimeout As Date
With SecAtt
    .nLength = Len(SecAtt)
    .bInheritHandle = 1
End With
'Pipe anlegen, den nachher zur Ausgabe benutzt wird
If CreatePipe(MyReadPipe, MyWritePipe, SecAtt, 0) = 0 Then Exit Function
With StartInf 'Startattribute festlegen
    .cb = Len(StartInf)
    .wShowWindow = SW_HIDE
    .dwFlags = STARTF_USESTDHANDLES
    .hStdOutput = MyWritePipe
End With
'Prozess (Programm) starten
If CreateProcess(0&, Kommando, SecAtt, _
    SecAtt, 1&, NORMAL_PRIORITY_CLASS, _
    0&, 0&, StartInf, MyProc) <> 1 Then
        CloseHandle MyReadPipe
        CloseHandle MyWritePipe
        Exit Function 'Erfolglos
End If
'Zeitpunkt zum Timeout festsetzen
MyTimeout = Now + TimeSerial(0, 0, 60)
Do
    'Läuft Anwendung noch?
    GetExitCodeProcess MyProc.hProcess, ExitCode
    'Sind Zeichen im Lesepuffer?
    CopyFromPipe MyReadPipe, 0&, 0, 0, Verfügbar, 0
    If Verfügbar Then
        'Buffer groß genug machen
        Buffer = String(50000, 0)
        'Inhalt auslesen
        ReadFile MyReadPipe, Buffer, Len(Buffer), Verfügbar, 0&
        'Den Buffer auf dei Länge des geholten Textes Trimmen
        Buffer = Left$(Buffer, Verfügbar)
        DosCommandResult = DosCommandResult & Buffer
    End If
    'Wenn Anwendung nicht mehr läuft, beenden
    If ExitCode <> STILL_ACTIVE Then Exit Do
    If Now > MyTimeout Then
        'Wenn nichts mehr geht, Prozess nach 60 s
        ' mit DOS-Fenster zerstören

        TerminateProcess MyProc.hProcess, ExitCode
        Exit Do
    End If
    DoEvents
Loop
'OEM-Text nach Win-Ansi umwandeln
DosCommandResult = NachChar(DosCommandResult)
'Handles schließen
CloseHandle MyProc.hProcess
CloseHandle MyProc.hThread
CloseHandle MyReadPipe
CloseHandle MyWritePipe
End Function

Function NachChar(Quelle As String) As String
Dim Ziel$, Rück&
Ziel = String(Len(Quelle), 0)
Rück = OemToChar(Quelle, Ziel)
NachChar = Ziel
End Function

Function NachOem(Quelle As String) As String
Dim Ziel$, Rück&
Ziel = String(Len(Quelle), 0)
Rück = CharToOem(Quelle, Ziel)
NachOem = Ziel
End Function