windowspowershell

Powershell Get-Process. works the first time, fails the 2nd time


I have a windows Powershell script that includes the following line...

$script:parent = (Get-Process -Id (Get-CimInstance Win32_Process -Filter "ProcessID = $pid").ParentProcessId).Name

The line appears twice in the same script. The first time it gets executed it always works fine and returns either svchost, explorer or powershell (meaning that the script was either started by task scheduler, a shortcut on the desktop, or another powershell script).

The second time it's executed, it mostly works fine (and returns the same result) but occasionally returns the following...

Get-Process : Cannot find a process with the process identifier 17216.
At C:\blah\blah\testscript.ps1:2675 char:23
+ ... t:parent = (Get-Process -Id (Get-CimInstance Win32_Process -Filter "P ...
+                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (17216:Int32) [Get-Process], ProcessCommandException
    + FullyQualifiedErrorId : NoProcessFoundForGivenId,Microsoft.PowerShell.Commands.GetProcessCommand

Why is it failing the 2nd time?

(BTW - I'm asking this out of curiosity only, there's a simple fix - only execute the command the one time!)

UPDATE - @mklement0 answer is correct. I hadn't spotted a critical fact, the second execution of 'who's the parent' only ever fails if the parent was a powershell script. The script runs 24*7 and is designed that in the event of a critical failure it will re-start another copy of itself and then exit. Since this only occurs once or twice a month I had forgotten about this feature


Solution

  • As iRon states in a comment on the question, your symptom implies that the parent process no longer exists at the time the 2nd call is made.

    This can indeed happen, for instance, if you call a PowerShell script via powershell.exe, the Windows PowerShell CLI, using an asynchronous call via Start-Process, i.e. a call that does not use the -Wait switch.

    Building on your own example Start-Process call, the following script demonstrates this scenario:

    # Script block for later use to determine the parent process' name.
    $sbGetParentProcessName = {
      (Get-Process -Id (Get-CimInstance Win32_Process -Filter "ProcessID = $pid").ParentProcessId).Name
    }
    
    $parent = & $sbGetParentProcessName
    "1st attempt: parent process name is: $parent"
    
    # If this script was invoked from the Windows (GUI) shell (Start Menu, taskbar, desktop, ...),
    # or from a scheduled task or a service, re-invoke it, then exit after 1 sec. 
    if ($parent -in 'explorer', 'svchost') {
      Write-Verbose "Re-invoking this script asynchronously..."
      Start-Process -FilePath "$PSHOME\powershell.exe" -ArgumentList '-NoExit', '-File', """$PSCommandPath"""
      Write-Verbose -Verbose 'Exiting after 1 sec...'
      Start-Sleep 1
      [Environment]::Exit(0)
    }
    
    # Getting here means that the script is being re-invoked.
    # Sleeping for 2 seconds ensures that the parent process no longer exists.
    Write-Verbose -Verbose 'Sleeping 2 seconds...'
    Start-Sleep 2
    
    # This attempt now predictably fails.
    # Note: The error message will print *first*.
    "2nd attempt: parent process name is: $(& $sbGetParentProcessName)"