🚀The world's best VBA AI has evolved. ExcelMaster is now an autonomous Agent.Read more →
Back to Blog

VBA Shell dans Excel — lancer un programme externe, et pourquoi votre code n'attend pas

|

VBA Shell dans Excel — lancer un programme externe, et pourquoi votre code n'attend pas

TL;DRShell démarre un programme et rend la main immédiatement, avant que ce programme n'ait rien fait. Il vous renvoie un identifiant de tâche (un Double), pas un code de sortie, ni une sortie, ni un indicateur de réussite, et la ligne suivante de votre macro s'exécute pendant que le programme est encore en train de se lancer. Si vous devez l'attendre ou lire ce qu'il a produit, Shell est le mauvais outil — utilisez WScript.Shell (.Run avec attente, ou .Exec).

Sub ShellDemo()
    Dim taskId As Double
    taskId = Shell("notepad.exe", vbNormalFocus)   ' renvoie un identifiant de tache, puis continue
    Debug.Print taskId                             ' ex. 4180 - PAS un code de sortie
    ' la ligne ci-dessous s'execute MAINTENANT, alors que Notepad s'ouvre encore
End Sub

Shell est le seul mot de VBA qui sort d'Excel pour démarrer un autre programme Windows. L'ennui, c'est qu'il ne se comporte en rien comme le reste de votre macro. Partout ailleurs, une ligne termine son travail avant que la suivante ne s'exécute, et un échec lève une erreur bien visible. Shell rompt ces deux habitudes : il lance le programme et rend la main aussitôt, et il ne vous dit presque rien sur le fait que le programme ait fonctionné ou non. Une fois que vous le voyez comme « lancer et oublier » (fire-and-forget), les bugs déroutants — « mon code a ouvert le fichier avant que le convertisseur n'ait fini de l'écrire » — cessent d'être déroutants.

Ce que vous allez apprendre

  • Le modèle mental — Shell lance puis oublie : il démarre, puis rend la main immédiatement
  • Pourquoi la valeur de retour est un identifiant de tâche, pas un code de sortie ni la sortie du programme
  • Pourquoi la ligne après Shell s'exécute trop tôt, et comment attendre réellement le programme
  • Comment mettre entre guillemets un chemin contenant des espaces, et pourquoi Shell seul ne peut pas ouvrir un PDF ou une URL
  • Quand abandonner Shell pour WScript.Shell .Run (attente + code de sortie) ou .Exec (capture de la sortie)
  • Les erreurs que Shell lève quand le programme est introuvable, et comment s'en prémunir

Le modèle mental : Shell lance et oublie

L'idée unique qui explique toutes les surprises de Shell : Shell n'exécute pas un programme — il en démarre un et s'en va. Il demande à Windows de lancer l'exécutable, récupère un numéro identifiant la nouvelle tâche, et rend aussitôt la main à votre macro. Le programme tourne désormais en parallèle de votre code, pas à l'intérieur.

Shell "calc.exe"          ' Windows demarre la Calculatrice...
MsgBox "done"             ' ...et ce MsgBox apparait instantanement, calc charge encore

Comparez cela à un appel de méthode normal comme Range("A1").Copy, terminé à l'instant où la ligne suivante s'exécute. Shell est l'inverse : il est asynchrone. Votre macro et le programme lancé suivent chacun leur route. Toutes les règles ci-dessous découlent de ce seul fait.

Le deuxième argument est le style de fenêtre — vbNormalFocus, vbMinimizedNoFocus, vbHide, etc. Il contrôle la façon dont la nouvelle fenêtre apparaît ; il ne fait pas attendre Shell. Aucun style de fenêtre ne rend Shell synchrone.

Shell renvoie un identifiant de tâche, pas un résultat

Shell renvoie un Double — l'identifiant de processus (de tâche) que Windows a attribué au nouveau programme. Les gens saisissent ce nombre en s'attendant à un code de sortie ou à un indicateur « a-t-il marché », et ce n'est ni l'un ni l'autre :

Dim result As Double
result = Shell("robocopy.exe C:\a C:\b", vbHide)
' result est un identifiant de tache comme 7820 - ne dit rien sur la reussite de robocopy

Comme l'appel rend la main avant que le programme n'ait fait son travail, il n'y a encore rien de significatif à signaler. Shell ne peut pas vous donner le code de sortie du programme, ne peut pas capturer sa sortie console, et ne vous dit pas si le programme a planté par la suite. L'identifiant de tâche n'est utile que comme poignée — par exemple, à passer à une API Windows telle que OpenProcess/WaitForSingleObject si vous décidez d'attendre. Si votre logique dépend de ce que le programme a renvoyé, Shell est structurellement incapable de le fournir, et aucun code supplémentaire autour de Shell n'y changera rien.

Pourquoi votre ligne suivante s'exécute trop tôt

C'est le bug vedette, et il découle directement du « lancer et oublier ». Vous lancez un outil qui produit un fichier, puis vous utilisez immédiatement ce fichier :

