You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PowerShell中Show-ControlPanelItem触发InvalidCastException的原因及解决

问题:PowerShell脚本执行Show-ControlPanelItem时触发InvalidCastException错误

在Windows PowerShell V5.1.19041.2673环境中,脚本执行Show-ControlPanelItem -CanonicalName Microsoft.RegionAndLanguage命令时,抛出如下错误:

+     Show-ControlPanelItem -CanonicalName Microsoft.RegionAndLanguage
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Show-ControlPanelItem], InvalidCastException
    + FullyQualifiedErrorId : System.InvalidCastException,Microsoft.PowerShell.Commands.ShowControlPanelItemCommand

异常触发的特殊场景

  • 手动先执行该命令,再运行脚本可正常工作
  • 通过Set-Content c:\temp\control.ps1 "Show-ControlPanelItem -CanonicalName Microsoft.RegionAndLanguage"; powershell -NoProfile C:\temp\control.ps1生成并运行的脚本能正常打开控制面板,但手动创建的脚本无法运行
  • 使用powershell -NoProfile调用原脚本同样无效

完整脚本

using namespace System.Globalization

function SendKey {
    param(
        [Parameter(Mandatory = $true)]
        [string]$key
    )

    [System.Windows.Forms.SendKeys]::SendWait($key)
}

function Set-InputLanguage {
    param (
        [Parameter(Mandatory)]
        [string]$languageTag
    )
    
    Write-Output "trying to set input language"

    $language = Get-WinUserLanguageList | Where-Object {$_.LanguageTag -like $languageTag}
    
    if ($null -ne $language) {
        $languageID = $language.InputMethodTips.Split(":")[1]
        Set-ItemProperty -Path Registry::"HKEY_USERS\.DEFAULT\Keyboard Layout\Preload" -Name 1 -Value "$($languageID)"
    }
}

function SetLanguage() {
    param(
        [Parameter(Mandatory)]
        [string]$languageTag
    )

    try {
        Set-Culture $languageTag
        Set-WinSystemLocale -SystemLocale $languageTag
        Set-WinUILanguageOverride -Language $languageTag
        Set-SystemPreferredUILanguage -Language $languageTag
        Set-InputLanguage $languageTag

        Write-Output "language preferences set successfully"
    }
    catch {
        Write-Output "language preferences could not be set"
    }
}

function IsLanguageInstalled() {
    param(
        [Parameter(Mandatory)]
        [string]$languageTag
    )

    $computer = Get-WmiObject -Class Win32_OperatingSystem
    $installedLanguagePacks = $computer.MUILanguages
    
    [int]$index = [array]::IndexOf($installedLanguagePacks.ToLower(), $languageTag)

    if ($index -gt -1) {
        return $true
    }
    return $false
}

function InstallLanguagePack() {
    Param(
        [Parameter(Mandatory)]
        [string]$languageTag
    )

    try {
        $packagePath = "$($PSScriptRoot)\langpacks\Microsoft-Windows-Client-Language-Pack_x64_$($languageTag).cab"
        Add-WindowsPackage -Online -PackagePath $packagePath -NoRestart

        $oldLanugageList = Get-WinUserLanguageList
        $oldLanugageList.Insert(0, $languageTag)
        Set-WinUserLanguageList -LanguageList $oldLanugageList -Force:$doForce

        return $true
    }
    catch {
        return $false
    }

}

function CopyLanguageSettings() {
    Show-ControlPanelItem -CanonicalName Microsoft.RegionAndLanguage
    add-type -AssemblyName System.Windows.Forms

    Start-Sleep -Milliseconds 1000

    SendKey -key '+{TAB}'
    SendKey -key '{RIGHT}'
    SendKey -key '{TAB}'
    SendKey -key '{ENTER}'
    Start-Sleep -Milliseconds 1000 
    SendKey -key '{TAB}'
    SendKey -key ' '
    SendKey -key '{TAB}'
    SendKey -key ' '
    Start-Sleep -Milliseconds 1000 
    #SendKey -key '{ENTER}'
}

