Public/Get-ServiceProcess.ps1
|
function Get-ServiceProcess { <# .SYNOPSIS Get the hosting process for one or more Windows services. .DESCRIPTION Resolves each service name (wildcards supported) to its hosting process via Win32_Service. When a single running service is matched, the underlying System.Diagnostics.Process is returned so the cmdlet composes naturally with Stop-Process, Wait-Process, etc. Otherwise, one ServiceProcess object is emitted per service with metadata including the svchost command line for shared-host services. .PARAMETER Name One or more service names. Supports wildcards. Accepts pipeline input by value (string) or by property name (e.g. Get-Service | Get-ServiceProcess). .PARAMETER PerService Reconfigure each shared-svchost service to run in its own process via 'sc.exe config <name> type= own'. Requires elevation. The change takes effect on next service restart. .EXAMPLE Get-ServiceProcess Spooler Returns the System.Diagnostics.Process hosting the Print Spooler. .EXAMPLE Get-ServiceProcess Spooler | Stop-Process Stops the process hosting the Spooler service. .EXAMPLE Get-Service W3* | Get-ServiceProcess Returns one ServiceProcess object per matching service. .EXAMPLE Get-ServiceProcess BITS -PerService -WhatIf Previews reconfiguring BITS to run in its own process. #> [CmdletBinding(SupportsShouldProcess)] [OutputType([System.Diagnostics.Process], [pscustomobject])] param( [Parameter(Position = 0, Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [string[]]$Name, [switch]$PerService ) begin { Assert-WindowsOnly -CommandName $MyInvocation.MyCommand.Name if ($PerService -and -not $WhatIfPreference -and -not (Test-IsElevated)) { throw '-PerService requires an elevated session.' } $results = [System.Collections.Generic.List[pscustomobject]]::new() # Bulk-fetch all Win32_Service CIM data once, index by name $cimIndex = @{} foreach ($c in (Get-CimInstance Win32_Service)) { $cimIndex[$c.Name] = $c } } process { foreach ($pattern in $Name) { $services = Get-Service -Name $pattern -ErrorAction SilentlyContinue if (-not $services) { Write-Error "No service matched '$pattern'." continue } foreach ($svc in $services) { if ($PerService) { if ($PSCmdlet.ShouldProcess($svc.Name, "sc.exe config <name> type= own")) { & sc.exe config $svc.Name type= own | Out-Null if ($LASTEXITCODE -ne 0) { Write-Error "sc.exe config $($svc.Name) type= own failed with exit code $LASTEXITCODE." continue } Write-Warning "$($svc.Name): reconfigured to type=own. Restart the service for the change to take effect." } } $cim = $cimIndex[$svc.Name] $procId = if ($cim) { [int]$cim.ProcessId } else { 0 } $proc = if ($procId -gt 0) { Get-Process -Id $procId -ErrorAction SilentlyContinue } else { $null } $results.Add([pscustomobject]@{ PSTypeName = 'ServiceProcess' Name = $svc.Name DisplayName = $svc.DisplayName Status = $svc.Status ProcessId = $procId ProcessName = if ($proc) { $proc.ProcessName } else { '' } Process = $proc CommandLine = if ($cim) { $cim.PathName } else { '' } }) } } } end { if ($results.Count -eq 1 -and $null -ne $results[0].Process) { $results[0].Process } else { $results } } } |