Skip to content

Commit ccc09aa

Browse files
committed
fix: auto-relaunch under Windows PowerShell 5.1 when invoked from pwsh
Running the script from PowerShell 7 (pwsh.exe) previously errored out with a MessageBox "Wrong PowerShell Edition" and exited. The script now detects PSEdition == 'Core' before any WPF load and re-spawns itself under powershell.exe (Windows PowerShell 5.1) with the full arg list preserved: CLI mode: Start-Process -Wait -NoNewWindow -PassThru so stdout/stderr/exit code return to the caller. If not elevated, adds -Verb RunAs so the single re-launch handles both edition swap and UAC in one prompt. GUI mode: Fire-and-forget new window (adds -Verb RunAs if not admin). If auto-launch fails (unlikely) the CLI path writes a clear error to stderr and the GUI path surfaces a MessageBox with the manual command. Arg forwarding factored out into Get-ForwardedArgString so both the edition re-host and the self-elevation code path use identical logic. Release v3.2.1.
1 parent bf25ecf commit ccc09aa

3 files changed

Lines changed: 77 additions & 26 deletions

File tree

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,16 @@ All notable changes to DefenderControl will be documented in this file.
44

55
## Unreleased
66

7+
## [v3.2.1] - 2026-04-24
8+
9+
### Fixed
10+
- Running the script from PowerShell 7 (pwsh.exe) no longer errors out with
11+
"Wrong PowerShell Edition". The script now detects PS 7 / Core and
12+
auto-relaunches itself under Windows PowerShell 5.1 (`powershell.exe`) with
13+
all original arguments preserved. CLI mode waits synchronously so stdout /
14+
stderr / exit codes return to the caller; GUI mode fires a new window. If
15+
the caller isn't already elevated, the re-launch also handles UAC.
16+
717
## [v3.2.0] - 2026-04-24
818

919
### Added

DefenderControl.ps1

Lines changed: 66 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<#
22
.SYNOPSIS
3-
Defender Control v3.2.0 - Comprehensive Microsoft Defender Disable/Enable Utility
3+
Defender Control v3.2.1 - Comprehensive Microsoft Defender Disable/Enable Utility
44
55
.DESCRIPTION
66
Professional WPF GUI + CLI tool to fully disable or re-enable Microsoft Defender
@@ -158,27 +158,77 @@ Note: -Mode Disable|Enable are reserved; use the GUI for mutating operations.
158158
}
159159

