Archive

Archive for February, 2017

Using PowerShell to Add “Open PowerShell Here” and “Open Command Prompt Here” to Explorer Context Menus

February 17th, 2017 1 comment

Description

I recently reloaded my primary workstation. When I do that, I run my PowerShell profile file manually once, and it configures quite a few things, including dot sourcing that same file into the main profile file, which it also creates. When I was all done, I realized I didn’t have the “Open PowerShell Here” options when right clicking in Windows Explorer. Being old and forgetful, I did some searching and was quite surprised (and disappointed) that every method for setting this was either using an .inf file, a .reg file, or manually editing the registry. What a travesty that no one was showing how to do it via PowerShell. So a bottle of Mountain Dew Throwback and some Pink Floyd was all I needed.

The right-click options can be visible in three different places. The first is when you right-click a folder. Second is when you right-click a root drive. And the third is when you right-click the background in Explorer. The method I’ll be showing here will include setting the option for all three. Additionally, I’ll show how to add an option to open an elevated PowerShell session, as well as a command prompt. When finished, you’ll end up with something like this:

All options are configured in the HKEY_CLASSES_ROOT hive of the registry. The folder level option resides under \Directory\shell; the root drive option resides under \Drive\shell; and the Explorer background option resides under \Directory\Background\shell. For each, we create a new key with a unique name for each of the options (PowerShell, PowerShell elevated, cmd prompt), some values for the display text and icon, and a sub key with a value that shows what commands will run when executed. This is easily accomplished using the New-Item and New-ItemProperty cmdlets in an elevated PowerShell session.

Add “Open PowerShell Here”

#folders
New-Item -ItemType String -Path "Registry::HKEY_CLASSES_ROOT\Directory\shell\PowerShellHere" -Value "Open PowerShell Here" -Force | Out-Null
New-Item -ItemType String -Path "Registry::HKEY_CLASSES_ROOT\Directory\shell\PowerShellHere\command" -Value "$env:SystemRoot\system32\WindowsPowerShell\v1.0\powershell.exe -NoExit -Command Set-Location '%V'" -Force | Out-Null
New-ItemProperty -PropertyType String -Path "Registry::HKEY_CLASSES_ROOT\Directory\shell\PowerShellHere" -Name "Icon" -Value "$env:SystemRoot\system32\WindowsPowerShell\v1.0\powershell.exe" -Force | Out-Null
#background
New-Item -ItemType String -Path "Registry::HKEY_CLASSES_ROOT\Directory\Background\shell\PowerShellHere" -Value "Open PowerShell Here" -Force | Out-Null
New-Item -ItemType String -Path "Registry::HKEY_CLASSES_ROOT\Directory\Background\shell\PowerShellHere\command" -Value "$env:SystemRoot\system32\WindowsPowerShell\v1.0\powershell.exe -NoExit -Command Set-Location '%V'" -Force | Out-Null
New-ItemProperty -PropertyType String -Path "Registry::HKEY_CLASSES_ROOT\Directory\Background\shell\PowerShellHere" -Name "Icon" -Value "$env:SystemRoot\system32\WindowsPowerShell\v1.0\powershell.exe" -Force | Out-Null
#root drives
New-Item -ItemType String -Path "Registry::HKEY_CLASSES_ROOT\Drive\shell\PowerShellHere" -Value "Open PowerShell Here" -Force | Out-Null
New-Item -ItemType String -Path "Registry::HKEY_CLASSES_ROOT\Drive\shell\PowerShellHere\command" -Value "$env:SystemRoot\system32\WindowsPowerShell\v1.0\powershell.exe -NoExit -Command Set-Location '%V'" -Force | Out-Null
New-ItemProperty -PropertyType String -Path "Registry::HKEY_CLASSES_ROOT\Drive\shell\PowerShellHere" -Name "Icon" -Value "$env:SystemRoot\system32\WindowsPowerShell\v1.0\powershell.exe" -Force | Out-Null

Add “Open PowerShell Here (Admin)”

#folders
New-Item -ItemType String -Path "Registry::HKEY_CLASSES_ROOT\Directory\shell\PowerShellHereAdmin" -Value "Open PowerShell Here (Admin)" -Force | Out-Null
New-Item -ItemType String -Path "Registry::HKEY_CLASSES_ROOT\Directory\shell\PowerShellHereAdmin\command" -Value "PowerShell -Command `"Start-Process $env:SystemRoot\system32\WindowsPowerShell\v1.0\powershell.exe`" -verb runAs -ArgumentList '-NoExit','Set-Location','''%L'''" -Force | Out-Null
New-ItemProperty -PropertyType String -Path "Registry::HKEY_CLASSES_ROOT\Directory\shell\PowerShellHereAdmin" -Name "Icon" -Value "$env:SystemRoot\system32\WindowsPowerShell\v1.0\powershell.exe,1" -Force | Out-Null
#background
New-Item -ItemType String -Path "Registry::HKEY_CLASSES_ROOT\Directory\Background\shell\PowerShellHereAdmin" -Value "Open PowerShell Here (Admin)" -Force | Out-Null
New-Item -ItemType String -Path "Registry::HKEY_CLASSES_ROOT\Directory\Background\shell\PowerShellHereAdmin\command" -Value "PowerShell -Command `"Start-Process $env:SystemRoot\system32\WindowsPowerShell\v1.0\powershell.exe`" -verb runAs -ArgumentList '-NoExit','Set-Location','''%V'''" -Force | Out-Null
New-ItemProperty -PropertyType String -Path "Registry::HKEY_CLASSES_ROOT\Directory\Background\shell\PowerShellHereAdmin" -Name "Icon" -Value "$env:SystemRoot\system32\WindowsPowerShell\v1.0\powershell.exe,1" -Force | Out-Null
#root drives
New-Item -ItemType String -Path "Registry::HKEY_CLASSES_ROOT\Drive\shell\PowerShellHereAdmin" -Value "Open PowerShell Here (Admin)" -Force | Out-Null
New-Item -ItemType String -Path "Registry::HKEY_CLASSES_ROOT\Drive\shell\PowerShellHereAdmin\command" -Value "PowerShell -Command `"Start-Process $env:SystemRoot\system32\WindowsPowerShell\v1.0\powershell.exe`" -verb runAs -ArgumentList '-NoExit','Set-Location','''%L'''" -Force | Out-Null
New-ItemProperty -PropertyType String -Path "Registry::HKEY_CLASSES_ROOT\Drive\shell\PowerShellHereAdmin" -Name "Icon" -Value "$env:SystemRoot\system32\WindowsPowerShell\v1.0\powershell.exe,1" -Force | Out-Null

Add “Open Command Prompt Here”

#folders
New-Item -Path "Registry::HKEY_CLASSES_ROOT\Directory\shell\CmdHere" -Value "Open Command Prompt Here" -ItemType string -Force | Out-Null
New-Item -Path "Registry::HKEY_CLASSES_ROOT\Directory\shell\CmdHere\command" -Value 'cmd.exe /k pushd %L' -ItemType string -Force | Out-Null
New-ItemProperty -Path "Registry::HKEY_CLASSES_ROOT\Directory\shell\CmdHere" -Name "Icon" -Value "C:\Windows\System32\cmd.exe,0" -Type string -Force | Out-Null
#background
New-Item -Path "Registry::HKEY_CLASSES_ROOT\Directory\Background\shell\CmdHere" -Value "Open Command Prompt Here" -ItemType string -Force | Out-Null
New-Item -Path "Registry::HKEY_CLASSES_ROOT\Directory\Background\shell\CmdHere\command" -Value 'cmd.exe /k pushd %L' -ItemType string -Force | Out-Null
New-ItemProperty -Path "Registry::HKEY_CLASSES_ROOT\Directory\Background\shell\CmdHere" -Name "Icon" -Value "C:\Windows\System32\cmd.exe,0" -Type string -Force | Out-Null
#root drives
New-Item -Path "Registry::HKEY_CLASSES_ROOT\Drive\shell\CmdHere" -Value "Open Command Prompt Here" -ItemType string -Force | Out-Null
New-Item -Path "Registry::HKEY_CLASSES_ROOT\Drive\shell\CmdHere\command" -Value 'cmd.exe /k pushd %L' -ItemType string -Force | Out-Null
New-ItemProperty -Path "Registry::HKEY_CLASSES_ROOT\Drive\shell\CmdHere" -Name "Icon" -Value "C:\Windows\System32\cmd.exe,0" -Type string -Force | Out-Null

Now we can put that all together, and add some checks to ensure the settings are not already configured, to avoid errors.

# folders
if(-not (Test-Path -Path "Registry::HKEY_CLASSES_ROOT\Directory\shell\PowerShellHere")){
    New-Item -ItemType String -Path "Registry::HKEY_CLASSES_ROOT\Directory\shell\PowerShellHere" -Value "Open PowerShell Here" -Force | Out-Null
    New-Item -ItemType String -Path "Registry::HKEY_CLASSES_ROOT\Directory\shell\PowerShellHere\command" -Value "$env:SystemRoot\system32\WindowsPowerShell\v1.0\powershell.exe -NoExit -Command Set-Location '%V'" -Force | Out-Null
    New-ItemProperty -PropertyType String -Path "Registry::HKEY_CLASSES_ROOT\Directory\shell\PowerShellHere" -Name "Icon" -Value "$env:SystemRoot\system32\WindowsPowerShell\v1.0\powershell.exe" -Force | Out-Null
}
if(-not (Test-Path -Path "Registry::HKEY_CLASSES_ROOT\Directory\shell\PowerShellHereAdmin")){
    New-Item -ItemType String -Path "Registry::HKEY_CLASSES_ROOT\Directory\shell\PowerShellHereAdmin" -Value "Open PowerShell Here (Admin)" -Force | Out-Null
    New-Item -ItemType String -Path "Registry::HKEY_CLASSES_ROOT\Directory\shell\PowerShellHereAdmin\command" -Value "PowerShell -Command `"Start-Process $env:SystemRoot\system32\WindowsPowerShell\v1.0\powershell.exe`" -verb runAs -ArgumentList '-NoExit','cd','%V'" -Force | Out-Null
    New-ItemProperty -PropertyType String -Path "Registry::HKEY_CLASSES_ROOT\Directory\shell\PowerShellHereAdmin" -Name "Icon" -Value "$env:SystemRoot\system32\WindowsPowerShell\v1.0\powershell.exe,1" -Force | Out-Null
}
if(-not (Test-Path -Path "Registry::HKEY_CLASSES_ROOT\Directory\shell\CmdHere")){
    New-Item -Path "Registry::HKEY_CLASSES_ROOT\Directory\shell\CmdHere" -Value "Open Command Prompt Here" -ItemType string -Force | Out-Null
    New-Item -Path "Registry::HKEY_CLASSES_ROOT\Directory\shell\CmdHere\command" -Value 'cmd.exe /k pushd %L' -ItemType string -Force | Out-Null
    New-ItemProperty -Path "Registry::HKEY_CLASSES_ROOT\Directory\shell\CmdHere" -Name "Icon" -Value "C:\Windows\System32\cmd.exe,0" -Type string -Force | Out-Null
}

# Explorer background
if(-not (Test-Path -Path "Registry::HKEY_CLASSES_ROOT\Directory\Background\shell\PowerShellHere")){
    New-Item -ItemType String -Path "Registry::HKEY_CLASSES_ROOT\Directory\Background\shell\PowerShellHere" -Value "Open PowerShell Here" -Force | Out-Null
    New-Item -ItemType String -Path "Registry::HKEY_CLASSES_ROOT\Directory\Background\shell\PowerShellHere\command" -Value "$env:SystemRoot\system32\WindowsPowerShell\v1.0\powershell.exe -NoExit -Command Set-Location '%V'" -Force | Out-Null
    New-ItemProperty -PropertyType String -Path "Registry::HKEY_CLASSES_ROOT\Directory\Background\shell\PowerShellHere" -Name "Icon" -Value "$env:SystemRoot\system32\WindowsPowerShell\v1.0\powershell.exe" -Force | Out-Null
}
if(-not (Test-Path -Path "Registry::HKEY_CLASSES_ROOT\Directory\Background\shell\PowerShellHereAdmin")){
    New-Item -ItemType String -Path "Registry::HKEY_CLASSES_ROOT\Directory\Background\shell\PowerShellHereAdmin" -Value "Open PowerShell Here (Admin)" -Force | Out-Null
    New-Item -ItemType String -Path "Registry::HKEY_CLASSES_ROOT\Directory\Background\shell\PowerShellHereAdmin\command" -Value "PowerShell -Command `"Start-Process $env:SystemRoot\system32\WindowsPowerShell\v1.0\powershell.exe`" -verb runAs -ArgumentList '-NoExit','cd','%V'" -Force | Out-Null
    New-ItemProperty -PropertyType String -Path "Registry::HKEY_CLASSES_ROOT\Directory\Background\shell\PowerShellHereAdmin" -Name "Icon" -Value "$env:SystemRoot\system32\WindowsPowerShell\v1.0\powershell.exe,1" -Force | Out-Null
}
if(-not (Test-Path -Path "Registry::HKEY_CLASSES_ROOT\Directory\Background\shell\CmdHere")){
    New-Item -Path "Registry::HKEY_CLASSES_ROOT\Directory\Background\shell\CmdHere" -Value "Open Command Prompt Here" -ItemType string -Force | Out-Null
    New-Item -Path "Registry::HKEY_CLASSES_ROOT\Directory\Background\shell\CmdHere\command" -Value 'cmd.exe /k pushd %L' -ItemType string -Force | Out-Null
    New-ItemProperty -Path "Registry::HKEY_CLASSES_ROOT\Directory\Background\shell\CmdHere" -Name "Icon" -Value "C:\Windows\System32\cmd.exe,0" -Type string -Force | Out-Null
}

# root drives
if(-not (Test-Path -Path "Registry::HKEY_CLASSES_ROOT\Drive\shell\PowerShellHere")){
    New-Item -ItemType String -Path "Registry::HKEY_CLASSES_ROOT\Drive\shell\PowerShellHere" -Value "Open PowerShell Here" -Force | Out-Null
    New-Item -ItemType String -Path "Registry::HKEY_CLASSES_ROOT\Drive\shell\PowerShellHere\command" -Value "$env:SystemRoot\system32\WindowsPowerShell\v1.0\powershell.exe -NoExit -Command Set-Location '%V'" -Force | Out-Null
    New-ItemProperty -PropertyType String -Path "Registry::HKEY_CLASSES_ROOT\Drive\shell\PowerShellHere" -Name "Icon" -Value "$env:SystemRoot\system32\WindowsPowerShell\v1.0\powershell.exe" -Force | Out-Null
}
if(-not (Test-Path -Path "Registry::HKEY_CLASSES_ROOT\Drive\shell\PowerShellHereAdmin")){
    New-Item -ItemType String -Path "Registry::HKEY_CLASSES_ROOT\Drive\shell\PowerShellHereAdmin" -Value "Open PowerShell Here (Admin)" -Force | Out-Null
    New-Item -ItemType String -Path "Registry::HKEY_CLASSES_ROOT\Drive\shell\PowerShellHereAdmin\command" -Value "PowerShell -Command `"Start-Process $env:SystemRoot\system32\WindowsPowerShell\v1.0\powershell.exe`" -verb runAs -ArgumentList '-NoExit','cd','%V'" -Force | Out-Null
    New-ItemProperty -PropertyType String -Path "Registry::HKEY_CLASSES_ROOT\Drive\shell\PowerShellHereAdmin" -Name "Icon" -Value "$env:SystemRoot\system32\WindowsPowerShell\v1.0\powershell.exe,1" -Force | Out-Null
}
if(-not (Test-Path -Path "Registry::HKEY_CLASSES_ROOT\Drive\shell\CmdHere")){
    New-Item -Path "Registry::HKEY_CLASSES_ROOT\Drive\shell\CmdHere" -Value "Open Command Prompt Here" -ItemType string -Force | Out-Null
    New-Item -Path "Registry::HKEY_CLASSES_ROOT\Drive\shell\CmdHere\command" -Value 'cmd.exe /k pushd %L' -ItemType string -Force | Out-Null
    New-ItemProperty -Path "Registry::HKEY_CLASSES_ROOT\Drive\shell\CmdHere" -Name "Icon" -Value "C:\Windows\System32\cmd.exe,0" -Type string -Force | Out-Null
}

