1
+ <#
2
+ Register or unregister the shell extension from the system context menu.
3
+
4
+ This is useful for debugging: unregister the shell extension, swap in your debug DLL, and then re-register
5
+
6
+ Must run as administrator
7
+
8
+ e.g.
9
+
10
+ . ./ExplorerCommand.ps1
11
+ Register-ExplorerCommand -FilePath "$Env:ProgramFiles\Devolutions\Agent\DevolutionsPedmShellExt.dll" -CLSID "{0ba604fd-4a5a-4abb-92b1-09ac5c3bf356}" -Verb "RunElevated" -MenuText "Run Elevated"
12
+ Unregister-ExplorerCommand -CLSID "{0ba604fd-4a5a-4abb-92b1-09ac5c3bf356}" -Verb "RunElevated"
13
+ #>
14
+
15
+ function Register-ExplorerCommand {
16
+ [CmdletBinding ()]
17
+ param (
18
+ [Parameter (Mandatory = $true )]
19
+ [string ]$FilePath ,
20
+
21
+ [Parameter (Mandatory = $true )]
22
+ [string ]$CLSID ,
23
+
24
+ [Parameter (Mandatory = $true )]
25
+ [string ]$Verb ,
26
+
27
+ [Parameter (Mandatory = $false )]
28
+ [string ]$MenuText = " Run Elevated" ,
29
+
30
+ [Parameter (Mandatory = $false )]
31
+ [string []]$Extensions = @ (" .exe" , " .msi" , " .lnk" , " .ps1" , " .bat" ) # Restrict to these
32
+ )
33
+
34
+ # Validate the DLL Path
35
+ if (! (Test-Path $FilePath )) {
36
+ Write-Error " ERROR: DLL path '$FilePath ' does not exist. Exiting."
37
+ return
38
+ }
39
+
40
+ Write-Host " ✅ DLL Path verified: $FilePath " - ForegroundColor Green
41
+
42
+ # Register CLSID in HKEY_CLASSES_ROOT\CLSID
43
+ $clsidPathHKCR = " Registry::HKEY_CLASSES_ROOT\CLSID\$CLSID "
44
+ if (Test-Path $clsidPathHKCR ) {
45
+ Write-Host " ⚠️ CLSID already exists in registry: $CLSID " - ForegroundColor Yellow
46
+ } else {
47
+ Write-Host " 🆕 Registering CLSID: $CLSID " - ForegroundColor Cyan
48
+ New-Item - Path $clsidPathHKCR - Force | Out-Null
49
+ Set-ItemProperty - Path $clsidPathHKCR - Name " (Default)" - Value " PedmShellExt"
50
+ Write-Host " ✅ CLSID registered in HKCR" - ForegroundColor Green
51
+ }
52
+
53
+ # Register InprocServer32
54
+ $inprocPathHKCR = " $clsidPathHKCR \InprocServer32"
55
+ if (! (Test-Path $inprocPathHKCR )) {
56
+ Write-Host " 🆕 Registering InprocServer32..." - ForegroundColor Cyan
57
+ New-Item - Path $inprocPathHKCR - Force | Out-Null
58
+ Set-ItemProperty - Path $inprocPathHKCR - Name " (Default)" - Value $FilePath
59
+ Set-ItemProperty - Path $inprocPathHKCR - Name " ThreadingModel" - Value " Apartment"
60
+ Write-Host " ✅ InprocServer32 registered" - ForegroundColor Green
61
+ }
62
+
63
+ # Register Explorer Command for Specific File Extensions
64
+ foreach ($ext in $Extensions ) {
65
+ $extKeyPath = " Registry::HKEY_CLASSES_ROOT\$ext "
66
+
67
+ # Find the associated file class (e.g., exefile for .exe)
68
+ try {
69
+ $fileClass = (Get-ItemProperty - Path $extKeyPath - ErrorAction Stop)." (Default)"
70
+ } catch {
71
+ Write-Host " ⚠️ No registry entry found for $ext . Skipping." - ForegroundColor Yellow
72
+ continue
73
+ }
74
+
75
+ # If no file class is found, assume the extension itself
76
+ if (-not $fileClass ) { $fileClass = $ext }
77
+
78
+ $commandPath = " Registry::HKEY_CLASSES_ROOT\$fileClass \shell\$Verb "
79
+
80
+ Write-Host " 🆕 Registering ExplorerCommand for: $ext -> $fileClass at $commandPath " - ForegroundColor Cyan
81
+
82
+ # Ensure the shell key exists
83
+ if (! (Test-Path " $commandPath " )) {
84
+ New-Item - Path $commandPath - Force | Out-Null
85
+ }
86
+
87
+ # Set menu text and ExplorerCommandHandler CLSID
88
+ Set-ItemProperty - Path $commandPath - Name " (Default)" - Value $MenuText
89
+ Set-ItemProperty - Path $commandPath - Name " ExplorerCommandHandler" - Value $CLSID
90
+ Set-ItemProperty - Path $commandPath - Name " MUIVerb" - Value " @FilePath,-150"
91
+ }
92
+
93
+ Add-Type - Namespace Win32 - Name NativeMethods - MemberDefinition @"
94
+ [System.Runtime.InteropServices.DllImport("shell32.dll")]
95
+ public static extern void SHChangeNotify(int wEventId, int uFlags, IntPtr dwItem1, IntPtr dwItem2);
96
+ "@
97
+
98
+ [Win32.NativeMethods ]::SHChangeNotify(0x8000000 , 0x1000 , [IntPtr ]::Zero, [IntPtr ]::Zero)
99
+
100
+ Write-Host " ✅ ExplorerCommand registered successfully for selected file types!" - ForegroundColor Green
101
+ }
102
+
103
+ function Unregister-ExplorerCommand {
104
+ [CmdletBinding ()]
105
+ param (
106
+ [Parameter (Mandatory = $true )]
107
+ [string ]$CLSID ,
108
+
109
+ [Parameter (Mandatory = $true )]
110
+ [string ]$Verb ,
111
+
112
+ [Parameter (Mandatory = $false )]
113
+ [string []]$Extensions = @ (" .exe" , " .msi" , " .lnk" , " .ps1" , " .bat" ) # Restrict to these
114
+ )
115
+
116
+ Write-Host " Unregistering ExplorerCommand with CLSID: $CLSID " - ForegroundColor Yellow
117
+
118
+ # Remove CLSID registration
119
+ $clsidPathHKCR = " Registry::HKEY_CLASSES_ROOT\CLSID\$CLSID "
120
+ if (Test-Path $clsidPathHKCR ) {
121
+ Remove-Item - Path $clsidPathHKCR - Force - Recurse - ErrorAction SilentlyContinue
122
+ Write-Host " ✅ Removed CLSID from HKCR" - ForegroundColor Green
123
+ } else {
124
+ Write-Host " ⚠️ CLSID not found in HKCR, skipping." - ForegroundColor Yellow
125
+ }
126
+
127
+ # Remove ExplorerCommand registry entry for specific file types
128
+ foreach ($ext in $Extensions ) {
129
+ $extKeyPath = " Registry::HKEY_CLASSES_ROOT\$ext "
130
+
131
+ # Find the associated file class (e.g., exefile for .exe)
132
+ try {
133
+ $fileClass = (Get-ItemProperty - Path $extKeyPath - ErrorAction Stop)." (Default)"
134
+ } catch {
135
+ Write-Host " ⚠️ No registry entry found for $ext . Skipping." - ForegroundColor Yellow
136
+ continue
137
+ }
138
+
139
+ # If no file class is found, assume the extension itself
140
+ if (-not $fileClass ) { $fileClass = $ext }
141
+
142
+ $commandPath = " Registry::HKEY_CLASSES_ROOT\$fileClass \shell\$Verb "
143
+
144
+ if (Test-Path $commandPath ) {
145
+ Write-Host " 🗑 Removing ExplorerCommand for: $ext -> $fileClass at $commandPath " - ForegroundColor Cyan
146
+ Remove-Item - Path $commandPath - Force - Recurse - ErrorAction SilentlyContinue
147
+ } else {
148
+ Write-Host " ⚠️ No registered menu for $ext ($fileClass ), skipping." - ForegroundColor Yellow
149
+ }
150
+ }
151
+
152
+ Add-Type - Namespace Win32 - Name NativeMethods - MemberDefinition @"
153
+ [System.Runtime.InteropServices.DllImport("shell32.dll")]
154
+ public static extern void SHChangeNotify(int wEventId, int uFlags, IntPtr dwItem1, IntPtr dwItem2);
155
+ "@
156
+
157
+ [Win32.NativeMethods ]::SHChangeNotify(0x8000000 , 0x1000 , [IntPtr ]::Zero, [IntPtr ]::Zero)
158
+
159
+ Write-Host " ✅ ExplorerCommand unregistered successfully!" - ForegroundColor Cyan
160
+ }
0 commit comments