2

I am trying to make a powershell script that adds some registry keys. But (I think) I am failing on the * folder under HKCR being recognized as a wildcard The goal of the powershell is to provide me with an "Open with Notepad++" option in the right click context menu of Windows 11, preferably (but not necessary) only for .bat and .cmd files.

The first 3 keys are being processed fine, but as soon as the 4th key needs to be handled it seems like it is running forever, no output is being generated anymore and the script doesn't finish.

The line that I think is causing the issue is: 'HKCR:`*\shell\Open with Notepad++'

My original code didn't have the backtick before the asterix, but without, with single and with double backtick before the asterix, the issue remains the same.

Thank you for your help!

# Check if 'HKEY_CLASSES_ROOT' path has been set
If (Get-PSDrive | 
    Where {
            ($PSitem.Name -Match 'HKCR') -and 
            ($PSitem.Root -Match 'HKEY_CLASSES_ROOT')
          }
   )
    {
        
}

Else {
    New-PSDrive -PSProvider registry -Root "HKEY_CLASSES_ROOT" -Name "HKCR" >$Null 2>&1
}

# Define the registry values to add/update
$registryValues = @(
    @{
        Path = 'HKCR:\batfile\shell\Open with\command'
        Name = '(default)'
        Value = '{09799AFB-AD67-11d1-ABCD-00C04FC30936}'
    },
    @{
        Path = 'HKCR:\batfile\shell\edit\command'
        Name = '(default)'
        Value = '"C:\Program Files (x86)\Notepad++\notepad++.exe %1"'
    },
    @{
        Path = 'HKCR:\cmdfile\shell\edit\command'
        Name = '(default)'
        Value = '"C:\Program Files (x86)\Notepad++\notepad++.exe %1"'
    },
    @{
        Path = 'HKCR:\`*\shell\Open with Notepad++'
        Name = 'Icon'
        Value = 'C:\Program Files (x86)\Notepad++\notepad++.exe'
    },
    @{
        Path = 'HKCR:\`*\shell\Open with Notepad++\command'
        Name = '(default)'
        Value = '"C:\Program Files (x86)\Notepad++\notepad++.exe %1"'
    }
)

# Function to create or update registry values
function Set-RegistryValue {
    param(
        [string]$Path,
        [string]$Name,
        [string]$Value
    )

    # Create the key if it does not exist
    If (-NOT (Test-Path $Path)) {
        New-Item -Path $Path -Force | Out-Null
    }  

    # Now set the value
    New-ItemProperty -Path $Path -Name $Name -Value $Value -PropertyType String -Force
    
}

# Add or update the registry values
foreach ($entry in $registryValues) {
    Set-RegistryValue -Path $entry.Path -Name $entry.Name -Value $entry.Value
}
  • I could recreate the Problem and I think I know why this is happening. In my Tests the only Problem occured with `New-ItemProperty`. Please change the `-Path` to `-LiteralPath` in the `New-ItemProperty`-Line (and only there). If this also fixes your Problem, I will write an detailed answer later. – Paxz Jul 25 '23 at 13:15

1 Answers1

0

tl;dr

  • The only immediate fix required is to replace
    New-Item -Path $Path -Force with
    New-Item -Path $Path.Replace('`', '') -Force, i.e. to unescape your escaped path and use a literal one - see the explanation in the bottom section.

  • However, I suggest restructuring your code to use literal (verbatim) paths throughout, as shown in the next section.


Since you're only dealing with what are conceptually literal (verbatim) paths, I suggest avoiding attempts to escape wildcard metacharacters such as * with ` (`*), which not only simplifies the code, but is also more robust, given the problems with inconsistent escaping behavior (see GitHub issue #7999).

Note the following in the code below:

  • -LiteralPath is used in lieu of -Path with all cmdlets that support this distinction (literal (verbatim) vs. wildcard-based path).

    • New-Item is the cmdlet used that has only a -Path parameter, and its treatment of arguments passed to it is inconsistent:
      • Without a -Name argument, the -Path argument is used literally.
      • With a -Name argument, the -Path argument is interpreted as a wildcard pattern.
      • This problematic inconsistency is the subject of GitHub issue #17106, and explained in detail in this answer.
  • Since all paths are now interpreted literally by the target cmdlets, no escaping of * as `* is needed.

  • Instead of defining a HKCR: drive, the HKEY_CLASSES_ROOT hive is directly targeted, simply by prefixing the key paths with the name of the PowerShell provider, i.e. registry::HKEY_CLASSES_ROOT

# Define the registry values to add/update
$registryValues = @(
    # ... first 2 entries omitted for brevity
    @{
        LiteralPath = 'registry::HKEY_CLASSES_ROOT\*\shell\Open with Notepad++'
        Name = 'Icon'
        Value = 'C:\Program Files (x86)\Notepad++\notepad++.exe'
    },
    @{
        LiteralPath = 'registry::HKEY_CLASSES_ROOT\*\shell\Open with Notepad++\command'
        Name = '(default)'
        Value = '"C:\Program Files (x86)\Notepad++\notepad++.exe %1"'
    }
)

# Function to create or update registry values
function Set-RegistryValue {
    param(
        [string]$LiteralPath,
        [string]$Name,
        [string]$Value
    )

    # Create the key if it does not exist
    If (-NOT (Test-Path -LiteralPath $LiteralPath)) {
        New-Item -Path $LiteralPath -Force | Out-Null
    }  

    # Now set the value
    New-ItemProperty -LiteralPath $LiteralPath -Name $Name -Value $Value -PropertyType String -Force
    
}

# Add or update the registry values
foreach ($entry in $registryValues) {
    Set-RegistryValue -LiteralPath $entry.Path -Name $entry.Name -Value $entry.Value
}

As for what you tried:

  • Because New-Item (in the absence of -Name) interprets its -Path argument literally (verbatim), New-Item -Path $Path -Force created key paths literally starting with `* rather than with *.

    • For reasons unknown to me, your accidentally escaped key paths additionally result in excessively slow execution, as if wildcard matching happened behind the scenes, but the key path is ultimately created as specified, i.e. with the (undesired) ` before *.
  • By contrast, New-ItemProperty's -Path parameter does interpret its argument as wildcard patterns, so your escaped path works as intended in principle (though -LiteralPath with an unescaped path is preferable, as noted), but since the preceding New-Item command creates a different parent key than intended, the New-ItemProperty command as a whole fails.

mklement0
  • 382,024
  • 64
  • 607
  • 775