Skip to content

Instantly share code, notes, and snippets.

@elico
Created September 10, 2026 22:01
Show Gist options
  • Select an option

  • Save elico/f92e4e653de3971f7ed2619f23cbcdef to your computer and use it in GitHub Desktop.

Select an option

Save elico/f92e4e653de3971f7ed2619f23cbcdef to your computer and use it in GitHub Desktop.
win-kms-activation.ps1
# ============================================================
# KMS Activation Utility -- Enhanced Edition
# Author : ngtech1ltd@gmail.com
# Version: 2.3
# ============================================================
# --- Self-Elevation: relaunch with the SAME binary that started us ---
$isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole(
[Security.Principal.WindowsBuiltInRole]::Administrator)
if (-not $isAdmin) {
Write-Host "Elevating permissions to Administrator..." -ForegroundColor Yellow
try {
$scriptPath = $MyInvocation.MyCommand.Definition
if (-not $scriptPath) { $scriptPath = $PSCommandPath }
$currentExe = [System.Diagnostics.Process]::GetCurrentProcess().MainModule.FileName
Start-Process -FilePath $currentExe `
-ArgumentList "-NoProfile -ExecutionPolicy Bypass -File `"$scriptPath`"" `
-Verb RunAs
exit
} catch {
Write-Host "Elevation request failed or was cancelled." -ForegroundColor Red
Pause
exit
}
}
# --- Load WinForms ---
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
[System.Windows.Forms.Application]::EnableVisualStyles()
# ============================================================
# GVLK Table (official Microsoft KMS client setup keys)
# ============================================================
$GVLKTable = [ordered]@{
"Windows 10/11 Pro" = "W269N-WFGWX-YVC9B-4J6C9-T83GX"
"Windows 10/11 Pro Workstation" = "NRG8B-VKK3Q-CXVCJ-9G2XF-6Q84J"
"Windows 10/11 Enterprise" = "NPPR9-FWDCX-D2C8J-H872K-2YT43"
"Windows 10/11 Education" = "NW6C2-QMPVW-D7KKK-3GKT6-VCFB2"
"Windows Server 2025 Standard" = "TVRH6-WHNXV-R9WG3-9XRFY-MY832"
"Windows Server 2025 Datacenter" = "D764K-2NDRG-47T6Q-P8T8W-YP6DF"
"Windows Server 2025 Azure Edition" = "XGN3F-F394H-FD2MY-PP6FD-8MCRC"
"Windows Server 2022 Standard" = "VDYBN-27WPP-V4HQT-9VMD4-VMK7H"
"Windows Server 2022 Datacenter" = "WX4NM-KYWYW-QJJR4-VW3GC-62PVU"
"Windows Server 2019 Standard" = "N69G4-B89J2-4G8F4-WWYCC-J464C"
"Windows Server 2019 Datacenter" = "WMDGN-G9PQG-XVVXX-R3X43-FCF23"
"Windows Server 2016 Standard" = "WC2BQ-8NRM3-FDDYY-2BFGV-KHKQY"
"Windows Server 2016 Datacenter" = "CB7KF-BWN84-R7R2Y-793K2-8XDDG"
}
# Which editions are valid per OS family (used to filter the dropdown)
$EditionsByFamily = @{
"Desktop" = @(
"Windows 10/11 Pro"
"Windows 10/11 Pro Workstation"
"Windows 10/11 Enterprise"
"Windows 10/11 Education"
)
"Server2025" = @(
"Windows Server 2025 Standard"
"Windows Server 2025 Datacenter"
"Windows Server 2025 Azure Edition"
)
"Server2022" = @(
"Windows Server 2022 Standard"
"Windows Server 2022 Datacenter"
)
"Server2019" = @(
"Windows Server 2019 Standard"
"Windows Server 2019 Datacenter"
)
"Server2016" = @(
"Windows Server 2016 Standard"
"Windows Server 2016 Datacenter"
)
}
# ============================================================
# OS Detection
# ============================================================
function Get-OSInfo {
$os = Get-CimInstance Win32_OperatingSystem
$caption = $os.Caption
$reg = Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -ErrorAction SilentlyContinue
$editionId = if ($reg -and $reg.EditionID) { $reg.EditionID } else { "" }
$productName = if ($reg -and $reg.ProductName) { $reg.ProductName } else { $caption }
# Detect evaluation: OS caption/product name says "Evaluation",
# or the registry EditionID ends with "Eval" (e.g. ServerStandardEval)
$isEval = ($caption -match "Evaluation") -or
($productName -match "Evaluation") -or
($editionId -match "Eval$")
# Determine server version from caption or productName
$serverVersion = $null
foreach ($ver in @("2025","2022","2019","2016")) {
if ($caption -match "Server $ver" -or $productName -match "Server $ver") {
$serverVersion = $ver
break
}
}
$family = "Unknown"
if ($serverVersion) {
$family = "Server$serverVersion"
} elseif ($caption -match "Windows 10|Windows 11" -or $productName -match "Windows 10|Windows 11") {
$family = "Desktop"
}
return @{
Caption = $caption
ProductName = $productName
EditionId = $editionId
Family = $family
ServerVersion = $serverVersion
IsEvaluation = $isEval
}
}
# ============================================================
# Edition picker dialog (Standard vs Datacenter)
# ============================================================
function Show-EditionPicker {
param([string]$ServerVersion)
$pf = New-Object System.Windows.Forms.Form
$pf.Text = "Choose Target Edition"
$pf.Size = New-Object System.Drawing.Size(330, 190)
$pf.StartPosition = "CenterParent"
$pf.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::FixedDialog
$pf.MaximizeBox = $false
$pf.MinimizeBox = $false
$pf.Font = New-Object System.Drawing.Font("Segoe UI", 9)
# Apply current theme colours
$T = Get-Theme
$pf.BackColor = $T.FormBack
$pf.ForeColor = $T.LabelFore
$lbl = New-Object System.Windows.Forms.Label
$lbl.Location = New-Object System.Drawing.Point(12, 14)
$lbl.Size = New-Object System.Drawing.Size(296, 36)
$lbl.Text = "Upgrading Windows Server $ServerVersion Evaluation.`nSelect the target edition:"
$lbl.BackColor = [System.Drawing.Color]::FromArgb(0,0,0,0)
$pf.Controls.Add($lbl)
$rbStd = New-Object System.Windows.Forms.RadioButton
$rbStd.Location = New-Object System.Drawing.Point(20, 58)
$rbStd.Size = New-Object System.Drawing.Size(280, 22)
$rbStd.Text = "Standard"
$rbStd.Checked = $true
$rbStd.BackColor = [System.Drawing.Color]::FromArgb(0,0,0,0)
$rbStd.ForeColor = $T.LabelFore
$pf.Controls.Add($rbStd)
$rbDC = New-Object System.Windows.Forms.RadioButton
$rbDC.Location = New-Object System.Drawing.Point(20, 84)
$rbDC.Size = New-Object System.Drawing.Size(280, 22)
$rbDC.Text = "Datacenter"
$rbDC.BackColor = [System.Drawing.Color]::FromArgb(0,0,0,0)
$rbDC.ForeColor = $T.LabelFore
$pf.Controls.Add($rbDC)
$btnOK = New-Object System.Windows.Forms.Button
$btnOK.Location = New-Object System.Drawing.Point(150, 118)
$btnOK.Size = New-Object System.Drawing.Size(72, 26)
$btnOK.Text = "OK"
$btnOK.DialogResult = [System.Windows.Forms.DialogResult]::OK
$btnOK.FlatStyle = [System.Windows.Forms.FlatStyle]::Flat
$btnOK.BackColor = $T.BtnBack
$btnOK.ForeColor = $T.BtnFore
$btnOK.FlatAppearance.BorderSize = 0
$pf.Controls.Add($btnOK)
$pf.AcceptButton = $btnOK
$btnCancel = New-Object System.Windows.Forms.Button
$btnCancel.Location = New-Object System.Drawing.Point(232, 118)
$btnCancel.Size = New-Object System.Drawing.Size(72, 26)
$btnCancel.Text = "Cancel"
$btnCancel.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
$btnCancel.FlatStyle = [System.Windows.Forms.FlatStyle]::Flat
$btnCancel.BackColor = $T.DangerBack
$btnCancel.ForeColor = $T.BtnFore
$btnCancel.FlatAppearance.BorderSize = 0
$pf.Controls.Add($btnCancel)
$pf.CancelButton = $btnCancel
$res = $pf.ShowDialog()
$pf.Dispose()
if ($res -eq [System.Windows.Forms.DialogResult]::OK) {
if ($rbStd.Checked) { return "Standard" } else { return "Datacenter" }
}
return $null
}
# ============================================================
# Theme
# ============================================================
$script:IsDark = $true
function Get-Theme {
if ($script:IsDark) {
return @{
FormBack = [System.Drawing.Color]::FromArgb(30, 30, 30)
GroupBack = [System.Drawing.Color]::FromArgb(45, 45, 48)
CtrlBack = [System.Drawing.Color]::FromArgb(60, 60, 63)
CtrlFore = [System.Drawing.Color]::FromArgb(220, 220, 220)
LabelFore = [System.Drawing.Color]::FromArgb(200, 200, 200)
BtnBack = [System.Drawing.Color]::FromArgb(0, 120, 212)
BtnFore = [System.Drawing.Color]::White
DangerBack = [System.Drawing.Color]::FromArgb(180, 40, 40)
LogBack = [System.Drawing.Color]::FromArgb(20, 20, 20)
LogFore = [System.Drawing.Color]::FromArgb(0, 210, 110)
SBarBack = [System.Drawing.Color]::FromArgb(0, 100, 180)
SBarFore = [System.Drawing.Color]::White
}
} else {
return @{
FormBack = [System.Drawing.Color]::FromArgb(245, 245, 245)
GroupBack = [System.Drawing.Color]::White
CtrlBack = [System.Drawing.Color]::White
CtrlFore = [System.Drawing.Color]::FromArgb(30, 30, 30)
LabelFore = [System.Drawing.Color]::FromArgb(50, 50, 50)
BtnBack = [System.Drawing.Color]::FromArgb(0, 120, 212)
BtnFore = [System.Drawing.Color]::White
DangerBack = [System.Drawing.Color]::FromArgb(196, 43, 28)
LogBack = [System.Drawing.Color]::FromArgb(250, 250, 250)
LogFore = [System.Drawing.Color]::FromArgb(0, 100, 0)
SBarBack = [System.Drawing.Color]::FromArgb(0, 122, 204)
SBarFore = [System.Drawing.Color]::White
}
}
}
function Apply-Theme {
param([System.Windows.Forms.Control]$Root)
$T = Get-Theme
$queue = New-Object System.Collections.Generic.Queue[System.Windows.Forms.Control]
$queue.Enqueue($Root)
while ($queue.Count -gt 0) {
$c = $queue.Dequeue()
$tn = $c.GetType().Name
switch ($tn) {
"Form" { $c.BackColor = $T.FormBack ; $c.ForeColor = $T.LabelFore }
"GroupBox" { $c.BackColor = $T.GroupBack ; $c.ForeColor = $T.LabelFore }
"Label" { if ($c.Name -ne "lblLicenseWarn") { $c.BackColor = [System.Drawing.Color]::FromArgb(0,0,0,0) ; $c.ForeColor = $T.LabelFore } }
"TextBox" { $c.BackColor = $T.CtrlBack ; $c.ForeColor = $T.CtrlFore }
"ComboBox" { $c.BackColor = $T.CtrlBack ; $c.ForeColor = $T.CtrlFore }
"RadioButton" { $c.BackColor = [System.Drawing.Color]::FromArgb(0,0,0,0) ; $c.ForeColor = $T.LabelFore }
"CheckBox" { $c.BackColor = [System.Drawing.Color]::FromArgb(0,0,0,0) ; $c.ForeColor = $T.LabelFore }
"ProgressBar" { $c.BackColor = $T.CtrlBack }
"Button" {
$c.FlatStyle = [System.Windows.Forms.FlatStyle]::Flat
$c.FlatAppearance.BorderSize = 0
$c.BackColor = if ($c.Tag -eq "danger") { $T.DangerBack } else { $T.BtnBack }
$c.ForeColor = $T.BtnFore
}
}
if ($c.Name -eq "txtLog") { $c.BackColor = $T.LogBack ; $c.ForeColor = $T.LogFore }
foreach ($child in $c.Controls) { $queue.Enqueue($child) }
}
$script:statusStrip.BackColor = $T.SBarBack
$script:statusStrip.ForeColor = $T.SBarFore
$script:statusLabel.ForeColor = $T.SBarFore
$script:statusLabel.BackColor = $T.SBarBack
}
# ============================================================
# Helpers
# ============================================================
function Test-GVLKFormat {
param([string]$Key)
return $Key -match '^[A-Z0-9]{5}-[A-Z0-9]{5}-[A-Z0-9]{5}-[A-Z0-9]{5}-[A-Z0-9]{5}$'
}
function Get-CurrentKMSSettings {
try {
$sls = Get-CimInstance -ClassName SoftwareLicensingService
$server = $sls.KeyManagementServiceMachine
$port = $sls.KeyManagementServicePort
if ([string]::IsNullOrWhiteSpace($server)) { $server = "(not set)" }
if (-not $port -or $port -eq 0) { $port = 1688 }
return @{ Server = $server; Port = $port }
} catch {
return @{ Server = "Error reading"; Port = 1688 }
}
}
function Write-Log {
param([string]$Message)
$ts = Get-Date -Format "HH:mm:ss"
$script:txtLog.AppendText("[$ts] $Message`r`n")
$script:txtLog.ScrollToCaret()
}
function Refresh-KMSLabel {
$k = Get-CurrentKMSSettings
$script:lblCurrentKMS.Text = "Currently registered KMS: $($k.Server) Port: $($k.Port)"
}
function Refresh-LicenseWarning {
try {
$wAppId = "55c92734-d682-4d71-983e-d6ec3f16059f"
$lic = Get-CimInstance -ClassName SoftwareLicensingProduct |
Where-Object { $_.ApplicationId -eq $wAppId -and $_.PartialProductKey }
$script:lblLicenseWarn.Visible = ($null -ne $lic -and $lic.LicenseStatus -eq 1)
} catch {
$script:lblLicenseWarn.Visible = $false
}
}
function Set-Busy {
param([bool]$Busy, [string]$Status = "")
$script:progressBar.Visible = $Busy
$script:btnActivate.Enabled = -not $Busy
$script:btnStatus.Enabled = -not $Busy
$script:btnClearKMS.Enabled = -not $Busy
$script:btnTestPort.Enabled = -not $Busy
# Upgrade eval button only re-enabled if this is actually an eval build
if ($script:OSInfo -and $script:OSInfo.IsEvaluation -and $script:OSInfo.ServerVersion) {
$script:btnUpgradeEval.Enabled = -not $Busy
}
$script:form.Cursor = if ($Busy) { [System.Windows.Forms.Cursors]::WaitCursor } `
else { [System.Windows.Forms.Cursors]::Default }
if ($Status) { $script:statusLabel.Text = $Status }
$script:form.Refresh()
}
# ============================================================
# Background initialisation (runs after window is visible)
# ============================================================
function Start-BackgroundInit {
# Determinate progress bar -- 3 tasks
$script:progressBar.Style = [System.Windows.Forms.ProgressBarStyle]::Blocks
$script:progressBar.Minimum = 0
$script:progressBar.Maximum = 3
$script:progressBar.Value = 0
$script:progressBar.Visible = $true
$script:statusLabel.Text = "Loading system information (0/3)..."
$script:form.Cursor = [System.Windows.Forms.Cursors]::WaitCursor
$script:initOSdone = $false
$script:initKMSdone = $false
$script:initLICdone = $false
# Runspace pool: all 3 tasks run in parallel
$script:rsPool = [RunspaceFactory]::CreateRunspacePool(1, 3)
$script:rsPool.Open()
# --- Task 1: OS Detection ---
$script:psOS = [PowerShell]::Create()
$script:psOS.RunspacePool = $script:rsPool
[void]$script:psOS.AddScript({
$os = Get-CimInstance Win32_OperatingSystem
$caption = $os.Caption
$reg = Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -ErrorAction SilentlyContinue
$edId = if ($reg -and $reg.EditionID) { $reg.EditionID } else { "" }
$prodNm = if ($reg -and $reg.ProductName) { $reg.ProductName } else { $caption }
$isEval = ($caption -match "Evaluation") -or ($prodNm -match "Evaluation") -or ($edId -match "Eval$")
$sver = $null
foreach ($v in @("2025","2022","2019","2016")) {
if ($caption -match "Server $v" -or $prodNm -match "Server $v") { $sver = $v; break }
}
$fam = "Unknown"
if ($sver) { $fam = "Server$sver" }
elseif ($caption -match "Windows 10|Windows 11" -or $prodNm -match "Windows 10|Windows 11") { $fam = "Desktop" }
[PSCustomObject]@{
Caption = $caption
ProductName = $prodNm
EditionId = $edId
Family = $fam
ServerVersion = $sver
IsEvaluation = $isEval
}
})
$script:handleOS = $script:psOS.BeginInvoke()
# --- Task 2: KMS Settings ---
$script:psKMS = [PowerShell]::Create()
$script:psKMS.RunspacePool = $script:rsPool
[void]$script:psKMS.AddScript({
try {
$sls = Get-CimInstance -ClassName SoftwareLicensingService
$srv = $sls.KeyManagementServiceMachine
$port = $sls.KeyManagementServicePort
if ([string]::IsNullOrWhiteSpace($srv)) { $srv = "(not set)" }
if (-not $port -or $port -eq 0) { $port = 1688 }
[PSCustomObject]@{ Server = $srv; Port = $port; Ok = $true }
} catch {
[PSCustomObject]@{ Server = "Error reading"; Port = 1688; Ok = $false }
}
})
$script:handleKMS = $script:psKMS.BeginInvoke()
# --- Task 3: License Status ---
$script:psLIC = [PowerShell]::Create()
$script:psLIC.RunspacePool = $script:rsPool
[void]$script:psLIC.AddScript({
try {
$wId = "55c92734-d682-4d71-983e-d6ec3f16059f"
$lic = Get-CimInstance -ClassName SoftwareLicensingProduct |
Where-Object { $_.ApplicationId -eq $wId -and $_.PartialProductKey }
if ($lic) {
[PSCustomObject]@{ Found=$true; LicenseStatus=($lic.LicenseStatus)
Name=($lic.Name); PartialKey=($lic.PartialProductKey) }
} else {
[PSCustomObject]@{ Found=$false; LicenseStatus=-1; Name=""; PartialKey="" }
}
} catch {
[PSCustomObject]@{ Found=$false; LicenseStatus=-1; Name=""; PartialKey="" }
}
})
$script:handleLIC = $script:psLIC.BeginInvoke()
# --- Polling timer: runs on the UI thread every 200 ms ---
$script:pollTimer = New-Object System.Windows.Forms.Timer
$script:pollTimer.Interval = 200
$script:pollTimer.Add_Tick({
$done = 0
if ($script:initOSdone) { $done++ }
if ($script:initKMSdone) { $done++ }
if ($script:initLICdone) { $done++ }
# Task 1 complete?
if (-not $script:initOSdone -and $script:handleOS.IsCompleted) {
$script:initOSdone = $true
try {
$r = $script:psOS.EndInvoke($script:handleOS)[0]
# Store OSInfo as a plain hashtable for compatibility with other handlers
$script:OSInfo = @{
Caption = $r.Caption
ProductName = $r.ProductName
EditionId = $r.EditionId
Family = $r.Family
ServerVersion = $r.ServerVersion
IsEvaluation = $r.IsEvaluation
}
$osLabel = "Detected OS: $($r.Caption)"
if ($r.IsEvaluation) { $osLabel += " [EVALUATION]" }
$script:lblDetectedOS.Text = $osLabel
# Filter dropdown to only editions valid for this OS
$script:cmbKeys.Items.Clear()
$allowed = $EditionsByFamily[$r.Family]
if (-not $allowed) { $allowed = @($GVLKTable.Keys) }
foreach ($ed in $GVLKTable.Keys) {
if ($allowed -contains $ed) { [void]$script:cmbKeys.Items.Add($ed) }
}
if ($script:cmbKeys.Items.Count -gt 0) {
$script:cmbKeys.SelectedIndex = 0
foreach ($ed in $script:cmbKeys.Items) {
$words = ($ed -split '[\s/]+') | Where-Object { $_.Length -gt 2 }
$mtch = ($words | Where-Object { $r.Caption -match [regex]::Escape($_) }).Count
if ($mtch -ge ($words.Count - 1)) { $script:cmbKeys.SelectedItem = $ed; break }
}
}
if ($r.IsEvaluation -and $r.ServerVersion) {
$script:btnUpgradeEval.Visible = $true
$script:btnUpgradeEval.Text = "Upgrade Eval -> WS$($r.ServerVersion) Std/DC"
}
Write-Log "OS : $($r.Caption)"
Write-Log "Family : $($r.Family) | Eval: $($r.IsEvaluation)"
Write-Log "Edition : $($r.EditionId)"
} catch { Write-Log "OS detection error: $_" }
$done++
$script:progressBar.Value = $done
$script:statusLabel.Text = "Loading system information ($done/3)..."
}
# Task 2 complete?
if (-not $script:initKMSdone -and $script:handleKMS.IsCompleted) {
$script:initKMSdone = $true
try {
$r = $script:psKMS.EndInvoke($script:handleKMS)[0]
$script:lblCurrentKMS.Text = "Currently registered KMS: $($r.Server) Port: $($r.Port)"
Write-Log "KMS : $($r.Server) Port: $($r.Port)"
} catch {
$script:lblCurrentKMS.Text = "Currently registered KMS: Error reading"
Write-Log "KMS read error: $_"
}
$done++
$script:progressBar.Value = $done
$script:statusLabel.Text = "Loading system information ($done/3)..."
}
# Task 3 complete?
if (-not $script:initLICdone -and $script:handleLIC.IsCompleted) {
$script:initLICdone = $true
try {
$r = $script:psLIC.EndInvoke($script:handleLIC)[0]
$script:lblLicenseWarn.Visible = ($r.Found -and $r.LicenseStatus -eq 1)
if ($r.Found) {
$stxt = switch ($r.LicenseStatus) {
0 {"Unlicensed"} 1 {"Licensed (fully activated)"} 2 {"OOB Grace"}
3 {"OOT Grace"} 4 {"Non-Genuine Grace"} 5 {"Notification"}
6 {"Extended Grace"} default {"Unknown ($($r.LicenseStatus))"}
}
Write-Log "License : $stxt | Key: $($r.PartialKey)"
} else {
Write-Log "License : No active Windows license product found"
}
} catch { Write-Log "License check error: $_" }
$done++
$script:progressBar.Value = $done
$script:statusLabel.Text = "Loading system information ($done/3)..."
}
# All 3 done?
if ($script:initOSdone -and $script:initKMSdone -and $script:initLICdone) {
$script:pollTimer.Stop()
$script:pollTimer.Dispose()
# Reset progress bar back to marquee for operation use
$script:progressBar.Visible = $false
$script:progressBar.Style = [System.Windows.Forms.ProgressBarStyle]::Marquee
# Enable action buttons
$script:btnActivate.Enabled = $true
$script:btnStatus.Enabled = $true
$script:btnClearKMS.Enabled = $true
$script:btnTestPort.Enabled = $true
if ($script:OSInfo -and $script:OSInfo.IsEvaluation -and $script:OSInfo.ServerVersion) {
$script:btnUpgradeEval.Enabled = $true
}
$script:form.Cursor = [System.Windows.Forms.Cursors]::Default
$script:statusLabel.Text = "Ready"
Write-Log "=== System information loaded. Ready. ==="
# Clean up runspaces
try { $script:psOS.Dispose() } catch {}
try { $script:psKMS.Dispose() } catch {}
try { $script:psLIC.Dispose() } catch {}
try { $script:rsPool.Close(); $script:rsPool.Dispose() } catch {}
}
})
$script:pollTimer.Start()
}
# ============================================================
# FORM
# ============================================================
$script:form = New-Object System.Windows.Forms.Form
$script:form.Text = "KMS Activation Utility v2.3 (Administrator)"
$script:form.Size = New-Object System.Drawing.Size(590, 700)
$script:form.MinimumSize = New-Object System.Drawing.Size(530, 640)
$script:form.StartPosition = "CenterScreen"
$script:form.Font = New-Object System.Drawing.Font("Segoe UI", 9)
# StatusStrip
$script:statusStrip = New-Object System.Windows.Forms.StatusStrip
$script:statusLabel = New-Object System.Windows.Forms.ToolStripStatusLabel
$script:statusLabel.Text = "Ready"
$script:statusLabel.Spring = $true
$script:statusLabel.TextAlign = [System.Drawing.ContentAlignment]::MiddleLeft
$script:statusStrip.Items.Add($script:statusLabel) | Out-Null
$script:form.Controls.Add($script:statusStrip)
# ---- GROUP: System Information ---------------------------------
$grpOS = New-Object System.Windows.Forms.GroupBox
$grpOS.Text = "System Information"
$grpOS.Location = New-Object System.Drawing.Point(10, 8)
$grpOS.Size = New-Object System.Drawing.Size(560, 48)
$grpOS.Anchor = "Top,Left,Right"
$script:form.Controls.Add($grpOS)
$script:lblDetectedOS = New-Object System.Windows.Forms.Label
$script:lblDetectedOS.Location = New-Object System.Drawing.Point(8, 18)
$script:lblDetectedOS.Size = New-Object System.Drawing.Size(544, 20)
$script:lblDetectedOS.Font = New-Object System.Drawing.Font("Segoe UI", 9, [System.Drawing.FontStyle]::Bold)
$grpOS.Controls.Add($script:lblDetectedOS)
# ---- LICENSE WARNING BANNER (hidden until fully-licensed detected) ----
$script:lblLicenseWarn = New-Object System.Windows.Forms.Label
$script:lblLicenseWarn.Location = New-Object System.Drawing.Point(10, 60)
$script:lblLicenseWarn.Size = New-Object System.Drawing.Size(560, 40)
$script:lblLicenseWarn.Text = " WARNING: This machine is FULLY ACTIVATED. A new product key will revoke the current" +
" license immediately and start a 30-day grace / reactivation period."
$script:lblLicenseWarn.BackColor = [System.Drawing.Color]::FromArgb(255, 185, 0)
$script:lblLicenseWarn.ForeColor = [System.Drawing.Color]::FromArgb(30, 30, 30)
$script:lblLicenseWarn.Font = New-Object System.Drawing.Font("Segoe UI", 8.5, [System.Drawing.FontStyle]::Bold)
$script:lblLicenseWarn.Anchor = "Top,Left,Right"
$script:lblLicenseWarn.TextAlign = [System.Drawing.ContentAlignment]::MiddleLeft
$script:lblLicenseWarn.Visible = $false
$script:lblLicenseWarn.Name = "lblLicenseWarn"
$script:form.Controls.Add($script:lblLicenseWarn)
# ---- GROUP: KMS Server -----------------------------------------
$grpServer = New-Object System.Windows.Forms.GroupBox
$grpServer.Text = "KMS Server"
$grpServer.Location = New-Object System.Drawing.Point(10, 102)
$grpServer.Size = New-Object System.Drawing.Size(560, 78)
$grpServer.Anchor = "Top,Left,Right"
$script:form.Controls.Add($grpServer)
$lblServer = New-Object System.Windows.Forms.Label
$lblServer.Location = New-Object System.Drawing.Point(8, 22)
$lblServer.Size = New-Object System.Drawing.Size(110, 20)
$lblServer.Text = "KMS Server Host:"
$grpServer.Controls.Add($lblServer)
$script:txtServer = New-Object System.Windows.Forms.TextBox
$script:txtServer.Location = New-Object System.Drawing.Point(122, 20)
$script:txtServer.Size = New-Object System.Drawing.Size(200, 22)
$script:txtServer.Text = "kms.nbk.ngtech.co.il"
$script:txtServer.Anchor = "Top,Left,Right"
$grpServer.Controls.Add($script:txtServer)
$lblPort = New-Object System.Windows.Forms.Label
$lblPort.Location = New-Object System.Drawing.Point(330, 22)
$lblPort.Size = New-Object System.Drawing.Size(32, 20)
$lblPort.Text = "Port:"
$grpServer.Controls.Add($lblPort)
$script:txtPort = New-Object System.Windows.Forms.TextBox
$script:txtPort.Location = New-Object System.Drawing.Point(362, 20)
$script:txtPort.Size = New-Object System.Drawing.Size(52, 22)
$script:txtPort.Text = "1688"
$grpServer.Controls.Add($script:txtPort)
$script:btnTestPort = New-Object System.Windows.Forms.Button
$script:btnTestPort.Location = New-Object System.Drawing.Point(422, 18)
$script:btnTestPort.Size = New-Object System.Drawing.Size(128, 26)
$script:btnTestPort.Text = "Test Connection"
$script:btnTestPort.Anchor = "Top,Right"
$grpServer.Controls.Add($script:btnTestPort)
$script:lblCurrentKMS = New-Object System.Windows.Forms.Label
$script:lblCurrentKMS.Location = New-Object System.Drawing.Point(8, 50)
$script:lblCurrentKMS.Size = New-Object System.Drawing.Size(544, 18)
$script:lblCurrentKMS.Font = New-Object System.Drawing.Font("Segoe UI", 8, [System.Drawing.FontStyle]::Italic)
$script:lblCurrentKMS.Text = "Currently registered KMS: loading..."
$grpServer.Controls.Add($script:lblCurrentKMS)
# ---- GROUP: Product Key ----------------------------------------
$grpKey = New-Object System.Windows.Forms.GroupBox
$grpKey.Text = "Product Key"
$grpKey.Location = New-Object System.Drawing.Point(10, 186)
$grpKey.Size = New-Object System.Drawing.Size(560, 80)
$grpKey.Anchor = "Top,Left,Right"
$script:form.Controls.Add($grpKey)
$lblEdition = New-Object System.Windows.Forms.Label
$lblEdition.Location = New-Object System.Drawing.Point(8, 22)
$lblEdition.Size = New-Object System.Drawing.Size(110, 20)
$lblEdition.Text = "Select Edition:"
$grpKey.Controls.Add($lblEdition)
$script:cmbKeys = New-Object System.Windows.Forms.ComboBox
$script:cmbKeys.Location = New-Object System.Drawing.Point(122, 20)
$script:cmbKeys.Size = New-Object System.Drawing.Size(430, 22)
$script:cmbKeys.DropDownStyle = [System.Windows.Forms.ComboBoxStyle]::DropDownList
$script:cmbKeys.Anchor = "Top,Left,Right"
# Dropdown is populated after OS detection in Form Load
$grpKey.Controls.Add($script:cmbKeys)
$lblKey = New-Object System.Windows.Forms.Label
$lblKey.Location = New-Object System.Drawing.Point(8, 50)
$lblKey.Size = New-Object System.Drawing.Size(110, 20)
$lblKey.Text = "GVLK Key Value:"
$grpKey.Controls.Add($lblKey)
$script:txtKey = New-Object System.Windows.Forms.TextBox
$script:txtKey.Location = New-Object System.Drawing.Point(122, 48)
$script:txtKey.Size = New-Object System.Drawing.Size(430, 22)
$script:txtKey.CharacterCasing = [System.Windows.Forms.CharacterCasing]::Upper
$script:txtKey.Anchor = "Top,Left,Right"
$grpKey.Controls.Add($script:txtKey)
$script:KeyFromDropdown = ""
$script:KeyManuallyEdited = $false
$script:cmbKeys.Add_SelectedIndexChanged({
$sel = $script:cmbKeys.SelectedItem.ToString()
if ($GVLKTable.Contains($sel)) {
$script:KeyFromDropdown = $GVLKTable[$sel]
$script:txtKey.Text = $script:KeyFromDropdown
$script:KeyManuallyEdited = $false
}
})
$script:txtKey.Add_TextChanged({
$script:KeyManuallyEdited = ($script:txtKey.Text -ne $script:KeyFromDropdown -and $script:KeyFromDropdown -ne "")
})
# ---- GROUP: Activity Log ---------------------------------------
$grpLog = New-Object System.Windows.Forms.GroupBox
$grpLog.Text = "Activity Log"
$grpLog.Location = New-Object System.Drawing.Point(10, 272)
$grpLog.Size = New-Object System.Drawing.Size(560, 210)
$grpLog.Anchor = "Top,Bottom,Left,Right"
$script:form.Controls.Add($grpLog)
$script:txtLog = New-Object System.Windows.Forms.TextBox
$script:txtLog.Name = "txtLog"
$script:txtLog.Location = New-Object System.Drawing.Point(8, 18)
$script:txtLog.Size = New-Object System.Drawing.Size(544, 155)
$script:txtLog.Multiline = $true
$script:txtLog.ScrollBars = "Vertical"
$script:txtLog.ReadOnly = $true
$script:txtLog.Font = New-Object System.Drawing.Font("Consolas", 8.5)
$script:txtLog.Anchor = "Top,Bottom,Left,Right"
$grpLog.Controls.Add($script:txtLog)
$script:btnCopyLog = New-Object System.Windows.Forms.Button
$script:btnCopyLog.Location = New-Object System.Drawing.Point(8, 180)
$script:btnCopyLog.Size = New-Object System.Drawing.Size(120, 24)
$script:btnCopyLog.Text = "Copy Log"
$script:btnCopyLog.Anchor = "Bottom,Left"
$grpLog.Controls.Add($script:btnCopyLog)
$script:btnSaveLog = New-Object System.Windows.Forms.Button
$script:btnSaveLog.Location = New-Object System.Drawing.Point(136, 180)
$script:btnSaveLog.Size = New-Object System.Drawing.Size(130, 24)
$script:btnSaveLog.Text = "Save Log to File"
$script:btnSaveLog.Anchor = "Bottom,Left"
$grpLog.Controls.Add($script:btnSaveLog)
$script:btnClearLog = New-Object System.Windows.Forms.Button
$script:btnClearLog.Location = New-Object System.Drawing.Point(274, 180)
$script:btnClearLog.Size = New-Object System.Drawing.Size(100, 24)
$script:btnClearLog.Text = "Clear Log"
$script:btnClearLog.Tag = "danger"
$script:btnClearLog.Anchor = "Bottom,Left"
$grpLog.Controls.Add($script:btnClearLog)
# ---- GROUP: Actions --------------------------------------------
$grpActions = New-Object System.Windows.Forms.GroupBox
$grpActions.Text = "Actions"
$grpActions.Location = New-Object System.Drawing.Point(10, 488)
$grpActions.Size = New-Object System.Drawing.Size(560, 152)
$grpActions.Anchor = "Bottom,Left,Right"
$script:form.Controls.Add($grpActions)
# Row 1: main action buttons
$script:btnActivate = New-Object System.Windows.Forms.Button
$script:btnActivate.Location = New-Object System.Drawing.Point(8, 22)
$script:btnActivate.Size = New-Object System.Drawing.Size(170, 30)
$script:btnActivate.Text = "Apply Key && Activate"
$grpActions.Controls.Add($script:btnActivate)
$script:btnStatus = New-Object System.Windows.Forms.Button
$script:btnStatus.Location = New-Object System.Drawing.Point(186, 22)
$script:btnStatus.Size = New-Object System.Drawing.Size(170, 30)
$script:btnStatus.Text = "Check License Status"
$grpActions.Controls.Add($script:btnStatus)
$script:btnClearKMS = New-Object System.Windows.Forms.Button
$script:btnClearKMS.Location = New-Object System.Drawing.Point(364, 22)
$script:btnClearKMS.Size = New-Object System.Drawing.Size(188, 30)
$script:btnClearKMS.Text = "Clear KMS Settings"
$script:btnClearKMS.Tag = "danger"
$script:btnClearKMS.Anchor = "Top,Right"
$grpActions.Controls.Add($script:btnClearKMS)
# Row 2: evaluation upgrade (shown only for server eval builds) + theme toggle
$script:btnUpgradeEval = New-Object System.Windows.Forms.Button
$script:btnUpgradeEval.Location = New-Object System.Drawing.Point(8, 60)
$script:btnUpgradeEval.Size = New-Object System.Drawing.Size(240, 30)
$script:btnUpgradeEval.Text = "Upgrade Evaluation Edition"
$script:btnUpgradeEval.Tag = "danger"
$script:btnUpgradeEval.Visible = $false # hidden until OS detected as Server Eval
$grpActions.Controls.Add($script:btnUpgradeEval)
$script:btnTheme = New-Object System.Windows.Forms.Button
$script:btnTheme.Location = New-Object System.Drawing.Point(464, 60)
$script:btnTheme.Size = New-Object System.Drawing.Size(88, 30)
$script:btnTheme.Text = "Light Mode"
$script:btnTheme.Anchor = "Top,Right"
$grpActions.Controls.Add($script:btnTheme)
# Row 3: progress bar
$script:progressBar = New-Object System.Windows.Forms.ProgressBar
$script:progressBar.Location = New-Object System.Drawing.Point(8, 100)
$script:progressBar.Size = New-Object System.Drawing.Size(544, 18)
$script:progressBar.Style = [System.Windows.Forms.ProgressBarStyle]::Marquee
$script:progressBar.Visible = $false
$script:progressBar.Anchor = "Bottom,Left,Right"
$grpActions.Controls.Add($script:progressBar)
# ============================================================
# EVENTS
# ============================================================
# -- Test Connection --
$script:btnTestPort.Add_Click({
$srv = $script:txtServer.Text.Trim()
$port = $script:txtPort.Text.Trim()
if ([string]::IsNullOrWhiteSpace($srv)) { Write-Log "Error: KMS Server Host cannot be empty."; return }
if (-not [int]::TryParse($port, [ref]$null)) { Write-Log "Error: Port must be a numeric value."; return }
Write-Log "Testing TCP connection to ${srv}:${port}..."
Set-Busy $true "Testing connection..."
try {
$r = Test-NetConnection -ComputerName $srv -Port ([int]$port) -WarningAction SilentlyContinue
if ($r.TcpTestSucceeded) {
Write-Log "SUCCESS: $srv : $port is reachable."
$script:statusLabel.Text = "Connection OK"
} else {
Write-Log "FAILED: Cannot reach $srv on port $port."
$script:statusLabel.Text = "Connection FAILED"
}
} catch {
Write-Log "Error: $_"
$script:statusLabel.Text = "Connection error"
} finally {
Set-Busy $false
}
})
# -- Apply Key & Activate (with retry) --
$script:btnActivate.Add_Click({
$key = $script:txtKey.Text.Trim().ToUpper()
$srv = $script:txtServer.Text.Trim()
$portTx = $script:txtPort.Text.Trim()
$maxRetry = 3
if (-not (Test-GVLKFormat -Key $key)) {
[System.Windows.Forms.MessageBox]::Show(
"Invalid GVLK key format.`nExpected: XXXXX-XXXXX-XXXXX-XXXXX-XXXXX",
"Invalid Key", "OK", "Warning") | Out-Null
return
}
if ($script:KeyManuallyEdited) {
$ans = [System.Windows.Forms.MessageBox]::Show(
"You manually changed the key.`nDropdown key : $script:KeyFromDropdown`nYou entered : $key`n`nProceed with the manual key?",
"Manual Key Override", "YesNo", "Warning")
if ($ans -ne "Yes") { return }
}
if (-not [int]::TryParse($portTx, [ref]$null)) { Write-Log "Error: Port must be numeric."; return }
$portInt = [int]$portTx
# --- Fully-licensed warning ---
try {
$wAppId = "55c92734-d682-4d71-983e-d6ec3f16059f"
$curLic = Get-CimInstance -ClassName SoftwareLicensingProduct |
Where-Object { $_.ApplicationId -eq $wAppId -and $_.PartialProductKey }
if ($curLic -and $curLic.LicenseStatus -eq 1) {
$warnText = "WARNING: This machine is already FULLY ACTIVATED." + [Environment]::NewLine + [Environment]::NewLine +
"Replacing the product key is usually unnecessary and carries real risks:" + [Environment]::NewLine + [Environment]::NewLine +
" - Your current activation will be REVOKED immediately." + [Environment]::NewLine +
" - The system enters a grace / reactivation period (typically 30 days)." + [Environment]::NewLine +
" - KMS must be reached again within that window, or Windows will show" + [Environment]::NewLine +
" activation warnings and may restrict certain personalisation features." + [Environment]::NewLine +
" - If the new key does not match your actual Volume License agreement," + [Environment]::NewLine +
" you could be out of compliance with Microsoft licensing terms." + [Environment]::NewLine + [Environment]::NewLine +
"Only proceed if you are intentionally moving this machine to a different" + [Environment]::NewLine +
"edition or KMS infrastructure and understand the consequences." + [Environment]::NewLine + [Environment]::NewLine +
"Proceed and REPLACE the current activation?"
$ans2 = [System.Windows.Forms.MessageBox]::Show(
$warnText,
"Machine Already Fully Activated -- Are You Sure?",
[System.Windows.Forms.MessageBoxButtons]::YesNo,
[System.Windows.Forms.MessageBoxIcon]::Warning,
[System.Windows.Forms.MessageBoxDefaultButton]::Button2)
if ($ans2 -ne [System.Windows.Forms.DialogResult]::Yes) {
Write-Log "Activation cancelled -- machine is already fully licensed."
return
}
Write-Log "WARNING: User chose to replace activation on a fully-licensed machine."
}
} catch {
# If license query fails, do not block activation
}
Set-Busy $true "Activating..."
$attempt = 0 ; $ok = $false
while ($attempt -lt $maxRetry -and -not $ok) {
$attempt++
Write-Log "--- Attempt $attempt / $maxRetry ---"
try {
Write-Log "Installing product key..."
$sls = Get-CimInstance -ClassName SoftwareLicensingService
Invoke-CimMethod -InputObject $sls -MethodName "InstallProductKey" `
-Arguments @{ ProductKey = $key } | Out-Null
Write-Log "Setting KMS server: $srv"
Invoke-CimMethod -InputObject $sls -MethodName "SetKeyManagementServiceMachine" `
-Arguments @{ KeyManagementServiceMachine = $srv } | Out-Null
Write-Log "Setting KMS port: $portInt"
Invoke-CimMethod -InputObject $sls -MethodName "SetKeyManagementServicePort" `
-Arguments @{ PortNumber = [uint32]$portInt } | Out-Null
Invoke-CimMethod -InputObject $sls -MethodName "RefreshLicenseStatus" | Out-Null
Write-Log "Requesting activation..."
$wAppId = "55c92734-d682-4d71-983e-d6ec3f16059f"
$lic = Get-CimInstance -ClassName SoftwareLicensingProduct |
Where-Object { $_.ApplicationId -eq $wAppId -and $_.PartialProductKey }
if (-not $lic) {
Write-Log "WARNING: No product with partial key found after install."
} else {
$res = Invoke-CimMethod -InputObject $lic -MethodName "Activate"
if ($res.ReturnValue -eq 0) { $ok = $true ; Write-Log "Activation succeeded." }
else { Write-Log "Activation returned code: $($res.ReturnValue)" }
}
} catch {
Write-Log "Error: $_"
if ($attempt -lt $maxRetry) { Write-Log "Retrying in 3 s..." ; Start-Sleep -Seconds 3 }
}
}
if ($ok) { $script:statusLabel.Text = "Activation successful" ; Write-Log "=== Done after $attempt attempt(s). ===" }
else { $script:statusLabel.Text = "Activation failed" ; Write-Log "=== Failed after $maxRetry attempt(s). ===" }
Refresh-KMSLabel
Refresh-LicenseWarning
Set-Busy $false
})
# -- Check License Status --
$script:btnStatus.Add_Click({
Set-Busy $true "Reading license status..."
try {
$wAppId = "55c92734-d682-4d71-983e-d6ec3f16059f"
$lic = Get-CimInstance -ClassName SoftwareLicensingProduct |
Where-Object { $_.ApplicationId -eq $wAppId -and $_.PartialProductKey }
if ($lic) {
$statusTxt = switch ($lic.LicenseStatus) {
0 { "Unlicensed (0)" } 1 { "Licensed (1)" } 2 { "OOB Grace (2)" }
3 { "OOT Grace (3)" } 4 { "Non-Genuine Grace (4)" } 5 { "Notification (5)" }
6 { "Extended Grace (6)" } default { "Unknown ($($lic.LicenseStatus))" }
}
Write-Log "--- License Status Report ---"
Write-Log "Status : $statusTxt"
Write-Log "Product : $($lic.Name)"
Write-Log "Partial Key : $($lic.PartialProductKey)"
if ($lic.GracePeriodRemaining -gt 0) {
$gd = [math]::Floor($lic.GracePeriodRemaining / 1440)
$gh = [math]::Floor(($lic.GracePeriodRemaining % 1440) / 60)
$gm = $lic.GracePeriodRemaining % 60
Write-Log "Grace Left : ${gd}d ${gh}h ${gm}m"
} else {
Write-Log "Grace Left : N/A (fully licensed)"
}
if ($lic.KeyManagementServiceMachine) {
Write-Log "KMS Server : $($lic.KeyManagementServiceMachine)"
Write-Log "KMS Port : $($lic.KeyManagementServicePort)"
}
if ($lic.KeyManagementServiceCurrentCount) {
Write-Log "KMS Count : $($lic.KeyManagementServiceCurrentCount)"
}
if ($lic.KeyManagementServiceLicensedRequests -gt 0) {
Write-Log "KMS Licensed : $($lic.KeyManagementServiceLicensedRequests) requests"
}
$script:statusLabel.Text = "License: $statusTxt"
} else {
Write-Log "No active license product found."
$script:statusLabel.Text = "No license found"
}
} catch {
Write-Log "Error: $_"
$script:statusLabel.Text = "Error reading license"
} finally {
Refresh-LicenseWarning
Set-Busy $false
}
})
# -- Clear KMS Settings --
$script:btnClearKMS.Add_Click({
$ans = [System.Windows.Forms.MessageBox]::Show(
"This will clear the registered KMS server and port.`nAre you sure?",
"Clear KMS Settings", "YesNo", "Warning")
if ($ans -ne "Yes") { return }
Set-Busy $true "Clearing KMS settings..."
try {
$sls = Get-CimInstance -ClassName SoftwareLicensingService
Invoke-CimMethod -InputObject $sls -MethodName "SetKeyManagementServiceMachine" `
-Arguments @{ KeyManagementServiceMachine = "" } | Out-Null
Invoke-CimMethod -InputObject $sls -MethodName "SetKeyManagementServicePort" `
-Arguments @{ PortNumber = [uint32]0 } | Out-Null
Write-Log "KMS settings cleared."
$script:statusLabel.Text = "KMS settings cleared"
Refresh-KMSLabel
} catch {
Write-Log "Error: $_"
$script:statusLabel.Text = "Error"
} finally {
Set-Busy $false
}
})
# -- Upgrade Evaluation Edition (DISM) --
$script:btnUpgradeEval.Add_Click({
$osInfo = $script:OSInfo
# Guard: must be a Server Evaluation
if (-not $osInfo -or -not $osInfo.IsEvaluation -or -not $osInfo.ServerVersion) {
[System.Windows.Forms.MessageBox]::Show(
"This function is only available on Windows Server Evaluation builds.",
"Not Applicable", "OK", "Information") | Out-Null
return
}
$ver = $osInfo.ServerVersion
# Show Standard / Datacenter picker
$tier = Show-EditionPicker -ServerVersion $ver
if (-not $tier) { return } # user cancelled
# Look up the GVLK for this server version + tier
$editionName = "Windows Server $ver $tier"
if (-not $GVLKTable.Contains($editionName)) {
[System.Windows.Forms.MessageBox]::Show(
"No GVLK key found for: $editionName",
"Missing Key", "OK", "Warning") | Out-Null
return
}
$key = $GVLKTable[$editionName]
$dismEdition = if ($tier -eq "Standard") { "ServerStandard" } else { "ServerDatacenter" }
$ans = [System.Windows.Forms.MessageBox]::Show(
"Ready to upgrade:`n`n Source : Windows Server $ver Evaluation`n Target : $tier ($dismEdition)`n GVLK : $key`n`nA reboot will likely be required. Proceed?",
"Confirm Evaluation Upgrade", "YesNo", "Warning")
if ($ans -ne "Yes") { return }
Set-Busy $true "Running DISM upgrade..."
Write-Log "--- Evaluation Edition Upgrade ---"
Write-Log "Source : Windows Server $ver Evaluation"
Write-Log "Target : $dismEdition ($tier)"
Write-Log "Key : $key"
try {
$outFile = Join-Path $env:TEMP "kms_dism_out.txt"
$errFile = Join-Path $env:TEMP "kms_dism_err.txt"
$dArgs = "/online /Set-Edition:$dismEdition /ProductKey:$key /AcceptEula /NoRestart"
$proc = Start-Process -FilePath "DISM.exe" -ArgumentList $dArgs `
-Wait -PassThru -NoNewWindow `
-RedirectStandardOutput $outFile -RedirectStandardError $errFile
if (Test-Path $outFile) { $o = Get-Content $outFile -Raw ; if ($o.Trim()) { Write-Log "DISM: $o" } }
if (Test-Path $errFile) { $e = Get-Content $errFile -Raw ; if ($e.Trim()) { Write-Log "DISM ERR: $e" } }
if ($proc.ExitCode -eq 0) {
Write-Log "DISM completed successfully (exit 0)."
$script:statusLabel.Text = "Upgrade complete"
[System.Windows.Forms.MessageBox]::Show(
"Edition upgrade completed.`nA reboot may be needed to finalise.",
"Done", "OK", "Information") | Out-Null
} elseif ($proc.ExitCode -eq 3010) {
Write-Log "DISM completed -- reboot required (exit 3010)."
$rb = [System.Windows.Forms.MessageBox]::Show(
"Upgrade complete. A reboot is required.`nReboot now?",
"Reboot Required", "YesNo", "Question")
if ($rb -eq "Yes") { Restart-Computer -Force }
} else {
Write-Log "DISM failed with exit code $($proc.ExitCode)."
$script:statusLabel.Text = "DISM failed (code $($proc.ExitCode))"
}
} catch {
Write-Log "Error running DISM: $_"
$script:statusLabel.Text = "DISM error"
} finally {
Set-Busy $false
}
})
# -- Copy Log --
$script:btnCopyLog.Add_Click({
if ([string]::IsNullOrWhiteSpace($script:txtLog.Text)) { $script:statusLabel.Text = "Log is empty." ; return }
[System.Windows.Forms.Clipboard]::SetText($script:txtLog.Text)
$script:statusLabel.Text = "Log copied to clipboard."
})
# -- Save Log --
$script:btnSaveLog.Add_Click({
if ([string]::IsNullOrWhiteSpace($script:txtLog.Text)) { $script:statusLabel.Text = "Log is empty." ; return }
$dlg = New-Object System.Windows.Forms.SaveFileDialog
$dlg.Filter = "Text Files (*.txt)|*.txt|All Files (*.*)|*.*"
$dlg.FileName = "KMS-Log-$(Get-Date -Format 'yyyyMMdd-HHmmss').txt"
$dlg.Title = "Save Activity Log"
if ($dlg.ShowDialog() -eq "OK") {
try {
$script:txtLog.Text | Out-File -FilePath $dlg.FileName -Encoding UTF8
Write-Log "Log saved: $($dlg.FileName)"
$script:statusLabel.Text = "Log saved."
} catch { Write-Log "Save error: $_" }
}
})
# -- Clear Log --
$script:btnClearLog.Add_Click({
$script:txtLog.Clear()
$script:statusLabel.Text = "Log cleared."
})
# -- Theme Toggle --
$script:btnTheme.Add_Click({
$script:IsDark = -not $script:IsDark
$script:btnTheme.Text = if ($script:IsDark) { "Light Mode" } else { "Dark Mode" }
Apply-Theme -Root $script:form
})
# ============================================================
# FORM LOAD -- pure UI setup, no CIM/registry calls here
# ============================================================
$script:form.Add_Load({
# Placeholders while background tasks run
$script:lblDetectedOS.Text = "Detecting operating system..."
$script:lblCurrentKMS.Text = "Currently registered KMS: loading..."
$script:btnUpgradeEval.Visible = $false
# Pre-populate dropdown with all editions; background task will re-filter once OS is known
$script:cmbKeys.Items.Clear()
foreach ($ed in $GVLKTable.Keys) { [void]$script:cmbKeys.Items.Add($ed) }
if ($script:cmbKeys.Items.Count -gt 0) { $script:cmbKeys.SelectedIndex = 0 }
# Disable action buttons until background init completes
$script:btnActivate.Enabled = $false
$script:btnStatus.Enabled = $false
$script:btnClearKMS.Enabled = $false
$script:btnTestPort.Enabled = $false
$script:btnUpgradeEval.Enabled = $false
Apply-Theme -Root $script:form
Write-Log "KMS Activation Utility v2.3 starting..."
})
# ============================================================
# FORM SHOWN -- window is visible; kick off background tasks
# ============================================================
$script:form.Add_Shown({
Start-BackgroundInit
})
# ============================================================
# LAUNCH
# ============================================================
[void]$script:form.ShowDialog()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment