Unable to retrieve line number of exe from module

This forum can be browsed by the general public. Posting is limited to current SAPIEN license holders with active maintenance and does not offer a response time guarantee.
Forum rules
DO NOT POST LICENSE NUMBERS, ACTIVATION KEYS OR ANY OTHER LICENSING INFORMATION IN THIS FORUM.
Only the original author and our tech personnel can reply to a topic that is created in this forum. If you find a topic that relates to an issue you are having, please create a new topic and reference the other in your post.

Any code longer than three lines should be added as code using the 'Select Code' dropdown menu or attached as a file.
Richmmc
Posts: 7
Last visit: Sun Apr 18, 2021 7:14 am
Has voted: 1 time

Unable to retrieve line number of exe from module

Post by Richmmc »

Product, version and build: PowerShell Studio 2021 v5.8.188
Operating system: 2019/2016/2012r2
PowerShell version(s): 5.1

Hi All,

I have a module which includes a logging function making use of Get-PSCallStack to determine the calling script, function and line number.
The function has been working fine during early development and provides the desired output.
When I call the logging function the output includes the calling script file, the calling function (if one exists other wise script file again) and the calling line number.

The function effectively performs the following:
  1. $psCallStack = Get-PSCallStack
  2. $psCallStack[1] ScriptName
  3. $psCallStack[1] ScriptLineNumber
  4. $psCallStack[1] Command
Dots omitted from the above example due to posting filter.

When compiling the script as "Microsoft Windows PowerShell (Command line)" the code continues to log as expected however, as this provides an exact copy of the PS1 in the executables directory when running I wish to use "SAPIEN PowerShell V5 Host (Command line)", this is where the error occurs.

It appears Get-PSCallStack is unable to retrieve the information from the code when compiled using the SAPIEN option (PrimalScriptHostImplementation)

Is there anyway to accurately retrieve the line number in the way I am describing? or a suitable work around?
I have attempted to use the $hostinvocation.scriptlinenumber property but this always results in 1.

Thanks

Rich

User avatar
Alexander Riedel
Posts: 7671
Last visit: Thu May 13, 2021 5:46 am
Answers: 3
Been upvoted: 9 times

Re: Unable to retrieve line number of exe from module

Post by Alexander Riedel »

Well, as there is no actual script really that is a little tough. I am not certain at this time if PowerShell can provide a location within a script block.
Alexander Riedel
SAPIEN Technologies, Inc.

Richmmc
Posts: 7
Last visit: Sun Apr 18, 2021 7:14 am
Has voted: 1 time

Re: Unable to retrieve line number of exe from module

Post by Richmmc »

Hi Alexander,

It already works, but only when not compiling with a SAPIEN Host, something we need to do.
Compiling with a SAPIEN Host appears to cause issues with Get-PSCallStack, at least in the way I am using it.

Module - Scaled down for illustration only
  1. function Add-LogEntry
  2. {
  3.     [CmdletBinding(SupportsPaging = $false,
  4.                    SupportsShouldProcess = $false,
  5.                    PositionalBinding = $false,
  6.                    ConfirmImpact = 'None')]
  7.     param
  8.     (
  9.         [Parameter(Mandatory = $true,  HelpMessage = 'The Message To Add To The Log')]
  10.         [string]$message,
  11.         [Parameter(Mandatory = $true,  HelpMessage = 'The Path To The Log File')]
  12.         [string]$logFilePath
  13.     )
  14.        
  15.     #Retrieve call stack for use in logging message
  16.     $psCallStack = Get-PSCallStack
  17.    
  18.     #Retrieve just the file in which the calling function exists, not the full path
  19.     $psCallStackScriptNameLeaf = Split-Path -Path $psCallStack[1].ScriptName -Leaf
  20.    
  21.     #Generate output string
  22.     $logFileEntry = $psCallStackScriptNameLeaf.PadRight(23) + $psCallStack[1].ScriptLineNumber.ToString().PadRight(6) + $psCallStack[1].Command.ToString().PadRight(31) + $message
  23.    
  24.     $logFileEntry | Out-File -FilePath $logFilePath -Append -Encoding ascii -ErrorAction Stop
  25.    
  26.     Write-Verbose -Message $logFileEntry
  27.    
  28. } #End function Add-LogEntry
  29.  
  30. function MyFunc
  31. {
  32.  
  33.     add-logentry -message "Test From My Func" -logFilePath <You need to add a path to an existing log file>
  34.  
  35. }