Shell "C:\Tools\convert.exe report.docx report.pdf"
Workbooks.Open "C:\Tools\report.pdf"   ' ECHOUE - le PDF n'existe pas encore

convert.exe a besoin d'une seconde ou deux pour tourner, mais Workbooks.Open s'exécute maintenant, alors que le convertisseur commence à peine. Le fichier est absent, et vous obtenez une erreur « fichier introuvable » qui pointe vers la mauvaise ligne — la vraie cause est trois lignes plus haut, dans le Shell qui n'a pas attendu.

Le correctif n'est pas un Application.Wait ou un Sleep fixé au jugé — attendez trop peu et ça échoue quand même, attendez trop et vous gaspillez le temps de l'utilisateur, et de toute façon une machine lente casse tout. Pour vraiment attendre que le programme se termine, il vous faut autre chose que Shell. La réponse propre, c'est WScript.Shell.Run avec son indicateur d'attente (ci-dessous) ; la réponse bas niveau, c'est l'API WaitForSingleObject sur l'identifiant de tâche. Ce que vous devez cesser de faire, c'est parsemer votre code de Sleep 2000 en croisant les doigts.

Chemins avec espaces, et ouvrir un document au lieu d'un exe

Deux pièges pratiques attrapent presque tout le monde.

Espaces dans le chemin. Shell prend une seule chaîne de ligne de commande, et un espace sépare le programme de ses arguments. Alors un chemin comme C:\Program Files\... se coupe au mauvais endroit :

Shell "C:\Program Files\App\app.exe"        ' error 53 - Windows cherche "C:\Program"
Shell """C:\Program Files\App\app.exe"""    ' correct - encadrez le chemin de l'exe avec des guillemets

À l'intérieur d'une chaîne VBA, chaque "" est un guillemet littéral, donc """...""" place de vrais guillemets autour du chemin. Encadrez l'exécutable de guillemets dès que son dossier peut contenir un espace (et la plupart en contiennent).

Ouvrir un document, pas un programme. Shell lance des exécutables — il ne sait pas qu'un .pdf doit s'ouvrir dans votre lecteur PDF ou qu'un .xlsx doit s'ouvrir dans Excel. Le pointer vers un document échoue :

Shell "C:\Reports\Q1.pdf"                    ' error 53 - pas un executable
Shell "cmd /c start """" ""C:\Reports\Q1.pdf"""   ' fonctionne - laisse le shell resoudre l'app par defaut

La commande start (via cmd /c) demande à Windows d'ouvrir le fichier avec le programme qui lui est associé — exactement ce que fait un double-clic. Le "" vide après start est le paramètre fictif du titre de fenêtre, que start exige lorsque le chemin est entre guillemets. Pour ouvrir des documents et des URL, cette approche est courante ; l'alternative plus propre est l'API ShellExecute ou WScript.Shell, abordée juste après.

Quand il vous faut vraiment le résultat : WScript.Shell

Dès l'instant où vous devez attendre le programme, lire son code de sortie ou capturer sa sortie, passez de Shell à l'objet WScript.Shell, créé avec CreateObject :

Dim sh As Object
Set sh = CreateObject("WScript.Shell")

' .Run - le troisieme argument True signifie ATTENDRE la fin du programme ;
' la valeur de retour est alors le vrai code de sortie.
Dim exitCode As Long
exitCode = sh.Run("robocopy.exe C:\a C:\b", 0, True)   ' 0 = fenetre masquee
If exitCode >= 8 Then MsgBox "robocopy failed: " & exitCode