160160
# ==================================================================================
161-
# SELF-ELEVATION (forwards original args so CLI mode survives UAC re-launch)
161+
# ARG-FORWARD HELPER (reused by both edition-rehost and self-elevation)
162162
# ==================================================================================
163-
if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole(
164-
[Security.Principal.WindowsBuiltInRole]::Administrator)) {
165-
166-
# Rebuild the original argument list from $PSBoundParameters so
167-
# switches/values survive the UAC re-launch.
168-
$forwardedArgs = New-Object System.Collections.Generic.List[string]
163+
function Get-ForwardedArgString {
164+
$fwd = New-Object System.Collections.Generic.List[string]
169165
foreach ($kv in $PSBoundParameters.GetEnumerator()) {
170166
$name = $kv.Key
171167
$val = $kv.Value
172168
if ($val -is [System.Management.Automation.SwitchParameter]) {
173-
if ($val.IsPresent) { $forwardedArgs.Add("-$name") | Out-Null }
169+
if ($val.IsPresent) { $fwd.Add("-$name") | Out-Null }
174170
} else {
175-
$forwardedArgs.Add("-$name") | Out-Null
176-
$forwardedArgs.Add('"' + ($val -replace '"','\"') + '"') | Out-Null
171+
$fwd.Add("-$name") | Out-Null
172+
$fwd.Add('"' + ($val -replace '"','\"') + '"') | Out-Null
177173
}
178174
}
179-
$argList = "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`""
180-
if ($forwardedArgs.Count -gt 0) { $argList += " " + ($forwardedArgs -join ' ') }
175+
$base = "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`""
176+
if ($fwd.Count -gt 0) { return $base + " " + ($fwd -join ' ') }
177+
return $base
178+
}
179+
180+
# ==================================================================================
181+
# POWERSHELL EDITION CHECK -- auto-relaunch under Windows PowerShell 5.1
182+
# ==================================================================================
183+
# WPF (PresentationFramework) requires Windows PowerShell 5.1; PS 7 / Core does
184+
# not ship it on all installs. Instead of erroring out, we silently re-spawn
185+
# the script under powershell.exe (WinPS 5.1), preserving all original args.
186+
if ($PSVersionTable.PSEdition -eq 'Core') {
187+
$isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole(
188+
[Security.Principal.WindowsBuiltInRole]::Administrator)
189+
$argList = Get-ForwardedArgString
190+
$winPs = Join-Path $env:WINDIR 'System32\WindowsPowerShell\v1.0\powershell.exe'
191+
if (-not (Test-Path $winPs)) { $winPs = 'powershell.exe' } # fall back to PATH
181192

193+
try {
194+
if ($script:IsCliMode) {
195+
# CLI: wait synchronously so stdout/stderr + exit code return to caller.
196+
$spArgs = @{
197+
FilePath = $winPs
198+
ArgumentList = $argList
199+
Wait = $true
200+
PassThru = $true
201+
NoNewWindow = $true
202+
}
203+
if (-not $isAdmin) { $spArgs.Remove('NoNewWindow'); $spArgs['Verb'] = 'RunAs' }
204+
$proc = Start-Process @spArgs
205+
exit $proc.ExitCode
206+
} else {
207+
# GUI: fire-and-forget new window
208+
$spArgs = @{ FilePath = $winPs; ArgumentList = $argList }
209+
if (-not $isAdmin) { $spArgs['Verb'] = 'RunAs' }
210+
Start-Process @spArgs
211+
}
212+
} catch {
213+
if ($script:IsCliMode) {
214+
[Console]::Error.WriteLine("DefenderControl: requires Windows PowerShell 5.1 and $winPs could not be launched: $($_.Exception.Message)")
215+
exit $script:EXIT_USAGE
216+
}
217+
Add-Type -AssemblyName PresentationFramework
218+
[System.Windows.MessageBox]::Show(
219+
"This tool requires Windows PowerShell 5.1.`n`nCould not auto-launch powershell.exe.`n`nManual: powershell.exe -File `"$PSCommandPath`"",
220+
"Could not re-launch under Windows PowerShell", "OK", "Error") | Out-Null
221+
}
222+
exit
223+
}
224+
225+
# ==================================================================================
226+
# SELF-ELEVATION (forwards original args so CLI mode survives UAC re-launch)
227+
# ==================================================================================
228+
if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole(
229+
[Security.Principal.WindowsBuiltInRole]::Administrator)) {
230+
231+
$argList = Get-ForwardedArgString
182232
try {
183233
Start-Process powershell.exe -ArgumentList $argList -Verb RunAs
184234
} catch {
@@ -202,7 +252,7 @@ if (-not $script:IsCliMode) {
202252
Add-Type -AssemblyName PresentationFramework, PresentationCore, WindowsBase, System.Windows.Forms
203253
}
204254

205-
$script:Version = "3.2.0"
255+
$script:Version = "3.2.1"
206256
$script:DryRun = [bool]$DryRun
207257
$script:ShowVerbose = $true
208258

@@ -231,17 +281,8 @@ if ($script:OSBuild -lt 17763 -and -not $script:IsCliMode) {
231281
"Old Windows Build", "OK", "Warning") | Out-Null
232282
}
233283

234-
# Check PowerShell edition
235-
if ($PSVersionTable.PSEdition -eq 'Core') {
236-
if ($script:IsCliMode) {
237-
[Console]::Error.WriteLine("DefenderControl: requires Windows PowerShell 5.1 (not PowerShell 7+). Run with powershell.exe, not pwsh.")
238-
exit $script:EXIT_USAGE
239-
}
240-
[System.Windows.MessageBox]::Show(
241-
"This tool requires Windows PowerShell 5.1 (not PowerShell 7+).`n`nPlease run with: powershell.exe -File `"$PSCommandPath`"",
242-
"Wrong PowerShell Edition", "OK", "Error") | Out-Null
243-
exit
244-
}
284+
# PowerShell edition check happened earlier (auto-relaunches under WinPS 5.1).
285+
# By this point we are guaranteed to be on Windows PowerShell 5.1.
245286

246287
# ==================================================================================
247288
# CLI MODE: read-only state enumeration + dispatch

SHA256SUMS.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
c3b0c3a35af52eaeca1ca05057daf41a472eee4ca3b6d4a1648901be9ad1adb5 *DefenderControl.ps1
1+
698e240b0c422d64220615ca65dcd5c1a14112644fb90d1edad9d2c96e523693 *DefenderControl.ps1

0 commit comments

Comments
 (0)