I am trying to get below to work under set-strictmode -version latest
, it works completely fine without strict mode, unfortunately it's a requirement to have latest strictmode in my environment.
What it does: go through registry to find the entry with EEELinkAdvertisement and assign it to $findEeeLinkAd
$findEeeLinkAd = Get-ChildItem -LiteralPath 'hklm:\SYSTEM\ControlSet001\Control\Class' -Recurse -ErrorAction SilentlyContinue | `
% {Get-ItemProperty -Path $_.pspath -ErrorAction SilentlyContinue | `
? {$_.EeeLinkAdvertisement} -ErrorAction SilentlyContinue }
I receive a bunch of the following errors despite running in Administrator:
The property 'EEELinkAdvertisement' cannot be found on this object. Verify that the property exists.
At line:3 char:12
+ ? {$_.EEELinkAdvertisement} }
+ ~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], PropertyNotFoundException
+ FullyQualifiedErrorId : PropertyNotFoundStrict
Any help would be appreciated!
Note:
Unless you're prepared to thoroughly test and each every existing script / function / written-in-PowerShell module every time you upgrade to a new PowerShell version, I suggest avoiding Set-StrictMode -Version Latest
in production code - it's fine during development, but when it comes to publishing code, the then-current highest version should be specified explicitly and thereby locked in. If you don't do that, existing code may break if and when a new strictness check is introduced in a later PowerShell version.
The bottom section shows how to generally deal with attempts to access nonexistent properties with strict mode in effect.
You can streamline your code as follows, which implicitly bypasses the problem of trying to access a property not present on all input objects:
$rootKey = 'hklm:\SYSTEM\ControlSet001\Control\Class'
# Find all keys that have a 'EeeLinkAdvertisement' value.
$findEeeLinkAd =
Get-ChildItem -LiteralPath $rootKey -Recurse -ErrorAction Ignore |
ForEach-Object { if ($null -ne $_.GetValue('EeeLinkAdvertisement')) { $_ } }
Note the switch from -ErrorAction SilentlyContinue
to -ErrorAction Ignore
: the latter quietly discards any errors, whereas the former doesn't display them, but still records them in the automatic $Error
variable, which is a session-wide collection of all (non-ignored) errors.
This takes advantage of the fact that the Microsoft.Win32.RegistryKey.GetValue()
method quietly ignores attempts to retrieve data for a nonexistent value and returns $null
.
As an aside: It is virtually pointless to apply the common -ErrorAction
parameter to the Where-Object
(?
) and ForEach-Object
(%
) cmdlets, because the parameter is not applied to the code that runs inside the script blocks ({ ... }
) passed to these commands.
Set-StrictMode
with -Version 2
or higher (including Latest
) causes attempts to access a nonexistent property on an object to report a statement-terminating error.
Do note that this means that by default only the statement at hand is terminated, whereas overall script execution continues.
Also note that -Off
is the default value; that is, no strictness checks are performed by default, which means that even attempts to reference nonexistent variables do not trigger an error by default; Set-StrictMode -Version 1
checks for nonexistent variables, but not for nonexistent properties.
There are several ways of avoiding such errors:
Use a Try
/ Catch
statement around the property access, as also shown in CFou's answer; this also makes it easy to specify a default value in the absence of the property, but catching the exception is slow compared to the reflection-based approach below:
$o = [pscustomobject] @{ foo = 1 }
$propValue = try { $o.NoSuchProperty } catch { 'default value' }
Temporarily disable strict mode in a child scope (this is about as slow as the try
/ catch
approach):
$o = [pscustomobject] @{ foo = 1 }
# $propValue will effectively receive $null.
# Strictly speaking: [System.Management.Automation.Internal.AutomationNull]::Value
$propValue = & { Set-StrictMode -Off; $o.NoSuchProperty }
Use reflection to test a property's existence, via the hidden .psobject.Properties
member available on all objects; this is the fastest approach:
$o = [pscustomobject] @{ foo = 1 }
$propValue = if ($o.psobject.Properties['NoSuchProperty']) { $o.NoSuchProperty }
In PowerShell [Core] 7.1+, you can do this more succinctly, via the null-conditional operator ?.
:
$o = [pscustomobject] @{ foo = 1 }
# Note the `?.`, which only tries to access the property if the expression
# to the left isn't $null.
$propValue = $o.psobject.Properties['NoSuchProperty']?.Value
?.
directly to a variable, you must unexpectedly enclose its name in {...}
, e.g. ${var}?.Property
; $var?.Property
does not work as intended, because PowerShell then assumes the variable name is var?
- see GitHub issue #11379 for an attempt to change that.Similarly, the null-coalescing operator, ??
(which itself became available in v7.0) can simplify providing a default value (which in earlier versions you can achieve by adding an else
branch to the if
statement above):
$o = [pscustomobject] @{ foo = 1 }
# The RHS of `??` is used if the LHS evaluates to $null.
$propValue = $o.psobject.Properties['NoSuchProperty']?.Value ?? 'default value'