Since the file that’s dot sourced in my PowerShell profile is shared among all of my machines, I tossed the above code in it to ensure that all machines are configured this way, and to ensure that if I reload one of them, it gets reconfigured with no additional work.

Moving to sub-menu

If your right-click menu is already full of other options, and you’d rather have them on a menu when you hit SHIFT+Right Click, that’s easy. We add a property called “extended”, with an empty value, to the key for each option. We can do them all at once using this code (after running the above code first):

#folders
if(-Not (Get-ItemProperty -Path "Registry::HKEY_CLASSES_ROOT\Directory\shell\PowerShellHere" -Name "extended")){
    New-ItemProperty -PropertyType String -Path "Registry::HKEY_CLASSES_ROOT\Directory\shell\PowerShellHere" -Name "extended" -Value $null | Out-Null
}
if(-Not (Get-ItemProperty -Path "Registry::HKEY_CLASSES_ROOT\Directory\shell\PowerShellHereAdmin" -Name "extended")){
    New-ItemProperty -PropertyType String -Path "Registry::HKEY_CLASSES_ROOT\Directory\shell\PowerShellHereAdmin" -Name "extended" -Value $null | Out-Null
}
if(-Not (Get-ItemProperty -Path "Registry::HKEY_CLASSES_ROOT\Directory\shell\CmdPromptHere" -Name "extended")){
    New-ItemProperty -PropertyType String -Path "Registry::HKEY_CLASSES_ROOT\Directory\shell\CmdPromptHere" -Name "extended" -Value $null | Out-Null
}

