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

WPF PowerShell应用切换用户ID时无响应,求Runspace改造或优化方案

解决WPF PowerShell应用无响应问题:Runspace集成方案

你的WPF应用无响应是因为所有耗时操作(AD查询、WMI调用、文件读取)都在UI线程执行,导致界面阻塞。下面是集成Runspace的完整修改脚本,将耗时任务放到后台线程执行,同时保证UI实时响应:

## Add .xaml file, remove errors and set variables to use them in this script

Add-Type -AssemblyName PresentationFramework

$xamlFile="\\zeus\a\lrc\GROUPS\WCO\USR\STU_DATA\EXPERT\EXPERT\ServiceDesk_guide\SCript\SD-Tool\MainWindow.xaml"
$inputXAML = Get-Content -Path $xamlFile -Raw
$inputXAML = $inputXAML -replace 'mc:Ignorable="d"',''-replace "x:N","N" -replace '^<Win.*','<Window'
[XML]$XAML =$inputXAML
$reader    = New-Object System.Xml.XmlNodeReader $XAML

try{
    $psform = [Windows.Markup.XamlReader]::Load($reader)
}catch{
    Write-host $_.Exception
    throw
}

$xaml.SelectNodes("//*[@Name]") | ForEach-Object {
    Try{
        Set-Variable -Name "var_$($_.Name)" -value $psform.FindName($_.Name) -ErrorAction stop
}catch{
    throw
    }
}

# 创建Runspace池,用于后台执行耗时任务
$runspacePool = [RunspaceFactory]::CreateRunspacePool(1, 2)
$runspacePool.ApartmentState = [System.Threading.ApartmentState]::STA
$runspacePool.Open()

