Scripts/Uninstall-Font.ps1
#Requires -Version 3.0 [CmdletBinding(SupportsShouldProcess)] Param( [Parameter(Mandatory)] [String]$Name, [ValidateSet('System', 'User')] [String]$Scope = 'System' ) $MoveFileEx = @' [DllImport("kernel32.dll", CharSet = CharSet.Unicode, EntryPoint = "MoveFileExW", ExactSpelling = true, SetLastError = true)] public static extern bool MoveFileEx([MarshalAs(UnmanagedType.LPWStr)] string lpExistingFileName, IntPtr lpNewFileName, uint dwFlags); '@ Function Uninstall-Font { [CmdletBinding(SupportsShouldProcess)] Param( [Parameter(Mandatory)] [String]$Name, [ValidateSet('System', 'User')] [String]$Scope = 'System' ) switch ($Scope) { 'System' { $FontsFolder = [Environment]::GetFolderPath('Fonts') $FontsRegKey = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts' } 'User' { $FontsFolder = Join-Path -Path ([Environment]::GetFolderPath('LocalApplicationData')) -ChildPath 'Microsoft\Windows\Fonts' $FontsRegKey = 'HKCU:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts' } } try { $FontsReg = Get-Item -Path $FontsRegKey -ErrorAction Stop } catch { throw ('Unable to open {0} fonts registry key: {1}' -f $Scope.ToLower(), $FontsRegKey) } if ($FontsReg.Property -notcontains $Name) { throw ('Font not registered for {0}: {1}' -f $Scope.ToLower(), $Name) } $FontRegValue = $FontsReg.GetValue($Name) if ($Scope -eq 'User') { $FontFilePath = $FontRegValue } else { $FontFilePath = Join-Path -Path $FontsFolder -ChildPath $FontRegValue } if ($PSCmdlet.ShouldProcess($Name, 'Remove font')) { try { Write-Debug -Message ('Removing font file: {0}' -f $FontFilePath) Remove-Item -Path $FontFilePath -ErrorAction Stop } catch [Management.Automation.ItemNotFoundException] { Write-Warning -Message ('Font file not found: {0}' -f $FontFilePath) } catch [UnauthorizedAccessException] { $RemoveOnReboot = $true } if ($RemoveOnReboot) { if (!(Test-IsAdministrator)) { throw 'Unable to remove in-use font. Retry as Administrator to remove on next reboot.' } if (!('PSWinGlue.UninstallFont' -as [Type])) { Add-Type -Namespace PSWinGlue -Name UninstallFont -MemberDefinition $MoveFileEx } $MOVEFILE_DELAY_UNTIL_REBOOT = 4 if ([PSWinGlue.UninstallFont]::MoveFileEx($FontFilePath, [IntPtr]::Zero, $MOVEFILE_DELAY_UNTIL_REBOOT) -eq $false) { throw (New-Object -TypeName ComponentModel.Win32Exception) } } Write-Debug -Message ('Removing font from {0} registry: {1}' -f $Scope.ToLower(), $Name) Remove-ItemProperty -Path $FontsRegKey -Name $Name } } Function Test-IsAdministrator { [CmdletBinding()] Param() $User = [Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent() if ($User.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { return $true } return $false } Function Test-PerUserFontsSupported { [CmdletBinding()] Param() # Windows 10 1809 introduced support for installing fonts per-user. The # corresponding release build number is 17763 (ignoring Insider builds). $BuildNumber = [Int](Get-CimInstance -ClassName 'Win32_OperatingSystem' -Verbose:$false).BuildNumber if ($BuildNumber -ge 17763) { return $true } return $false } if ($Scope -eq 'System' -and !(Test-IsAdministrator)) { throw 'Administrator privileges are required to remove system-wide fonts.' } elseif ($Scope -eq 'User' -and !(Test-PerUserFontsSupported)) { throw 'Per-user fonts are only supported from Windows 10 1809.' } Uninstall-Font @PSBoundParameters |