#background
if(-Not (Get-ItemProperty -Path "Registry::HKEY_CLASSES_ROOT\Directory\Background\shell\PowerShellHere" -Name "extended")){
    New-ItemProperty -PropertyType String -Path "Registry::HKEY_CLASSES_ROOT\Directory\Background\shell\PowerShellHere" -Name "extended" -Value $null | Out-Null
}
if(-Not (Get-ItemProperty -Path "Registry::HKEY_CLASSES_ROOT\Directory\Background\shell\PowerShellHereAdmin" -Name "extended")){
    New-ItemProperty -PropertyType String -Path "Registry::HKEY_CLASSES_ROOT\Directory\Background\shell\PowerShellHereAdmin" -Name "extended" -Value $null | Out-Null
}
if(-Not (Get-ItemProperty -Path "Registry::HKEY_CLASSES_ROOT\Directory\Background\shell\CmdPromptHere" -Name "extended")){
    New-ItemProperty -PropertyType String -Path "Registry::HKEY_CLASSES_ROOT\Directory\Background\shell\CmdPromptHere" -Name "extended" -Value $null | Out-Null
}

#root drives
if(-Not (Get-ItemProperty -Path "Registry::HKEY_CLASSES_ROOT\Drive\shell\PowerShellHere" -Name "extended")){
    New-ItemProperty -PropertyType String -Path "Registry::HKEY_CLASSES_ROOT\Drive\shell\PowerShellHere" -Name "extended" -Value $null | Out-Null
}
if(-Not (Get-ItemProperty -Path "Registry::HKEY_CLASSES_ROOT\Drive\shell\PowerShellHereAdmin" -Name "extended")){
    New-ItemProperty -PropertyType String -Path "Registry::HKEY_CLASSES_ROOT\Drive\shell\PowerShellHereAdmin" -Name "extended" -Value $null | Out-Null
}
if(-Not (Get-ItemProperty -Path "Registry::HKEY_CLASSES_ROOT\Drive\shell\CmdPromptHere" -Name "extended")){
    New-ItemProperty -PropertyType String -Path "Registry::HKEY_CLASSES_ROOT\Drive\shell\CmdPromptHere" -Name "extended" -Value $null | Out-Null
}