## 点击搜索按钮执行后台任务
$var_OnClick.Add_Click({
    # 禁用搜索按钮,显示加载状态
    $var_OnClick.IsEnabled = $false
    $var_OnClick.Content = "查询中..."

    # 获取当前输入的用户名,后台线程不能直接访问UI控件
    $targetUserName = $var_userName.Text.Trim()
    if (-not $targetUserName) {
        [System.Windows.MessageBox]::Show("请输入用户ID")
        $var_OnClick.IsEnabled = $true
        $var_OnClick.Content = "搜索"
        return
    }

    # 定义后台执行的脚本块
    $backgroundScript = {
        param($userName, $wwg00m, $dedicatedSb)

        #Params
        $Computernames    =  Get-Content -Path "\\zeus\a\lrc\GROUPS\WCO\USR\STU_DATA\EXPERT\EXPERT\ServiceDesk_guide\SCript\Data\Computers in a specific workgroup or domain.csv" |    
                             Select-Object -Skip 3 |       
                             ConvertFrom-Csv  -Delimiter ","
        $pcNamecsv        =  $Computernames.Where({ $_.Details_Table0_User_Name0 -eq $userName}).Details_Table0_Name0 | Where { $_ -like 'BNL0*' -or $_ -like 'BNL5*'}
        $Mailboxrights    =  Import-csv  "\\zeus\a\lrc\GROUPS\WCO\USR\STU_DATA\EXPERT\EXPERT\ServiceDesk_guide\SCript\Data\mailboxRights2.csv" -delimiter "|" 

        # if hostname >1 search for hostname last used
        $ADcomputing = @('Name','LastLogonDate')
        $ADproperty    = foreach ($PC in $pcNamecsv) {Get-ADcomputer $PC -Properties $ADcomputing} 
        $pcName = ($ADproperty | Sort LastLogonDate -Descending | Select -First 1).Name

        # Search for IP address with nslookup
        $ipAddress     = (nslookup $pcName | Select-String Address | Where-Object LineNumber -eq 5).ToString().Split(' ')[-1]

        # find properties in Active Directory
        $adProperties = @(
                'Name','mail','OfficePhone','LockedOut','Enabled',
                'msDS-UserPasswordExpiryTimeComputed','AccountExpirationDate','Givenname','DisplayName'
            )

        $wwgUser      = Get-ADuser -Identity $userName -Server $wwg00m -Properties $adProperties
        $agfUser      = Get-ADuser -Identity $userName -Properties $adProperties
        $agfExpiry    = Get-ADUser -identity $userName –Properties "msDS-UserPasswordExpiryTimeComputed" |
                        Select-Object -Property @{Name="ExpiryDate";Expression={[datetime]::FromFileTime($_.msDS-UserPasswordExpiryTimeComputed)}} | 
                        select -expandproperty "ExpiryDate" -first 1 
        $wwgExpiry    = Get-ADUser -identity $userName -server $wwg00m –Properties "msDS-UserPasswordExpiryTimeComputed" |
                        Select-Object -Property @{Name="ExpiryDate";Expression={[datetime]::FromFileTime($_.msDS-UserPasswordExpiryTimeComputed)}} | 
                        select -expandproperty "ExpiryDate" -first 1 

        # Check if VPN connection was made before accessing Windows environment                 
        $windowsLogon = try {(Get-Childitem -path \\$pcName\c$\Users\*\AppData\Local\Temp\logon.txt -ErrorAction Stop | Sort "LastWriteTime"  -Descending | Select -First 1).LastWriteTime} catch {$null}
        $vpnLogon     = (Get-ADuser $userName -server mgm.agf.be -properties Modified).Modified

        # Find properties in Active Directory from nested AVC groups
        $SID          = (Get-ADuser $userName -Properties SID).SID -replace 'SID',''
        $AVCSEC       = (Get-ADGroup -LDAPFilter "(member=CN=$SID,CN=ForeignSecurityPrincipals,DC=wwg00m,DC=rootdom,DC=net)" `
                                     -Server wwg00m.rootdom.net -Properties Name).Name 
        $DedicatedProperties = @('Name','Description')
        $dedicatedClients    = Get-ADcomputer -filter * -SearchBase $dedicatedSb -server $wwg00m -properties $DedicatedProperties 
        $dedicatedclientName = $dedicatedClients.Where({ $_.Description -Match $SID}).Name

        # Shared mailbox properties 
        $Mailbox   = $Mailboxrights.Where({$_.Trustee -match $userName}) | Select-Object "Display Name", "Email Address",  "Rights","Domain" 

        # Computer information, hostname obtained from csv list 
        $model            = try {(Get-CimInstance -Class Win32_ComputerSystem -ComputerName $pcName -ea Stop ).Model} catch {$null}
        $osVersion        = try {(Get-CimInstance win32_operatingsystem -ComputerName $pcName -ea Stop ).Version} catch {$null}
        $osSystem         = try {(Get-ADcomputer $pcName -properties operatingsystem -ea Stop ).operatingsystem} catch {$null}

        # Bitlocker recover key 
        $objComputer      =  Get-ADComputer $pcName 
        $Bitlocker_Object =  Get-ADObject -Filter {objectclass -eq 'msFVE-RecoveryInformation'} -SearchBase $objComputer.DistinguishedName -Properties 'msFVE-RecoveryPassword'
        $bitlockerKey     = ($Bitlocker_Object | select msFVE-RecoveryPassword).msFVE-RecoveryPassword

        # 返回所有需要更新UI的数据
        [PSCustomObject]@{
            agfUser = $agfUser
            wwgUser = $wwgUser
            agfExpiry = $agfExpiry
            wwgExpiry = $wwgExpiry
            pcName = $pcName
            ipAddress = $ipAddress
            windowsLogon = $windowsLogon
            vpnLogon = $vpnLogon
            AVCSEC = $AVCSEC
            dedicatedclientName = $dedicatedclientName
            Mailbox = $Mailbox
            model = $model
            osVersion = $osVersion
            osSystem = $osSystem
            bitlockerKey = $bitlockerKey
        }
    }

    # 创建后台任务
    $psJob = [PowerShell]::Create().AddScript($backgroundScript).AddParameters(@{
        userName = $targetUserName
        wwg00m = "wwg00m.rootdom.net"
        dedicatedSb = "OU=vClientD,OU=AZ-BENE,OU=E2,OU=VDI,OU=Prod,OU=AVC,OU=Clients,DC=wwg00m,DC=rootdom,DC=net"
    })
    $psJob.RunspacePool = $runspacePool

    # 异步执行任务,完成后更新UI
    $asyncResult = $psJob.BeginInvoke()
    $waitHandle = $asyncResult.AsyncWaitHandle
    $waitHandle.WaitOne() | Out-Null

    try {
        $resultData = $psJob.EndInvoke($asyncResult)
        if ($resultData) {
            # 回到UI线程更新控件,WPF控件只能在创建线程操作
            $psform.Dispatcher.Invoke([Action]{
                # Main Tabitem
                $var_fullname.Text =    $resultData.agfUser.DisplayName
                $var_phone.Text    =    $resultData.agfUser.OfficePhone
                $var_hostname.Text =    $resultData.pcName
                $var_mail.Text     =    $resultData.agfUser.mail
                $var_enabled.Text  =    $resultData.agfUser.enabled
                $var_exp_acc.Text  =    $resultData.agfUser.AccountExpirationDate
                $var_exp_pass.Text =    $resultData.agfExpiry
                $var_locked.Text   =    $resultData.agfUser.LockedOut

                # wwg00m Tabitem
                $var_enabled_wwg00m.Text  =    $resultData.wwgUser.enabled
                $var_exp_acc_wwg00m.Text  =    $resultData.wwgUser.AccountExpirationDate
                $var_exp_pass_wwg00m.Text =    $resultData.wwgExpiry
                $var_locked_wwg00m.Text   =    $resultData.wwgUser.LockedOut

                # Computer Tabitem
                $var_osName.Text   = $resultData.osVersion
                $var_model.Text    = $resultData.model
                $var_iAddress.Text = $resultData.ipAddress
                $var_bkey.Text     = $resultData.bitlockerKey
                $var_cName.Text    = $resultData.pcName
                $var_vName.Text    = $resultData.osSystem

                # Used in more then 1 Tabitem
                $var_vpnLog.Text        =    $resultData.vpnLogon
                $var_windowsLog.Text    =    $resultData.windowsLogon
                $var_ipCurrent.Text     =    $resultData.ipAddress

                # AVC Tabitem
                $var_Pooled.Text = if ($resultData.AVCSEC -like "AVC-P-POOL-AZBENE-VDIW10*") {"Available"} else {"Not available"}
                $var_Dedicated.Text = if ($resultData.AVCSEC -like "AVC-P-POOL-AZBENE-DCW10*") {"Available"} else {"Not available"}
                $var_uat.Text = if ($resultData.AVCSEC -like "AVC-QS-POOL-AZBENE-VDIW10*") {"Available"} else {"Not available"}
                $var_poolRing0.Text = if ($resultData.AVCSEC -like "AVC-P-POOL-AZBENE-RING0-VDIWIN10") {"Available"} else {"Not available"}
                $var_dedicatedRing0.Text = if ($resultData.AVCSEC -like "AVC-P-POOL-AZBENE-RING0-DEDWIN10") {"Available"} else {"Not available"}
                $var_adminLight.Text = if ($resultData.AVCSEC -like "AVC-P-CONF-DCW10-AdminLight") {"Available"} else {"Not Available"}
                $var_hostNamededicated.Text = $resultData.dedicatedclientName

                # DataGrid2 Mail Tabitem                       
                $var_dataGrid2.ItemsSource = $resultData.Mailbox

                # 重置搜索按钮状态
                $var_OnClick.IsEnabled = $true
                $var_OnClick.Content = "搜索"
            })
        }
    } catch {
        $psform.Dispatcher.Invoke([Action]{
            [System.Windows.MessageBox]::Show("查询出错: $_")
            $var_OnClick.IsEnabled = $true
            $var_OnClick.Content = "搜索"
        })
    } finally {
        $psJob.Dispose()
    }
})

# unlock agf account button
$var_unlockAGF.Add_Click{
    $userName = $var_userName.Text.Trim()
    if (-not $userName) {
        [System.Windows.MessageBox]::Show("请输入用户ID")
        return
    }
    Unlock-ADAccount -Identity $userName 
    Start-Sleep -Seconds 2
    [System.Windows.MessageBox]::Show('Account has been unlocked')
}

## DataGrid and Dropdownlist Tabitem: Access
$var_comboBox.Text = $null
$var_dataGrid.ItemsSource = $null

$var_comboBox.Add_SelectionChanged({
    $userName = $var_userName.Text.Trim()
    if (-not $userName) {
        $var_dataGrid.ItemsSource = $null
        return
    }

    # 同样用Runspace处理下拉选择的耗时查询
    $var_comboBox.IsEnabled = $false
    $wwg00m = "wwg00m.rootdom.net"

    $backgroundScript = {
        param($userName, $wwg00m)
        $agfGroups     = (Get-ADuser -identity $userName -Properties MemberOf).MemberOf -Replace 'CN=', '' -Replace ',.*', ''
        $wwg00mGroups  = (Get-ADuser -identity $userName -server $wwg00m  -Properties MemberOf).MemberOf -Replace 'CN=', '' -Replace ',.*', ''
        $sid           = (Get-ADuser -identity $userName -Properties SID).SID -replace 'SID','' 
        $avcGroups     = (Get-ADGroup -LDAPFilter "(member=CN=$sid,CN=ForeignSecurityPrincipals,DC=wwg00m,DC=rootdom,DC=net)" `
                                                              -Server $wwg00m -Properties Name).Name 
        [PSCustomObject]@{
            agfGroups = $agfGroups
            wwg00mGroups = $wwg00mGroups
            avcGroups = $avcGroups
        }
    }

    $psJob = [PowerShell]::Create().AddScript($backgroundScript).AddParameters(@{userName=$userName; wwg00m=$wwg00m})
    $psJob.RunspacePool = $runspacePool

    $asyncResult = $psJob.BeginInvoke()
    $waitHandle = $asyncResult.AsyncWaitHandle
    $waitHandle.WaitOne() | Out-Null

    try {
        $resultData = $psJob.EndInvoke($asyncResult)
        $psform.Dispatcher.Invoke([Action]{
            $item = $var_comboBox.SelectedItem.Content
            if ($item -eq "wwg00m") {
                $var_dataGrid.ItemsSource = $resultData.wwg00mGroups
            } elseif ($item -eq "agf.be" ) {
                $var_dataGrid.ItemsSource  = $resultData.agfGroups
            } elseif ($item -eq "AVC" ) {
                $var_dataGrid.ItemsSource   = $resultData.avcGroups
            }
            $var_comboBox.IsEnabled = $true
        })
    } catch {
        $psform.Dispatcher.Invoke([Action]{
            [System.Windows.MessageBox]::Show("查询组信息出错: $_")
            $var_comboBox.IsEnabled = $true
        })
    } finally {
        $psJob.Dispose()
    }
})

## Connect to host with msra "Remote assistance"
$var_connectHost.Add_Click({
    $pcName = $var_hostname.Text
    if ($pcName) {
        Start-Process -FilePath "C:\Windows\System32\msra.exe" -Args "/Offerra \\$pcName"
    } else {
        [System.Windows.MessageBox]::Show("请先查询用户信息获取主机名")
    }
})

## show GUI
$psform.ShowDialog()  

# 关闭Runspace池
$runspacePool.Close()
$runspacePool.Dispose()

关键改动说明

  • Runspace池管理:创建固定大小的Runspace池,复用后台线程,避免频繁创建销毁线程的开销
  • UI线程隔离:后台任务只负责数据查询,所有UI控件更新通过Dispatcher.Invoke回到UI线程执行,符合WPF线程安全要求
  • 加载状态反馈:搜索按钮在查询过程中禁用并显示"查询中...",让用户感知到后台正在处理
  • 错误处理:后台任务异常时捕获并提示用户,同时恢复按钮状态
  • 替换WMI为CIM:Get-CimInstance比`Get
相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 07:42:33