function InstallLanguage {
    param (
        [Parameter(Mandatory)]
        [string]$languageTag
    )

    $languageTag = $languageTag.ToLower()
    [bool]$isLanguageInstalled = IsLanguageInstalled -languageTag "en-gb"

    if ($isLanguageInstalled = $false) {
        InstallLanguagePack -languageTag "en-gb"
    }

    $isLanguageInstalled = IsLanguageInstalled -languageTag $languageTag

    if ($isLanguageInstalled) {
        Write-Output "Language: $languageTag already installed"
        Write-Output "trying to set language preferences"
        SetLanguage -languageTag $languageTag
        Write-Output "trying to copy language preferences"
        CopyLanguageSettings
        #Restart-Computer
    }
    else {
        Write-Output "trying to install language: $languageTag"
        $isLanguageInstalledSuccessfully = InstallLanguagePack -languageTag $languageTag
    
        if ($isLanguageInstalledSuccessfully) {
            Write-Output "language installed successfully"
            Write-Output "trying to set language preferences"
            SetLanguage -languageTag $languageTag
            Write-Output "trying to copy language preferences"
            CopyLanguageSettings
            #Restart-Computer
        }
        else {
            Write-Output "language could not be installed"
        }
    }
}

InstallLanguage -languageTag $args[0]

原因分析

  1. 线程模型不兼容:Show-ControlPanelItem依赖COM组件创建UI界面,而PowerShell默认使用MTA(多线程单元)模型,部分COM UI组件和WinForms需要STA(单线程单元)环境,线程模型不匹配会触发类型转换错误。
  2. 脚本编码冲突:手动创建的脚本如果是UTF-8带BOM格式,可能导致PowerShell解析上下文异常;而Set-Content生成的脚本默认用ANSI编码,避开了该问题。
  3. 语言上下文干扰:脚本先执行Set-Culture、Set-WinSystemLocale等系统语言修改命令,改变了当前PowerShell会话的文化信息,导致后续调用控制面板项时,COM组件的本地化上下文不匹配,引发异常。
  4. 脚本逻辑错误:InstallLanguage函数中存在赋值运算符替代比较运算符的错误($isLanguageInstalled = $false),导致不必要的语言包安装操作,干扰系统语言环境。

解决方法

方法1:强制STA线程并调整组件加载顺序

PowerShell默认用MTA线程,需强制切换为STA,并提前加载WinForms组件:

  1. 在脚本最顶部添加STA线程声明:
#Requires -RunAsAdministrator
[System.Threading.Thread]::CurrentThread.ApartmentState = [System.Threading.ApartmentState]::STA
  1. 修改CopyLanguageSettings函数,先加载WinForms再调用控制面板命令:
function CopyLanguageSettings() {
    add-type -AssemblyName System.Windows.Forms
    Show-ControlPanelItem -CanonicalName Microsoft.RegionAndLanguage

    Start-Sleep -Milliseconds 1000

    SendKey -key '+{TAB}'
    SendKey -key '{RIGHT}'
    SendKey -key '{TAB}'
    SendKey -key '{ENTER}'
    Start-Sleep -Milliseconds 1000 
    SendKey -key '{TAB}'
    SendKey -key ' '
    SendKey -key '{TAB}'
    SendKey -key ' '
    Start-Sleep -Milliseconds 1000 
    #SendKey -key '{ENTER}'
}

方法2:修复脚本编码问题

手动创建的脚本若为UTF-8带BOM格式,会导致解析异常:

  • 用记事本打开脚本,选择「文件」→「另存为」,编码选择ANSI或UTF-8无BOM,保存后重新运行。

方法3:绕过PowerShell Cmdlet,直接调用控制面板

用Start-Process直接打开区域语言设置界面,避开COM调用问题:

# 替换CopyLanguageSettings中的Show-ControlPanelItem命令
Start-Process "control.exe" "intl.cpl"

若需批量配置,可通过导入/导出XML文件实现(避免SendKeys的不可靠性):

# 导出当前设置到临时文件
control.exe intl.cpl,,/f:"$env:TEMP\region.xml"
# 修改XML后导入设置
control.exe intl.cpl,,/f:"$env:TEMP\region_modified.xml"

方法4:修复脚本逻辑错误

将InstallLanguage函数中的赋值错误改为比较运算符:

# 原错误代码
# if ($isLanguageInstalled = $false) {
# 修改后
if ($isLanguageInstalled -eq $false) {
    InstallLanguagePack -languageTag "en-gb"
}

内容的提问来源于stack exchange,提问作者Tobs

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 01:05:02