src/Suspend-PsActivity.ps1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#requires -version 6
using namespace System.Linq
using namespace System.ComponentModel

function Suspend-PsActivity {
  [CmdletBinding(DefaultParameterSetName='Name')]
  param(
    [Parameter(Mandatory, ParameterSetName='Name', Position=0)]
    [ValidateNotNullOrEmpty()]
    [ValidateScript({!!($script:ps = Get-Process $_ -ErrorAction 0)})]
    [String]$Name,

    [Parameter(Mandatory, ParameterSetName='Id', Position=0)]
    [ValidateScript({!!($script:ps = Get-Process -Id $_ -ErrorAction 0)})]
    [Int32]$Id
  )

  process {
    $ntdll = New-Delegate ntdll -Signature @{
      NtSuspendProcess = [Func[IntPtr, Int32]]
      RtlNtStatusToDosError = [Func[Int32, Int32]]
    }

    $ps.ForEach{
      if ([Enumerable]::Sum([Int32[]](
        Select-Object -InputObject $_.Threads[0] -Property ThreadState, WaitReason
      ).PSObject.Properties.Value.ForEach{$_ -eq 5}) -ne 2) {
        if (($nts = $ntdll.NtSuspendProcess.Invoke($_.Handle)) -ne 0) {
          Write-Verbose "$([Win32Exception]::new(
            $ntdll.RtlNtStatusToDosError.Invoke($nts)
          ).Message)"

        }
        else { Write-Verbose "Process $($_.Id) is suspended." }
      }
      else { Write-Verbose "Process $($_.Id) is already suspended." }
      $_.Dispose()
    }
  }
}