' .Exec - execute et lit le texte stdout du programme
Dim p As Object, output As String
Set p = sh.Exec("cmd /c dir C:\")
output = p.StdOut.ReadAll

.Run(command, windowStyle, waitOnReturn) est le remplacement direct quand vous voulez attendre et obtenir un code de sortie. .Exec va plus loin et vous donne un objet processus vivant dont vous pouvez lire le StdOut — le seul moyen de ramener dans Excel le texte d'un programme console. Construisez les chemins de la commande à partir d'Environ pour qu'ils marchent sur n'importe quelle machine, et enveloppez le tout dans une gestion des erreurs pour qu'un programme manquant soit signalé proprement au lieu de faire planter.

Le verdict honnête : Shell pour lancer-et-oublier, WScript.Shell pour le reste

Shell est un mot à un seul tour, et ce tour est étroit : démarrer un programme et l'oublier. Il est authentique et utile pour exactement cela — lancer une visionneuse, ouvrir un dossier, démarrer un outil au long cours que vous n'avez pas besoin de suivre. Les bugs viennent de ce qu'on lui demande des choses qu'il n'a jamais promises. Quatre règles couvrent toute la surface :

  • Il lance et oublieShell rend la main immédiatement ; le programme tourne en parallèle. Aucun style de fenêtre ne le fait attendre.
  • La valeur de retour est un identifiant de tâche, pas un résultat → pas de code de sortie, pas de sortie, pas d'indicateur de réussite. Si votre logique a besoin du résultat du programme, Shell est le mauvais outil.
  • Mettez entre guillemets les chemins avec espaces ; les documents ne sont pas des exécutables → encadrez l'exe de ""..."" ; ouvrez un document avec cmd /c start ou ShellExecute, pas directement Shell.
  • Besoin d'attendre ou de lire la sortie ? Utilisez WScript.Shell.Run(..., True) attend et renvoie le code de sortie ; .Exec capture le StdOut. Saisissez-le dès l'instant où le silence de Shell pose problème.

Adaptez l'outil à la tâche et la catégorie de bug « ça a ouvert le fichier avant que le programme ait fini » disparaît — vous utilisiez un lanceur « lancer et oublier » pour faire un travail « attendre et vérifier ».

Comment ExcelMaster aide

Les bugs de Shell qui coûtent vraiment du temps sont ceux de timing : une macro qui ouvre un fichier que le programme lancé n'a pas encore écrit, un Sleep 2000 qui marche sur votre machine et échoue sur une plus lente, un convertisseur dont l'échec est invisible parce que Shell n'a rien signalé. Chacun vient d'avoir utilisé un mot « lancer et oublier » là où il fallait en réalité attendre et vérifier.

ExcelMaster écrit correctement la logique de lancement-puis-attente. Décrivez la tâche — « exécute ce convertisseur, puis ouvre sa sortie », ou « appelle cet outil en ligne de commande et dis-moi s'il a échoué » — et il choisit le bon mécanisme : Shell quand « lancer et oublier » convient vraiment, ou WScript.Shell.Run/.Exec quand vous devez attendre la fin, lire un code de sortie, ou capturer une sortie. Vous décrivez le programme que vous voulez exécuter ; il écrit le code qui l'exécute et qui sait quand c'est terminé.

Questions fréquentes

Comment faire attendre VBA Shell jusqu'à la fin du programme ?

Shell lui-même ne peut pas attendre — il rend toujours la main immédiatement. Pour attendre, utilisez plutôt l'objet WScript.Shell : CreateObject("WScript.Shell").Run(command, windowStyle, True). Le troisième argument True (waitOnReturn) le fait bloquer jusqu'à ce que le programme se termine, et la valeur de retour est alors le vrai code de sortie du programme. L'autre approche est celle d'une API Windows — passer l'identifiant de tâche renvoyé par Shell à WaitForSingleObject — mais WScript.Shell.Run est bien plus simple pour un usage courant.

Que renvoie la fonction VBA Shell ?

Shell renvoie un Double — l'identifiant de tâche (de processus) que Windows a attribué au programme qui vient de démarrer. Ce n'est pas un code de sortie, ni la sortie du programme, ni un indicateur de réussite. Comme Shell rend la main avant que le programme n'ait fini, il n'y a encore aucun résultat à signaler. L'identifiant de tâche n'est qu'une poignée que vous pourriez passer à une API Windows. Si vous avez besoin du code de sortie du programme, utilisez plutôt WScript.Shell.Run(..., True).

Pourquoi VBA Shell donne-t-il l'erreur 53 (fichier introuvable) ?

Deux causes courantes. D'abord, le chemin contient un espace et n'est pas entre guillemets — Shell "C:\Program Files\..." se coupe à l'espace, donc Windows cherche C:\Program ; encadrez le chemin de l'exécutable de guillemets doublés : Shell """C:\Program Files\App\app.exe""". Ensuite, vous avez pointé Shell vers un document (un .pdf, un .xlsx) plutôt que vers un exécutable — Shell lance des programmes, pas des fichiers. Pour ouvrir un document avec son application par défaut, utilisez Shell "cmd /c start """" ""C:\file.pdf""" ou l'API ShellExecute.

Comment exécuter un fichier batch ou une commande en ligne de commande depuis VBA ?

Pour un fichier .bat, Shell peut le lancer directement : Shell "C:\scripts\build.bat" (mettez le chemin entre guillemets s'il contient des espaces). Pour une commande brute qui n'est pas un exécutable — un dir, un copy, un pipe — passez-la par l'interpréteur de commandes : Shell "cmd /c copy a.txt b.txt". Si vous devez l'attendre et lire son code de sortie ou sa sortie, utilisez CreateObject("WScript.Shell").Run("cmd /c ...", 0, True) ou .Exec au lieu de Shell.

Comment capturer la sortie d'un programme exécuté depuis VBA ?

Shell ne peut pas capturer la sortie du tout. Utilisez la méthode .Exec de l'objet WScript.Shell, qui renvoie un objet processus doté d'un flux StdOut lisible : Set p = CreateObject("WScript.Shell").Exec("cmd /c dir C:\") : output = p.StdOut.ReadAll. C'est la façon standard de ramener dans VBA le texte d'un programme console. Pour les programmes qui ne signalent leur réussite que par un code de sortie, utilisez plutôt .Run(command, 0, True) et vérifiez sa valeur de retour.

Testé dans

Testé dans : Excel 365 (Windows 11), VBA 7.1 — vérifié le 30/08/2026.

Guides connexes : VBA CreateObject & GetObject · VBA Environ · VBA Wait & Sleep · VBA On Error · VBA FreeFile & Open