-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathVsDevShellManager.ps1
More file actions
171 lines (155 loc) · 8.86 KB
/
Copy pathVsDevShellManager.ps1
File metadata and controls
171 lines (155 loc) · 8.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
<#
.SYNOPSIS
Universal Developer Shell Context Menu Manager (v0.5.6).
.DESCRIPTION
Manages context menu entries for Visual Studio and standalone .NET SDKs.
Features logging, remote updates, and a coffee exit.
.USAGE
irm "https://raw.githubusercontent.com/ArtClark/DeveloperShellContextMenu/refs/heads/main/VsDevShellManager.ps1" | iex
.NOTES
Version: 0.5.6
#>
# --- Constants & Variables (Sorted Alphabetically) ---
$CoffeeUrl = "https://www.buymeacoffee.com/ArtClark"
$ContextNameCmd = "VsDevCmd"
$ContextNameDotNet = "DotNetShell"
$ContextNamePs = "VsDevPs"
$LogFile = Join-Path $env:TEMP "DeveloperShellContextMenu.log"
$ProjectUrl = "https://github.com/ArtClark/DeveloperShellContextMenu"
$ScriptVersion = "0.5.6"
$UninstallKey = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\DevShellManager"
$UpdateUrl = "https://raw.githubusercontent.com/ArtClark/DeveloperShellContextMenu/refs/heads/main/VsDevShellManager.ps1"
# --- Functions (Sorted Alphabetically) ---
function Add-RegistryEntry {
param ([string]$KeyPath, [string]$Label, [string]$Command, [string]$IconPath)
Write-Log "Action: Adding Registry Entry [$Label] at $KeyPath"
if (-not (Test-Path $KeyPath)) { New-Item -Path $KeyPath -Force | Out-Null }
Set-ItemProperty -Path $KeyPath -Name "(Default)" -Value $Label
Set-ItemProperty -Path $KeyPath -Name "Icon" -Value $IconPath
Set-ItemProperty -Path $KeyPath -Name "Version" -Value $ScriptVersion
$cmdPath = Join-Path $KeyPath "command"
if (-not (Test-Path $cmdPath)) { New-Item -Path $cmdPath -Force | Out-Null }
Set-ItemProperty -Path $cmdPath -Name "(Default)" -Value $Command
Register-Uninstaller -IconPath $IconPath
}
function Get-Status {
param ([string]$Path, [bool]$Available = $true)
if (-not $Available) { return " [N/A] " }
if (Test-Path $Path) {
$regVer = Get-ItemProperty -Path $Path -Name "Version" -ErrorAction SilentlyContinue
if ($null -ne $regVer.Version -and $regVer.Version -lt $ScriptVersion) {
return " [$(Write-Output "[System.Char]::ConvertFromUtf32(0x2191)")] "
}
return " [$(Write-Output "[System.Char]::ConvertFromUtf32(0x2714)")] "
}
return " [$(Write-Output "[System.Char]::ConvertFromUtf32(0x2718)")] "
}
function Get-VsEnvironment {
Write-Log "Status: Scanning for Developer Environments..."
try {
$dotNetExe = Get-Command dotnet -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Source
$vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
$vsInstalls = @()
if (Test-Path $vswhere) {
$vsInstalls = & $vswhere -all -format json | ConvertFrom-Json
}
return @{
VsData = if ($vsInstalls.Count -gt 0) { $vsInstalls | Sort-Object installationVersion -Descending | Select-Object -First 1 } else { $null };
DotNetPath = $dotNetExe
}
} catch {
Write-Log "ERROR: Exception during environment scan: $($_.Exception.Message)"
return @{ VsData = $null; DotNetPath = $null }
}
}
function Register-Uninstaller {
param ([string]$IconPath)
Write-Log "Action: Updating Windows registration and Support Links."
if (-not (Test-Path $UninstallKey)) { New-Item -Path $UninstallKey -Force | Out-Null }
$uninstaller = "powershell.exe -WindowStyle Hidden -Command ""$keys=@('$ContextNameCmd','$ContextNamePs','$ContextNameDotNet'); foreach(`$k in `$keys){ Get-ChildItem -Path 'HKCR:\Directory\shell','HKCR:\Directory\Background\shell','HKCR:\Drive\shell' | Where-Object Name -Like '*`$k' | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue }; Remove-Item -Path '$UninstallKey' -Force -ErrorAction SilentlyContinue"""
$updater = "powershell.exe -Command ""irm $UpdateUrl | iex"""
$props = @{
DisplayName = "Developer Shell Context Menus (Art Clark)"
UninstallString = $uninstaller
ModifyPath = $updater
DisplayIcon = $IconPath
DisplayVersion = $ScriptVersion
Publisher = "Art Clark"
HelpLink = "$ProjectUrl/issues"
URLInfoAbout = $ProjectUrl
NoModify = 0
NoRepair = 0
}
foreach ($name in $props.Keys) { Set-ItemProperty -Path $UninstallKey -Name $name -Value $props[$name] }
}
function Remove-RegistryEntries {
Write-Log "Action: Removing all Registry entries."
$paths = @("HKCR:\Directory\shell\$ContextNameCmd", "HKCR:\Directory\Background\shell\$ContextNameCmd", "HKCR:\Drive\shell\$ContextNameCmd",
"HKCR:\Directory\shell\$ContextNamePs", "HKCR:\Directory\Background\shell\$ContextNamePs", "HKCR:\Drive\shell\$ContextNamePs",
"HKCR:\Directory\shell\$ContextNameDotNet", "HKCR:\Directory\Background\shell\$ContextNameDotNet", "HKCR:\Drive\shell\$ContextNameDotNet",
$UninstallKey)
foreach ($p in $paths) { if (Test-Path $p) { Remove-Item -Path $p -Recurse -Force } }
Write-Host "`nCleanup Complete." -ForegroundColor Green; Start-Sleep -Seconds 1
}
function Show-MainMenu {
$Host.UI.RawUI.WindowTitle = "Developer Shell Context Menu Manager v$ScriptVersion"
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
$envData = Get-VsEnvironment
$vsAvail = $null -ne $envData.VsData
$sdkAvail = $null -ne $envData.DotNetPath
do {
# Clear-Host <-- Commented out per user request to see startup errors
Write-Host "`n--- Developer Context Menu Manager (v$ScriptVersion) ---" -ForegroundColor Green
Write-Host "Log Path: $LogFile" -ForegroundColor Gray
$regPs = "HKCR:\Directory\shell\$ContextNamePs"
$regCmd = "HKCR:\Directory\shell\$ContextNameCmd"
$regSdk = "HKCR:\Directory\shell\$ContextNameDotNet"
Write-Host "1.$(Get-Status $regPs $vsAvail)Visual Studio PowerShell"
Write-Host "2.$(Get-Status $regCmd $vsAvail)Visual Studio CMD"
Write-Host "3.$(Get-Status $regSdk $sdkAvail).NET SDK Shell [VS Code Friendly]"
Write-Host "4. View Log File"
Write-Host "5. Remove all options & Uninstall"
Write-Host "6. Exit and buy Art a coffee ☕" -ForegroundColor Yellow
$input = Read-Host "`nSelection"
if ($input -eq "1" -and $vsAvail) {
$vsRoot = $envData.VsData.installationPath
$dll = Get-ChildItem -Path $vsRoot -Filter "Microsoft.VisualStudio.DevShell.dll" -Recurse | Select-Object -First 1
$cmd = "powershell.exe -noe -c `"&{Import-Module '$($dll.FullName)'; Enter-VsDevShell $($envData.VsData.instanceId); Set-Location '%V'}`""
Add-RegistryEntry $regPs "VS Dev PowerShell Here" $cmd "powershell.exe"
Add-RegistryEntry "HKCR:\Directory\Background\shell\$ContextNamePs" "VS Dev PowerShell Here" $cmd "powershell.exe"
Add-RegistryEntry "HKCR:\Drive\shell\$ContextNamePs" "VS Dev PowerShell Here" $cmd "powershell.exe"
}
elseif ($input -eq "2" -and $vsAvail) {
$vsRoot = $envData.VsData.installationPath
$bat = Get-ChildItem -Path $vsRoot -Filter "VsDevCmd.bat" -Recurse | Select-Object -First 1
$cmd = "cmd.exe /k `"$($bat.FullName)`" && pushd `"%V`""
Add-RegistryEntry $regCmd "VS Dev CMD Here" $cmd "cmd.exe"
Add-RegistryEntry "HKCR:\Directory\Background\shell\$ContextNameCmd" "VS Dev CMD Here" $cmd "cmd.exe"
Add-RegistryEntry "HKCR:\Drive\shell\$ContextNameCmd" "VS Dev CMD Here" $cmd "cmd.exe"
}
elseif ($input -eq "3" -and $sdkAvail) {
$cmd = "powershell.exe -noe -c `"Set-Location '%V'; Write-Host '.NET SDK Shell Active' -ForegroundColor Yellow`""
Add-RegistryEntry $regSdk ".NET SDK Shell Here" $cmd "powershell.exe"
Add-RegistryEntry "HKCR:\Directory\Background\shell\$ContextNameDotNet" ".NET SDK Shell Here" $cmd "powershell.exe"
Add-RegistryEntry "HKCR:\Drive\shell\$ContextNameDotNet" ".NET SDK Shell Here" $cmd "powershell.exe"
}
elseif ($input -eq "4") { if (Test-Path $LogFile) { notepad.exe $LogFile } }
elseif ($input -eq "5") { Remove-RegistryEntries }
elseif ($input -eq "6") { Start-Process $CoffeeUrl; break }
} while ($true)
}
function Write-Log {
param ([string]$Message)
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
if (-not (Test-Path $LogFile)) {
"This file, 'DeveloperShellContextMenu.log', is safe to delete. It is used for debugging the Developer Shell Context Menu Manager script.`n" | Out-File -FilePath $LogFile
}
"[$timestamp] $Message" | Out-File -FilePath $LogFile -Append
}
# --- Main Execution ---
if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
Write-Host "ERROR: Admin rights required." -ForegroundColor Red
} else {
# Redirect all streams to the log file for the initial scan
Show-MainMenu
}