I am writing a recursive function and would like to add a debugging mode with some (function) caller information along with the ScriptLineNumber
.
In PowerShell 7, I am able to do something like:
MyScript.ps1
[CmdletBinding()]param()
function MyFunction($Depth = 0) {
if ($DebugPreference -in 'Stop', 'Continue', 'Inquire') {
$Caller = (Get-PSCallStack)[1]
Write-Debug "Caller: $($Caller.FunctionName), Line: $($Caller.ScriptLineNumber)"
}
Write-Host 'Current depth:' $Depth
if ($Depth -lt 1) { MyFunction ($Depth + 1) }
}
MyFunction
.\MyScript.ps1 -Debug
DEBUG: Caller: <ScriptBlock>, Line: 11
Current depth: 0
DEBUG: Caller: MyFunction, Line: 8
Current depth: 1
How can I do this in Windows PowerShell 5.1?
I am lacking the C# knowledge, but I guess the answer is hidden in this C# code but I have no clue where Context.Debugger.GetCallStack()
comes from...
Before answering I should point out that Get-PSCallStack
is available in Windows PowerShell 5.1, and hasn't changed since - so this exercise is likely entirely futile.
I am lacking the C# knowledge, but I guess the answer is hidden in this C# code but I have no clue where
Context.Debugger.GetCallStack()
comes from...
Context
is a protected member of PSCmdlet
(or rather, its ancestor class InternalCommand
), which is why you can invoke it from an instance method declared by a derived type (like the GetPSCallStackCommand
class).
Since we don't have access to non-public member from inside a PSScriptCmdlet
, we'll need a bit of reflection magic:
function Get-PSScriptCallStack {
[CmdletBinding()]
param()
$nonPublicInstanceFlags = [System.Reflection.BindingFlags]'Instance,NonPublic'
$member_psCmdletContext = $PSCmdlet.GetType().GetMembers($nonPublicInstanceFlags).Where({ $_.Name -eq 'Context' })[0]
$contextInstance = $member_psCmdletContext.GetValue($PSCmdlet, @())
$member_contextDebugger = $contextInstance.GetType().GetMembers($nonPublicInstanceFlags).Where({ $_.Name -eq 'Debugger' })[0]
$debuggerInstance = $member_contextDebugger.GetValue($contextInstance, @())
# Debugger.GetCallStack() is public
$debuggerInstance.GetCallStack()
}