113 votes

Obtenir ExitCode en utilisant Start-Process et WaitForExit au lieu de -Wait

Je tente de lancer un programme à partir de PowerShell, d'attendre la sortie, puis d'accéder à ExitCode, mais je n'ai pas beaucoup de chance. Je ne veux pas utiliser -Wait avec Start-Process, car j'ai besoin que certains traitements se déroulent en arrière-plan.

Voici un script de test simplifié :

cd "C:\Windows"

# ExitCode est disponible lors de l'utilisation de -Wait...
Write-Host "Démarrage de Notepad avec -Wait - le code de retour sera disponible"
$process = (Start-Process -FilePath "notepad.exe" -PassThru -Wait)
Write-Host "Le processus s'est terminé avec le code de retour : " $process.ExitCode

# ExitCode n'est pas disponible lors de l'attente séparée
Write-Host "Démarrage de Notepad sans -Wait - le code de retour NE sera PAS disponible"
$process = (Start-Process -FilePath "notepad.exe" -PassThru)
$process.WaitForExit()
Write-Host "Le code de sortie du processus devrait être ici : " $process.ExitCode

Exécuter ce script va provoquer le démarrage de Notepad. Après sa fermeture manuelle, le code de sortie sera affiché, puis il redémarrera, sans utiliser -wait. Aucun ExitCode n'est fourni lorsque cela est quitté :

Démarrage de Notepad avec -Wait - le code de retour sera disponible
Le processus s'est terminé avec le code de retour :  0
Démarrage de Notepad sans -Wait - le code de retour NE sera PAS disponible
Le code de sortie du processus devrait être ici :

J'ai besoin de pouvoir effectuer des traitements supplémentaires entre le démarrage du programme et l'attente de sa fin, donc je ne peux pas utiliser -Wait. Comment puis-je faire cela tout en ayant toujours accès à la propriété .ExitCode de ce processus ?

153voto

Daniel McQuiston Points 346

Il y a deux choses à retenir ici. Une consiste à ajouter l'argument -PassThru et l'autre est d'ajouter l'argument -Wait. Vous devez ajouter l'argument d'attente en raison de ce défaut.

-PassThru []
    Renvoie un objet de processus pour chaque processus démarré par la cmdlet. Par défaut,
    cette cmdlet ne génère aucune sortie.

Une fois que vous avez fait cela, un objet de processus est renvoyé et vous pouvez consulter la propriété ExitCode de cet objet. Voici un exemple :

$process = start-process ping.exe -windowstyle Hidden -ArgumentList "-n 1 -w 127.0.0.1" -PassThru -Wait
$process.ExitCode

# Cela affichera 1

Si vous l'exécutez sans -PassThru ou -Wait, cela n'affichera rien.

La même réponse se trouve ici: Comment exécuter un programme d'installation Windows et obtenir une valeur de réussite/échec en PowerShell ?

Il convient également de noter qu'il y a une solution de contournement mentionnée dans le lien du "rapport de défaut", comme suit :

# Démarrer le processus avec la commande -PassThru pour pouvoir y accéder ultérieurement
$process = Start-Process 'ping.exe' -WindowStyle Hidden -ArgumentList '-n 1 -w 127.0.0.1' -PassThru

# Cela affichera False/True en fonction de si le processus est terminé ou non
# Doit être appelé pour que la commande ci-dessous fonctionne correctement
$process.HasExited

# Cela affichera le code de sortie réel du processus
$process.GetType().GetField('exitCode', 'NonPublic, Instance').GetValue($process)

79voto

Adrian Points 619

En essayant la dernière suggestion ci-dessus, j'ai découvert une solution encore plus simple. Tout ce que j'avais à faire était de mettre en cache le handle du processus. Dès que j'ai fait cela, $process.ExitCode a fonctionné correctement. Si je ne mettais pas en cache le handle du processus, $process.ExitCode était nul.

exemple:

$proc = Start-Process $msbuild -PassThru
$handle = $proc.Handle # mettre en cache proc.Handle
$proc.WaitForExit();

if ($proc.ExitCode -ne 0) {
    Write-Warning "$_ a quitté avec le code de statut $($proc.ExitCode)"
}

55voto

Andy Arismendi Points 16501

Deux choses que vous pourriez faire je pense...

  1. Créez manuellement l'objet System.Diagnostics.Process et contournez Start-Process
  2. Exécutez l'exécutable dans un travail en arrière-plan (uniquement pour les processus non interactifs!)

Voici comment vous pourriez faire l'un ou l'autre:

$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = "notepad.exe"
$pinfo.RedirectStandardError = $true
$pinfo.RedirectStandardOutput = $true
$pinfo.UseShellExecute = $false
$pinfo.Arguments = ""
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $pinfo
$p.Start() | Out-Null
#Faites d'autres choses ici....
$p.WaitForExit()
$p.ExitCode

OU

Start-Job -Name DoSomething -ScriptBlock {
    & ping.exe somehost
    Write-Output $LASTEXITCODE
}
# Faites d'autres choses ici
Get-Job -Name DoSomething | Wait-Job | Receive-Job

25voto

blt Points 379

L'option '-Wait' semblait bloquer pour moi même si mon processus était terminé.

J'ai essayé la solution d'Adrian et ça marche. Mais j'ai utilisé Wait-Process au lieu de compter sur un effet secondaire de la récupération du handle du processus.

Donc:

$proc = Start-Process $msbuild -PassThru
Wait-Process -InputObject $proc

if ($proc.ExitCode -ne 0) {
    Write-Warning "$_ a quitté avec le code d'état $($proc.ExitCode)"
}

5voto

Greg Vogel Points 91

Ou essayez d'ajouter ceci...

$code = @"
[DllImport("kernel32.dll")]
public static extern int GetExitCodeProcess(IntPtr hProcess, out Int32 exitcode);
"@
$type = Add-Type -MemberDefinition $code -Name "Win32" -Namespace Win32 -PassThru
[Int32]$exitCode = 0
$type::GetExitCodeProcess($process.Handle, [ref]$exitCode)

En utilisant ce code, vous pouvez toujours laisser PowerShell gérer les flux de sortie/erreur redirigés, ce que vous ne pouvez pas faire directement en utilisant System.Diagnostics.Process.Start().

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X