Script - to demonstrate behaviour
  1.  
  2. import-module -name <you need to add the path to the module> -Verbose -force
  3.  
  4. <#
  5. Padding
  6. #>
  7.  
  8.  
  9. add-logentry -message "Test From Script" -logFilePath <You need to add a path to an existing log file>
  10. MyFunc
You will need to amend a few paths in the above examples to make it work.

Expected Output:

ExampleScript.ps1 14 ExampleScript.ps1 Test From Script
ExampleModule.psm1 33 MyFunc Test From My Func


This logging function is going to be used throughout the module which will contain a few dozen functions.
The module will be called by a number of scripts.


When executed from ISE - Recieve Expected Output
When executed from POSH Console - Recieve Expected Output
When executed as compiled with "Microsoft Windows PowerShell (Command Line)" - Receive Expected Output

When executed as compiled with "SAPIEN PowerShel V5 Host (Command Line)" - Recieve Errors related to path being null in split-path.

The error only occurs for the message from the script, the message from within the module reports correctly, i.e. all module actions are logged but all script actions log entries are omitted due to the error.

It apperas that Get-PSCallStack does not report back on the calling script when its compiled using the SAPIEN Hosts.

Interestingly though, If you use Get-PSCallStack directly in the script it will report its line number but is off by a few lines. So the function can be used in the compiled script but when used in the module it does not show information regarding the calling compiled script.

I am trying to find a way to maintain the expected output when using a SAPIEN Host compiled script.

User avatar
Alexander Riedel
Posts: 7671
Last visit: Thu May 13, 2021 5:46 am
Answers: 3
Been upvoted: 9 times

Re: Unable to retrieve line number of exe from module

Post by Alexander Riedel »

I apologize, maybe my original answer was not verbose enough.
When you package (it is not a compiler) with a SAPIEN Script Host, there is no script FILE. It is executed from memory. That's the point of it.
Because there is no FILE, mechanisms like Get-PSCallStack don't work, because they assume that there is a FILE with a name and lines and whatnot.

You can use this code snippet to determine the name of the executable:
  1. function get-scriptdirectory {
  2.   if($hostinvocation -ne null) {
  3.     Split-Path $hostinvocation.MyCommand.path
  4.   }
  5.   else {
  6.     $invocation=(get-variable MyInvocation -Scope 1).Value
  7.     Split-Path $invocation.myinvocation.path
  8.   }
  9. }
But it will not provide you with a line number.
Alexander Riedel
SAPIEN Technologies, Inc.

Richmmc
Posts: 7
Last visit: Sun Apr 18, 2021 7:14 am
Has voted: 1 time

Re: Unable to retrieve line number of exe from module

Post by Richmmc »

Hi Alex, Thanks for the info.

You say, "Because there is no FILE, mechanisms like Get-PSCallStack don't work".

I think what threw me, is that Get-PSCallStack does return a line number if called directly in the packaged script, rather then the module. From testing this line number appears to be off by a couple, depending on the length of the script so I assume its to do with the way its wrapped.

So PSCallStack can give you a line number from within a package to a certain level of accuracy.
Testing on a script just shy of 1000 lines its off by 4.
Not ideal, but close enough to provide some benefit to any colleagues that have to work on my code in years to come when I may not be around.

I think I will modify the module function to allow another parameter set to override the Get-PSCallStack and have the calling script/line number provided as parameters, this will maintain a single function for logging and keep a consistent output, with the caveat that line numbers from scripts are a "guide"

Thanks for your help.