Removing the options

If you decide you’re not a fan of the options, removing them is really easy using the Remove-Item cmdlet.

#folders
if(Test-Path -Path "Registry::HKEY_CLASSES_ROOT\Directory\shell\PowerShellHere"){
    Remove-Item -Path "Registry::HKEY_CLASSES_ROOT\Directory\shell\PowerShellHere" -Recurse -Confirm:$False
}
if(Test-Path -Path "Registry::HKEY_CLASSES_ROOT\Directory\shell\PowerShellHereAdmin"){
    Remove-Item -Path "Registry::HKEY_CLASSES_ROOT\Directory\shell\PowerShellHereAdmin" -Recurse -Confirm:$False
}
if(Test-Path -Path "Registry::HKEY_CLASSES_ROOT\Directory\shell\CmdPromptHere"){
    Remove-Item -Path "Registry::HKEY_CLASSES_ROOT\Directory\shell\CmdPromptHere" -Recurse -Confirm:$False
}

#background
if(Test-Path -Path "Registry::HKEY_CLASSES_ROOT\Directory\Background\shell\PowerShellHere"){
    Remove-Item -Path "Registry::HKEY_CLASSES_ROOT\Directory\Background\shell\PowerShellHere" -Recurse -Confirm:$False
}
if(Test-Path -Path "Registry::HKEY_CLASSES_ROOT\Directory\Background\shell\PowerShellHereAdmin"){
    Remove-Item -Path "Registry::HKEY_CLASSES_ROOT\Directory\Background\shell\PowerShellHereAdmin" -Recurse -Confirm:$False
}
if(Test-Path -Path "Registry::HKEY_CLASSES_ROOT\Directory\Background\shell\CmdPromptHere"){
    Remove-Item -Path "Registry::HKEY_CLASSES_ROOT\Directory\Background\shell\CmdPromptHere" -Recurse -Confirm:$False
}

#root drives
if(Test-Path -Path "Registry::HKEY_CLASSES_ROOT\Drive\shell\PowerShellHere"){
    Remove-Item -Path "Registry::HKEY_CLASSES_ROOT\Drive\shell\PowerShellHere" -Recurse -Confirm:$False
}
if(Test-Path -Path "Registry::HKEY_CLASSES_ROOT\Drive\shell\PowerShellHereAdmin"){
    Remove-Item -Path "Registry::HKEY_CLASSES_ROOT\Drive\shell\PowerShellHereAdmin" -Recurse -Confirm:$False
}
if(Test-Path -Path "Registry::HKEY_CLASSES_ROOT\Drive\shell\CmdPromptHere"){
    Remove-Item -Path "Registry::HKEY_CLASSES_ROOT\Drive\shell\CmdPromptHere" -Recurse -Confirm:$False
}

Donations

I’ve never been one to really solicit donations for my work. My offerings are created because *I* need to solve a problem, and once I do, it makes sense to offer the results of my work to the public. I mean, let’s face it: I can’t be the only one with that particular issue, right? Quite often, to my surprise, I’m asked why I don’t have a “donate” button so people can donate a few bucks. I’ve never really put much thought into it. But those inquiries are coming more often now, so I’m yielding to them. If you’d like to donate, you can send a few bucks via PayPal at https://www.paypal.me/PatRichard. Money collected from that will go to the costs of my website (hosting and domain names), as well as to my home lab.

 

Categories: PowerShell Tags: