Stop-Process stops a running process when graceful completion is no longer acceptable or an executable exceeds its allowed runtime. Use it as a controlled fallback inside automation rather than as an unconditional first step. section 6.1.2 section 9.4.2
Reliable process termination follows an explicit control flow:
- Wait for a service or executable to stop normally. section 6.1.2 section 9.4.2
- Track elapsed time with a counter, timer, or stopwatch so a hang cannot run indefinitely. section 6.1.2 section 9.4.2
- Invoke Stop-Process only after the threshold is exceeded, and record or report the forced termination. section 6.1.2 section 9.4.2
Timed executable termination
Start an executable with -PassThru, poll its HasExited property, and measure runtime with a stopwatch. When the limit is exceeded, pipe the process to Stop-Process -Force and throw an error indicating that execution failed. section 9.4.2
$RuntimeSeconds = 2
$ping = Start-Process -FilePath 'ping.exe' -ArgumentList
'google.com','-n 10' -PassThru
$timer = [system.diagnostics.stopwatch]::StartNew()
while($ping.HasExited -eq $false){
if($timer.Elapsed.TotalSeconds -gt $RuntimeSeconds){
$ping | Stop-Process -Force
throw "The application did not exit in time"
}
}
$timer.Elapsed.TotalSeconds
$timer.Stop()
Shows the complete timeout pattern: Start-Process -PassThru, HasExited polling, stopwatch measurement, forced termination, and failure reporting. section 9.4.2
Listing 6.3 Disable-WindowsService
Function Disable-WindowsService {
[CmdletBinding()]
[OutputType([object])]
param(
[Parameter(Mandatory = $true)]
[string[]]$Services,
[Parameter(Mandatory = $true)]
[int]$HardKillSeconds,
[Parameter(Mandatory = $true)]
[int]$SecondsToWait
)
[System.Collections.Generic.List[PSObject]] $ServiceStatus = @()
foreach ($Name in $Services) {
$ServiceStatus.Add([pscustomobject]@{ #1
Service = $Name
HardKill = $false
Status = $null
Startup = $null
})
try {
$Get = @{ #2
Name = $Name
ErrorAction = 'Stop'
}
$Service = Get-Service @Get
$Set = @{
InputObject = $Service
StartupType = 'Disabled'
}
Set-Service @Set
$Stop = @{
InputObject = $Service
Force = $true
NoWait = $true
ErrorAction = 'SilentlyContinue'
}
Stop-Service @Stop
Get-Service -Name $Name | ForEach-Object {
$ServiceStatus[-1].Status = $_.Status.ToString()
$ServiceStatus[-1].Startup = $_.StartType.ToString()
}
}
catch {
$msg = 'NoServiceFoundForGivenName,Microsoft.PowerShell' +
'.Commands.GetServiceCommand'
if ($_.FullyQualifiedErrorId -eq $msg) {
$ServiceStatus[-1].Status = 'Stopped' #3
}
else {
Write-Error $_
}
}
}
$timer = [system.diagnostics.stopwatch]::StartNew()
do { #4
$ServiceStatus | Where-Object { $_.Status -ne 'Stopped' } |
ForEach-Object {
$_.Status = (Get-Service $_.Service).Status.ToString()
if ($_.HardKill -eq $false -and #5
$timer.Elapsed.TotalSeconds -gt $HardKillSeconds) {
Write-Verbose "Attempting hard kill on $($_.Service)"
$query = "SELECT * from Win32_Service WHERE name = '{0}'"
$query = $query -f $_.Service
$svcProcess = Get-CimInstance -Query $query
$Process = @{
Id = $svcProcess.ProcessId
Force = $true
ErrorAction = 'SilentlyContinue'
}
Stop-Process @Process
$_.HardKill = $true
}
}
$Running = $ServiceStatus | Where-Object { $_.Status -ne 'Stopped' }
} while ( $Running -and $timer.Elapsed.TotalSeconds -lt $SecondsToWait )
$ServiceStatus | #6
Where-Object { $_.Status -ne 'Stopped' } |
ForEach-Object { $_.Status = 'Reboot Required' }
$ServiceStatus #7
}
Places forced process termination inside a reusable Disable-WindowsService function that tracks service status and prevents repeated hard-kill attempts. section 6.1.2
