I used a method with and without a class and the Write-Error seems to produce different outputs. In case of class, it doesn't specify the function and the line number is always 1,1
function oper1() {
Try {
[string] $cmd = ".\some_exe_which_does_not_exist.exe"
iex $cmd
}
Catch {
Write-Error $_.Exception.Message
}
}
oper1
Output for above:
oper1 : The term '.\some_exe_which_does_not_exist.exe' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again. At F:\debug\encryption_concat_tests\Untitled1.ps1:11 char:1 + oper1 + ~~~~~ + CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,oper1
When I enclosed the same function in a class, I got this:
class Operator {
[void] oper1() {
Try {
[string] $cmd = ".\some_exe_which_does_not_exist.exe"
iex $cmd
}
Catch {
Write-Error $_.Exception.Message
}
}
}
[Operator] $operator = New-Object Operator
$operator.oper1()
The term '.\some_exe_which_does_not_exist.exe' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again. At line:1 char:1 + F:\debug\encryption_concat_tests\Untitled1.ps1 + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException
What could be the reason for this behaviour for methods inside classes?
As an aside: Invoke-Expression
(iex
) should generally be avoided; definitely don't use it to invoke an external program - just invoke it directly, as shown below.
In PowerShell class methods:
Do not use Write-Error
, as classes are not designed to emit non-terminating errors.
[void]
- see GitHub issue #5331.Instead, communicate errors solely by throwing them with the Throw
statement or by not catching terminating errors (which include exceptions from .NET methods and cmdlet calls with -ErrorAction Stop
).
Throw
and -ErrorAction Stop
(or $ErrorActionPreference = 'Stop'
) create script-terminating (thread-terminating) errors, whereas exceptions thrown by a .NET method (not caught and re-thrown in the class method) only create statement-terminating errors; that is, while the class-method body is terminated right away, execution continues in the caller by default; the latter also applies to the call operator (&
) not finding an executable, errors in expressions such as 1 / 0
, and cmdlet calls that emit statement-terminating errors (the most severe error type they can report) without their being promoted to script-terminating ones with -ErrorAction Stop
; see this GitHub docs issue for a comprehensive overview of PowerShell's complex error handling.See this answer for more information about error handling and stream-output behavior in class methods in particular.
Here's a corrected version of your code.
class Operator {
[void] oper1() {
Try {
# Try to invoke a non-existent executable.
& ".\some_exe_which_does_not_exist.exe"
}
Catch {
# Re-throw the error.
# Alternatively, don't use try / catch, but the error
# then only aborts the method call, not the entire script.
Throw
}
}
}
$operator = [Operator]::new()
$operator.oper1()