0

i have the following code:

function Test-PendingReboot {
    try {
        $util = [wmiclass]'\.\root\ccm\clientsdk:CCM_ClientUtilities'
        $status = $util.DetermineIfRebootPending()
        if (($null -ne $status) -and $status.RebootPending) {
            return $true
        }
    } catch {}
    return $false
}

which is part of a function that, as you can see, checks if a system reboot is pending.

That casting to [wmiclass] is possible only when a reboot is pending.

The function works correctly, but with the side effect of the casting error (when a reboot is not pending) going into the $Error automatic variable. You can check with:

$error.clear()
Test-PendingReboot
$error

This will print the following (False is just the return value of the call):

False

Cannot convert value "\.\root\ccm\clientsdk:CCM_ClientUtilities" to type "System.Management.ManagementClass". Error: "Invalid parameter "
At C:\_\t.ps1:26 char:89
+ … re'; return ([wmiclass]'\.\root\ccm\clientsdk:CCM_ClientUtilities') } …
+               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : InvalidArgument: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvalidCastToWMIClass

I don't want that. I want to have complete control over the error, as the error itself is also part of the logic of the function (because it means that a reboot is certainly not pending). I want to control it as when using -ErrorAction ignore instead of silentlycontinue with a cmdlet.

I tried using the following:

$ErrorActionPreference = 'ignore'
$util = [wmiclass]'\.\root\ccm\clientsdk:CCM_ClientUtilities'

or even the following (yes that's mad):

$ErrorActionPreference = 'ignore'
$util = Invoke-Command -ScriptBlock { $ErrorActionPreference = 'ignore'; return ([wmiclass]'\.\root\ccm\clientsdk:CCM_ClientUtilities') } -ea Ignore

but nothing works...

I'm starting to doubt, Is it even possible?

PLEASE NOTE: this piece of code is just an example, my question is, in general, how to stop casting error from getting into $Error?


thanks

aetonsi
  • 208
  • 2
  • 9
  • I believe the only thing you can do is save/restore the `$Error` variable. – zett42 Mar 31 '23 at 09:04
  • Try following : Get-WmiObject -ComputerName '.' -Namespace 'root\ccm\ClientSDK' -Class 'CCM_ClientUtilities' I do not have the sdk installed so I'm not getting anything. See following : https://learn.microsoft.com/en-us/windows/win32/wmisdk/creating-wmi-clients – jdweng Mar 31 '23 at 09:13
  • @zett42 i'm starting to think that too... what would the best way be? is `$errbck = $global:Error.clone(); ...my function creating errors... ; $global:Error = $errbck;` ok? – aetonsi Apr 03 '23 at 12:24
  • @jdweng yes, that, together with `-ErrorAction ignore`, would work, in this situation. but My question is about how to stop *any* casting error from going into `$Error`. The `test-pendingreboot` function is just a practical example (i added a clarification in the question body now) – aetonsi Apr 03 '23 at 12:26
  • I do not like the way Powershell does casting. Powershell is written in c#. I've seen lots of cases where code that works in c# fails in Powershell do to casting. Powsershell automatically converts objects to a PS Object, while c# everything is declared (except var) and cast to the declared types. I've tried in PS to cast like c# and it works most of the times, but not always. – jdweng Apr 03 '23 at 12:41
  • that's exactly my question. the cast might fail or it might not, so i want to catch and suppress the possible error. in c# a try catch would be enough to completely manage an exception (AFAIK), in powershell it's not enough (as it still pollutes `$Error`) – aetonsi Apr 03 '23 at 21:22

2 Answers2

1

You might consider to redirect the (error) stream:

function Test-PendingReboot {
    $util = & { [wmiclass]'\.\root\ccm\clientsdk:CCM_ClientUtilities' } *>&1
    $util.ForEach{
        if ($_ -is [System.Management.Automation.ErrorRecord]) {
            "Your error: $_"
        }
        else {
            $status = $_.DetermineIfRebootPending()
            if ($null -ne $status) { $status.RebootPending }
        }
    }
}

To completely intercept the error, you might consider to start an expensive and cumbersome new PowerShell session:

$Result = PowerShell {
    $util = & { [wmiclass]'\.\root\ccm\clientsdk:CCM_ClientUtilities' } *>&1
    $util.ForEach{
        if ($_ -is [System.Management.Automation.ErrorRecord]) {
            "Your error: $_"
        }
        else {
            $status = $_.DetermineIfRebootPending()
            if ($null -ne $status) { $status.RebootPending }
        }
    }
}
iRon
  • 20,463
  • 10
  • 53
  • 79
  • my goal is to stop the error from ending up in the `$Error` variable. if you run `$Error.clear()`, then your function, then `$Error`, you will still get the error into the automatic variable, as redirecting it doesn't stop it from popping up in there. AFAIK the only way to do it is using `-ErrorAction Ignore`, but i have not been able to apply it to an explicit cast operation – aetonsi Apr 03 '23 at 12:22
  • 1
    In that case, see: [Faulty PowerShell cmdlets filling up $Error automatic variable](https://stackoverflow.com/a/75336462/1701026) – iRon Apr 03 '23 at 12:27
  • damn so there's no way at all, to apply `-ea ignore` to any statement? not even with scriptblocks or some trick? – aetonsi Apr 03 '23 at 12:37
  • To completely intercept the error, you might consider to start an expensive and cumbersome new PowerShell session (see update). Or use [`Start-Job`](https://learn.microsoft.com/powershell/module/microsoft.powershell.core/start-job) and capture the streams – iRon Apr 03 '23 at 13:09
  • (apart from the fact that your code for some reason errors out with `PowerShell : The handle is invalid.`, and i don't understand what it means) Yes, having an isolated powershell session would work, except that the new process output would always be captured as string. the example function would return the string `True` instead of the boolean – aetonsi Apr 04 '23 at 07:38
0

This never has an exception as long as sccm client is installed (get-package 'configuration manager client'):

# or Invoke-WmiMethod
$myargs = @{ namespace = 'root\ccm\clientsdk'
             class = 'CCM_ClientUtilities'
             name = 'DetermineIfRebootPending' }
Invoke-CimMethod @myargs | select RebootPending

RebootPending
-------------
False
js2010
  • 23,033
  • 6
  • 64
  • 66
  • as i wrote at the end of the question, my point is not the function itself, my point is how to catch/suppress casting errors – aetonsi Apr 03 '23 at 21:18