Try/catch does not seem to have an effect Try/catch does not seem to have an effect powershell powershell

Try/catch does not seem to have an effect


I was able to duplicate your result when trying to run a remote WMI query. The exception thrown is not caught by the Try/Catch, nor will a Trap catch it, since it is not a "terminating error". In PowerShell, there are terminating errors and non-terminating errors . It appears that Try/Catch/Finally and Trap only works with terminating errors.

It is logged to the $error automatic variable and you can test for these type of non-terminating errors by looking at the $? automatic variable, which will let you know if the last operation succeeded ($true) or failed ($false).

From the appearance of the error generated, it appears that the error is returned and not wrapped in a catchable exception. Below is a trace of the error generated.

PS C:\scripts\PowerShell> Trace-Command -Name errorrecord  -Expression {Get-WmiObject win32_bios -ComputerName HostThatIsNotThere}  -PSHostDEBUG: InternalCommand Information: 0 :  Constructor Enter CtorMicrosoft.PowerShell.Commands.GetWmiObjectCommand: 25857563DEBUG: InternalCommand Information: 0 :  Constructor Leave CtorMicrosoft.PowerShell.Commands.GetWmiObjectCommand: 25857563DEBUG: ErrorRecord Information: 0 :  Constructor Enter CtorSystem.Management.Automation.ErrorRecord: 19621801 exception =System.Runtime.InteropServices.COMException (0x800706BA): The RPCserver is unavailable. (Exception from HRESULT: 0x800706BA)   atSystem.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode, IntPtr errorInfo)   at System.Management.ManagementScope.InitializeGuts(Object o)   at System.Management.ManagementScope.Initialize()   at System.Management.ManagementObjectSearcher.Initialize()   at System.Management.ManagementObjectSearcher.Get()   at Microsoft.PowerShell.Commands.GetWmiObjectCommand.BeginProcessing()errorId = GetWMICOMException errorCategory = InvalidOperationtargetObject =DEBUG: ErrorRecord Information: 0 :  Constructor Leave CtorSystem.Management.Automation.ErrorRecord: 19621801

A work around for your code could be:

try{    $colItems = get-wmiobject -class "Win32_PhysicalMemory" -namespace "root\CIMV2" -computername $strComputerName -Credential $credentials    if ($?)    {      foreach ($objItem in $colItems)       {          write-host "Bank Label: " $objItem.BankLabel          write-host "Capacity: " ($objItem.Capacity / 1024 / 1024)          write-host "Caption: " $objItem.Caption          write-host "Creation Class Name: " $objItem.CreationClassName                write-host      }    }    else    {       throw $error[0].Exception    }


If you want try/catch to work for all errors (not just the terminating errors) you can manually make all errors terminating by setting the ErrorActionPreference.

try {   $ErrorActionPreference = "Stop"; #Make all errors terminating   get-item filethatdoesntexist; # normally non-terminating   write-host "You won't hit me";  } catch{   Write-Host "Caught the exception";   Write-Host $Error[0].Exception;}finally{   $ErrorActionPreference = "Continue"; #Reset the error action pref to default}

Alternatively... you can make your own try/catch function that accepts scriptblocks so that your try/catch calls are not as kludge. I have mine return true/false just in case I need to check if there was an error... but it doesn't have to. Also, exception logging is optional, and can be taken care of in the catch, but I found myself always calling the logging function in the catch block, so I added it to the try/catch function.

function log([System.String] $text){write-host $text;}function logException{    log "Logging current exception.";    log $Error[0].Exception;}function mytrycatch ([System.Management.Automation.ScriptBlock] $try,                    [System.Management.Automation.ScriptBlock] $catch,                    [System.Management.Automation.ScriptBlock]  $finally = $({})){    # Make all errors terminating exceptions.    $ErrorActionPreference = "Stop";        # Set the trap    trap [System.Exception]{        # Log the exception.        logException;                # Execute the catch statement        & $catch;                # Execute the finally statement        & $finally                # There was an exception, return false        return $false;    }        # Execute the scriptblock    & $try;        # Execute the finally statement    & $finally        # The following statement was hit.. so there were no errors with the scriptblock    return $true;}#execute your own try catchmytrycatch {        gi filethatdoesnotexist; #normally non-terminating        write-host "You won't hit me."    } {        Write-Host "Caught the exception";    }


It is also possible to set the error action preference on individual cmdlets, not just for the whole script. This is done using the parameter ErrorAction (alisa EA) which is available on all cmdlets.

Example

try { Write-Host $ErrorActionPreference; #Check setting for ErrorAction - the default is normally Continue get-item filethatdoesntexist; # Normally generates non-terminating exception so not caught write-host "You will hit me as exception from line above is non-terminating";   get-item filethatdoesntexist -ErrorAction Stop; #Now ErrorAction parameter with value Stop causes exception to be caught  write-host "you won't reach me as exception is now caught";}catch{ Write-Host "Caught the exception"; Write-Host $Error[0].Exception;}