TL;DR —
Shellstartet ein Programm und kehrt sofort zurück, bevor dieses Programm irgendetwas getan hat. Es reicht eine Task-ID (einenDouble) heraus, nicht einen Exit-Code, eine Ausgabe oder ein Erfolgssignal, und die nächste Zeile Ihres Makros läuft, während das Programm noch startet. Wenn Sie auf das Programm warten oder lesen müssen, was es erzeugt hat, istShelldas falsche Werkzeug — nehmen SieWScript.Shell(.Runmit Warten, oder.Exec).
Sub ShellDemo()
Dim taskId As Double
taskId = Shell("notepad.exe", vbNormalFocus) ' liefert eine Task-ID, laeuft dann weiter
Debug.Print taskId ' z. B. 4180 - NICHT ein Exit-Code
' die Zeile unten laeuft JETZT, waehrend Notepad noch oeffnet
End Sub
Shell ist das eine VBA-Verb, das über Excel hinausgreift, um ein anderes Windows-Programm zu starten.
Der Haken ist, dass es sich ganz anders verhält als der Rest Ihres Makros. Überall sonst beendet eine
Zeile ihre Arbeit, bevor die nächste läuft, und ein Fehlschlag wirft einen lauten Fehler. Shell bricht
mit beiden Gewohnheiten: Es startet das Programm und kehrt sofort zurück, und es verrät Ihnen so gut wie
nichts darüber, ob das Programm funktioniert hat. Sobald Sie es sich als fire-and-forget vorstellen,
hören die rätselhaften Bugs — „mein Code hat die Datei geöffnet, bevor der Konverter sie fertig
geschrieben hatte" — auf, rätselhaft zu sein.
Was Sie lernen
- Das mentale Modell —
Shellstartet und vergisst: Es startet, dann kehrt es sofort zurück - Warum der Rückgabewert eine Task-ID ist, kein Exit-Code und nicht die Ausgabe des Programms
- Warum die Zeile nach
Shellzu früh läuft, und wie Sie tatsächlich auf das Programm warten - Wie Sie einen Pfad mit Leerzeichen in Anführungszeichen setzen, und warum
Shellallein kein PDF und keine URL öffnen kann - Wann Sie
Shellzugunsten vonWScript.Shell.Run(warten + Exit-Code) oder.Exec(Ausgabe erfassen) aufgeben - Die Fehler, die
Shellwirft, wenn das Programm fehlt, und wie Sie sie absichern
Das mentale Modell: Shell startet und vergisst
Die eine Idee, die jede Shell-Überraschung erklärt: Shell führt ein Programm nicht aus — es startet
eines und geht davon. Es bittet Windows, die ausführbare Datei zu starten, bekommt eine Nummer zurück,
die die neue Aufgabe identifiziert, und kehrt sofort zu Ihrem Makro zurück. Das Programm läuft nun
parallel zu Ihrem Code, nicht in ihm.
Shell "calc.exe" ' Windows startet den Rechner...
MsgBox "done" ' ...und diese MsgBox erscheint sofort, calc laedt noch
Vergleichen Sie das mit einem normalen Methodenaufruf wie Range("A1").Copy, der in dem Moment
abgeschlossen ist, in dem die nächste Zeile läuft. Shell ist das Gegenteil: Es ist asynchron. Ihr
Makro und das gestartete Programm gehen getrennte Wege. Jede Regel weiter unten ist eine Folge dieser
einen Tatsache.
Das zweite Argument ist der Fensterstil — vbNormalFocus, vbMinimizedNoFocus, vbHide und so weiter.
Er steuert, wie das neue Fenster erscheint; er bringt Shell nicht zum Warten. Es gibt keinen
Fensterstil, der Shell synchron macht.
Shell liefert eine Task-ID, kein Ergebnis
Shell liefert einen Double — die Prozess- (Task-) ID, die Windows dem neuen Programm zugewiesen
hat. Man greift nach dieser Nummer und erwartet einen Exit-Code oder ein „Hat-es-geklappt"-Signal, und sie
ist weder das eine noch das andere:
Dim result As Double
result = Shell("robocopy.exe C:\a C:\b", vbHide)
' result ist eine Task-ID wie 7820 - sie sagt nichts darueber aus, ob robocopy erfolgreich war
Weil der Aufruf zurückkehrt, bevor das Programm seine Arbeit getan hat, gibt es noch nichts Sinnvolles zu
melden. Shell kann Ihnen den Exit-Code des Programms nicht geben, kann seine Konsolenausgabe nicht
erfassen und sagt Ihnen nicht, ob das Programm später abgestürzt ist. Die Task-ID taugt nur als Handle —
etwa um sie an eine Windows-API wie OpenProcess/WaitForSingleObject zu übergeben, falls Sie sich zum
Warten entscheiden. Wenn Ihre Logik davon abhängt, was das Programm zurückgegeben hat, kann Shell das
strukturell nicht liefern, und keine Menge zusätzlichen Codes rund um Shell ändert daran etwas.
Warum Ihre nächste Zeile zu früh läuft
Das ist der Vorzeige-Bug, und er fällt direkt aus fire-and-forget heraus. Sie starten ein Werkzeug, das eine Datei erzeugt, und verwenden diese Datei dann sofort:
Shell "C:\Tools\convert.exe report.docx report.pdf"
Workbooks.Open "C:\Tools\report.pdf" ' SCHEITERT - das PDF existiert noch nicht
convert.exe braucht eine Sekunde oder zwei, aber Workbooks.Open läuft jetzt, während der Konverter
kaum angelaufen ist. Die Datei fehlt, und Sie bekommen einen „Datei nicht gefunden"-Fehler, der auf die
falsche Zeile zeigt — die eigentliche Ursache steht eine Zeile höher, in dem Shell, das nicht gewartet
hat.
Die Lösung ist nicht ein festes Application.Wait oder ein geratenes Sleep — pausieren Sie zu kurz,
scheitert es weiterhin; pausieren Sie zu lang, verschwenden Sie die Zeit des Nutzers; und in jedem Fall
bringt eine langsame Maschine es zu Fall. Um wirklich zu warten, bis das Programm fertig ist, brauchen
Sie etwas anderes als Shell. Die saubere Antwort ist WScript.Shell.Run mit seinem Warte-Flag (weiter
unten); die hardwarenahe Antwort ist die WaitForSingleObject-API gegen die Task-ID. Womit Sie aufhören
sollten, ist, Sleep 2000 zu verstreuen und zu hoffen.
Pfade mit Leerzeichen, und ein Dokument statt einer exe öffnen
Zwei praktische Fallen bringen fast jeden zu Fall.
Leerzeichen im Pfad. Shell nimmt eine einzige Befehlszeilen-Zeichenkette, und ein Leerzeichen trennt
das Programm von seinen Argumenten. Ein Pfad wie C:\Program Files\... bricht also an der falschen Stelle:
Shell "C:\Program Files\App\app.exe" ' error 53 - Windows sucht nach "C:\Program"
Shell """C:\Program Files\App\app.exe""" ' korrekt - den exe-Pfad in Anfuehrungszeichen setzen
Innerhalb einer VBA-Zeichenkette ist jedes "" ein wörtliches Anführungszeichen, also setzt """..."""
echte Anführungszeichen um den Pfad. Setzen Sie die ausführbare Datei immer dann in Anführungszeichen,
wenn ihr Ordner ein Leerzeichen enthalten könnte (und die meisten tun es).
Ein Dokument öffnen, kein Programm. Shell startet ausführbare Dateien — es weiß nicht, dass eine
.pdf in Ihrem PDF-Reader und eine .xlsx in Excel geöffnet werden sollte. Zeigt man es auf ein
Dokument, scheitert es:
Shell "C:\Reports\Q1.pdf" ' error 53 - keine ausfuehrbare Datei
Shell "cmd /c start """" ""C:\Reports\Q1.pdf""" ' funktioniert - die Shell die Standard-App aufloesen lassen
Der start-Befehl (über cmd /c) bittet Windows, die Datei mit dem Programm zu öffnen, das dafür
registriert ist — dasselbe, was ein Doppelklick tut. Das leere "" nach start ist der Platzhalter für
den Fenstertitel, den start verlangt, wenn der Pfad in Anführungszeichen steht. Zum Öffnen von
Dokumenten und URLs ist dieser Weg verbreitet; die sauberere Alternative ist die ShellExecute-API oder
WScript.Shell, um die es als Nächstes geht.
Wenn Sie das Ergebnis wirklich brauchen: WScript.Shell
In dem Moment, in dem Sie auf das Programm warten, seinen Exit-Code lesen oder seine Ausgabe
erfassen müssen, steigen Sie von Shell auf das WScript.Shell-Objekt um, erzeugt mit
CreateObject:
Dim sh As Object
Set sh = CreateObject("WScript.Shell")
' .Run - drittes Argument True bedeutet WARTEN, bis das Programm endet;
' der Rueckgabewert ist dann der echte Exit-Code.
Dim exitCode As Long
exitCode = sh.Run("robocopy.exe C:\a C:\b", 0, True) ' 0 = verstecktes Fenster
If exitCode >= 8 Then MsgBox "robocopy failed: " & exitCode
' .Exec - das Programm ausfuehren und seinen stdout-Text lesen
Dim p As Object, output As String
Set p = sh.Exec("cmd /c dir C:\")
output = p.StdOut.ReadAll
.Run(command, windowStyle, waitOnReturn) ist das direkte Upgrade, wenn Sie warten und einen Exit-Code
bekommen wollen. .Exec geht weiter und gibt Ihnen ein lebendes Prozessobjekt, dessen StdOut Sie lesen
können — der einzige Weg, den Text eines Konsolenprogramms zurück nach Excel zu holen. Bauen Sie die Pfade
des Befehls aus Environ, damit sie auf jeder Maschine funktionieren, und hüllen
Sie das Ganze in Fehlerbehandlung, damit ein fehlendes Programm sauber gemeldet
wird, statt abzustürzen.
Das ehrliche Fazit: Shell für fire-and-forget, WScript.Shell für den Rest
Shell ist ein Verb mit einem einzigen Kunststück, und das Kunststück ist eng: ein Programm starten und
es vergessen. Dafür ist es echt und nützlich — einen Betrachter anwerfen, einen Ordner öffnen, ein
langlaufendes Werkzeug starten, das Sie nicht verfolgen müssen. Die Bugs entstehen, wenn man es um Dinge
bittet, die es nie versprochen hat. Vier Regeln decken die ganze Fläche ab:
- Es startet und vergisst →
Shellkehrt sofort zurück; das Programm läuft parallel. Es gibt keinen Fensterstil, der es zum Warten bringt. - Der Rückgabewert ist eine Task-ID, kein Ergebnis → kein Exit-Code, keine Ausgabe, kein
Erfolgssignal. Wenn Ihre Logik das Ergebnis des Programms braucht, ist
Shelldas falsche Werkzeug. - Pfade mit Leerzeichen einklammern; Dokumente sind keine ausführbaren Dateien → die exe in
""...""einschließen; ein Dokument mitcmd /c startoderShellExecuteöffnen, nicht direkt mitShell. - Müssen Sie warten oder Ausgabe lesen? Nehmen Sie
WScript.Shell→.Run(..., True)wartet und liefert den Exit-Code;.ExecerfasstStdOut. Greifen Sie danach, sobald das Schweigen vonShellzum Problem wird.
Passen Sie das Werkzeug zur Aufgabe, und die Bug-Klasse „es hat die Datei geöffnet, bevor das Programm fertig war" verschwindet — Sie hatten einen fire-and-forget-Starter benutzt, um eine Warten-und-Prüfen-Aufgabe zu erledigen.
Wie ExcelMaster hilft
Die Shell-Bugs, die echte Zeit kosten, sind die Timing-Bugs: ein Makro, das eine Datei öffnet, die das
gestartete Programm noch nicht geschrieben hat, ein Sleep 2000, das auf Ihrer Maschine funktioniert und
auf einer langsameren scheitert, ein Konverter, dessen Fehlschlag unsichtbar bleibt, weil Shell nichts
gemeldet hat. Jeder davon kommt daher, dass man ein fire-and-forget-Verb dort benutzt, wo man eigentlich
warten und prüfen musste.
ExcelMaster schreibt die
Starten-und-Warten-Logik richtig. Beschreiben Sie die Aufgabe — „führe diesen Konverter aus, dann öffne
seine Ausgabe" oder „ruf dieses Kommandozeilen-Werkzeug auf und sag mir, ob es fehlgeschlagen ist" — und
es wählt den richtigen Mechanismus: Shell, wenn fire-and-forget wirklich in Ordnung ist, oder
WScript.Shell.Run/.Exec, wenn Sie auf den Abschluss warten, einen Exit-Code lesen oder Ausgabe
erfassen müssen. Sie beschreiben das Programm, das Sie ausführen wollen; es schreibt den Code, der es
ausführt und weiß, wann es fertig ist.
Häufig gestellte Fragen
Wie bringe ich VBA Shell dazu, auf das Programm zu warten?
Shell selbst kann nicht warten — es kehrt immer sofort zurück. Um zu warten, nehmen Sie stattdessen das
WScript.Shell-Objekt: CreateObject("WScript.Shell").Run(command, windowStyle, True). Das dritte
Argument True (waitOnReturn) blockiert, bis das Programm endet, und der Rückgabewert ist dann der echte
Exit-Code des Programms. Die Alternative ist ein Windows-API-Weg — die Task-ID, die Shell liefert, an
WaitForSingleObject übergeben —, aber WScript.Shell.Run ist für den Alltag weit einfacher.
Was liefert die VBA-Funktion Shell zurück?
Shell liefert einen Double — die Task- (Prozess-) ID, die Windows dem neu gestarteten Programm
zugewiesen hat. Sie ist kein Exit-Code, nicht die Ausgabe des Programms und kein Erfolgssignal. Weil
Shell zurückkehrt, bevor das Programm fertig ist, gibt es noch kein Ergebnis zu melden. Die Task-ID ist
nur ein Handle, das Sie an eine Windows-API übergeben könnten. Wenn Sie den Exit-Code des Programms
brauchen, nehmen Sie stattdessen WScript.Shell.Run(..., True).
Warum wirft VBA Shell error 53 (Datei nicht gefunden)?
Zwei häufige Ursachen. Erstens: Der Pfad enthält ein Leerzeichen und steht nicht in Anführungszeichen —
Shell "C:\Program Files\..." bricht am Leerzeichen, also sucht Windows nach C:\Program; setzen Sie den
Pfad der ausführbaren Datei in doppelte Anführungszeichen: Shell """C:\Program Files\App\app.exe""".
Zweitens: Sie haben Shell auf ein Dokument gezeigt (eine .pdf, .xlsx) statt auf eine ausführbare
Datei — Shell startet Programme, keine Dateien. Um ein Dokument mit seiner Standard-App zu öffnen,
nehmen Sie Shell "cmd /c start """" ""C:\file.pdf""" oder die ShellExecute-API.
Wie führe ich eine Batch-Datei oder einen Befehl aus VBA aus?
Eine .bat-Datei kann Shell direkt starten: Shell "C:\scripts\build.bat" (den Pfad in
Anführungszeichen, wenn er Leerzeichen hat). Einen rohen Befehl, der keine ausführbare Datei ist — ein
dir, ein copy, eine Pipe —, führen Sie über den Befehlsinterpreter aus: Shell "cmd /c copy a.txt b.txt". Wenn Sie darauf warten und seinen Exit-Code oder seine Ausgabe lesen müssen, nehmen Sie
CreateObject("WScript.Shell").Run("cmd /c ...", 0, True) oder .Exec statt Shell.
Wie kann ich die Ausgabe eines aus VBA gestarteten Programms erfassen?
Shell kann Ausgabe überhaupt nicht erfassen. Nehmen Sie die .Exec-Methode des WScript.Shell-Objekts,
die ein Prozessobjekt mit einem lesbaren StdOut-Stream liefert: Set p = CreateObject("WScript.Shell").Exec("cmd /c dir C:\") : output = p.StdOut.ReadAll. Das ist der Standardweg,
um den Text eines Konsolenprogramms zurück nach VBA zu holen. Für Programme, die Erfolg nur über einen
Exit-Code signalisieren, nehmen Sie stattdessen .Run(command, 0, True) und prüfen dessen Rückgabewert.
Getestet in
Getestet in: Excel 365 (Windows 11), VBA 7.1 — zuletzt geprüft am 30.08.2026.
Verwandte Anleitungen: VBA CreateObject & GetObject · VBA Environ · VBA Wait & Sleep · VBA On Error · VBA FreeFile & Open
