web-dev-qa-db-fra.com

Obtention d'ExitCode en utilisant Start-Process et WaitForExit au lieu de -Wait

J'essaie d'exécuter 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 il me faut un traitement en arrière-plan.

Voici un script de test simplifié:

cd "C:\Windows"

# ExitCode is available when using -Wait...
Write-Host "Starting Notepad with -Wait - return code will be available"
$process = (Start-Process -FilePath "notepad.exe" -PassThru -Wait)
Write-Host "Process finished with return code: " $process.ExitCode

# ExitCode is not available when waiting separately
Write-Host "Starting Notepad without -Wait - return code will NOT be available"
$process = (Start-Process -FilePath "notepad.exe" -PassThru)
$process.WaitForExit()
Write-Host "Process exit code should be here: " $process.ExitCode

Si vous exécutez ce script, Notepad sera démarré. Après la fermeture manuelle de celui-ci, le code de sortie sera imprimé et redémarrera sans utiliser -wait. Aucun code ExitCode n'est fourni à la fermeture:

Starting Notepad with -Wait - return code will be available
Process finished with return code:  0
Starting Notepad without -Wait - return code will NOT be available
Process exit code should be here:

Je dois pouvoir effectuer un traitement supplémentaire entre le démarrage du programme et l'attente de son arrêt. Je ne peux donc pas utiliser -Wait. Comment puis-je faire cela tout en ayant accès à la propriété .ExitCode à partir de ce processus?

57
Richard

Je pense que tu peux faire deux choses ...

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

Voici comment vous pouvez faire soit:

$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
#Do Other Stuff Here....
$p.WaitForExit()
$p.ExitCode

OU

Start-Job -Name DoSomething -ScriptBlock {
    & ping.exe somehost
    Write-Output $LASTEXITCODE
}
#Do other stuff here
Get-Job -Name DoSomething | Wait-Job | Receive-Job
40
Andy Arismendi

Il y a deux choses à retenir ici. On est d'ajouter le -PassThru argument et deux consiste à ajouter le -Wait argument. Vous devez ajouter l'argument wait à cause de ce défaut: http://connect.Microsoft.com/PowerShell/feedback/details/520554/start-process-does-not-return-exitcode-property

-PassThru [<SwitchParameter>]
    Returns a process object for each process that the cmdlet started. By d
    efault, this cmdlet does not generate any output.

Une fois cette opération effectuée, 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

# This will print 1

Si vous l'exécutez sans -PassThru ou -Wait, il n’imprimera rien.

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

81
Daniel McQuiston

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 descripteur de processus. Dès que j'ai fait cela, $ process.ExitCode a fonctionné correctement. Si je n'ai pas mis en cache le descripteur de processus, $ process.ExitCode était null.

exemple:

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

if ($proc.ExitCode -ne 0) {
    Write-Warning "$_ exited with status code $($proc.ExitCode)"
}
32
Adrian

L'option '-Attendre' a semblé me ​​bloquer 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 descripteur de processus.

Alors:

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

if ($proc.ExitCode -ne 0) {
    Write-Warning "$_ exited with status code $($proc.ExitCode)"
}
8
blt

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 prendre en charge la gestion des flux de sortie/d'erreur redirigés, ce que vous ne pouvez pas utiliser à l'aide de System.Diagnostics.Process.Start () directement.

4
Greg Vogel