简体   繁体   中英

Powershell object cannot be found with strictmode latest

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!

EeeLinkAdvertisement is an undefined property for many items you grab, so this is normal. You can cheat like this :

Get-ChildItem -LiteralPath 'hklm:\SYSTEM\ControlSet001\Control\Class' -Recurse -ErrorAction SilentlyContinue | `
   % {Get-ItemProperty -Path $_.pspath -ErrorAction SilentlyContinue | `
  ? {try{$_.EeeLinkAdvertisement}catch{}} -ErrorAction SilentlyContinue }

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 can break 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 collection.

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.


Avoiding errors when accessing non-existent properties in strict mode:

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
  • Pitfall as of v7.1 : If you apply ?. directly to a variable , you must unexpectedly enclose its name in {...} , eg ${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'

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM