以域管理员启动进程后Wait-Process报拒绝访问,如何等待进程完成?
解决跨用户上下文等待安装进程完成的问题
嘿,我明白你遇到的麻烦了——在低权限用户脚本里跑管理员权限的安装,还要等它跑完再继续,但直接用Wait-Process碰上个权限拒绝的报错。这其实是因为当前用户没有权限访问域管理员身份启动的进程对象,所以得换个思路来处理。
下面给你几个实用的解决方案,按优先级推荐:
1. 直接用Start-Process的-Wait参数(首选)
这是最简单的办法,Start-Process本身就带了-Wait参数,当你用-Credential指定域管理员身份启动安装程序时,它会自动等待进程完成,不需要后续再调用Wait-Process。而且它内部处理了权限问题,不会出现拒绝访问的情况。
示例代码:
# 获取域管理员凭据 $domainAdminCred = Get-Credential -Message "请输入域管理员账号信息" # 启动安装程序并等待完成 Start-Process -FilePath "C:\你的安装程序路径\installer.exe" ` -Credential $domainAdminCred ` -Wait ` -NoNewWindow # 如果不需要弹出新窗口可以加这个参数 # 到这里安装已经完成,继续执行后续任务 Write-Host "安装已完成,开始执行后续操作..."
注意:如果你的安装程序启动后会立刻退出,实际安装工作由它启动的子进程完成,那这个方法可能会提前继续执行脚本。这种情况就需要用下面的方法。
2. 跟踪主进程及所有子进程
有些安装程序会启动子进程来完成实际安装,主进程直接退出,这时候就得跟踪所有相关进程直到它们全部结束。我们可以用CIM查询来获取进程信息,避免权限问题:
$domainAdminCred = Get-Credential -Message "请输入域管理员账号信息" # 用ProcessStartInfo启动进程,获取主进程ID $startInfo = New-Object System.Diagnostics.ProcessStartInfo $startInfo.FileName = "C:\你的安装程序路径\installer.exe" $startInfo.UserName = $domainAdminCred.UserName $startInfo.Password = $domainAdminCred.Password $startInfo.UseShellExecute = $false $installProcess = New-Object System.Diagnostics.Process $installProcess.StartInfo = $startInfo $installProcess.Start() | Out-Null $mainProcessId = $installProcess.Id # 定义递归函数获取所有子进程 function Get-AllChildProcesses { param([int]$ParentProcessId) $childProcesses = Get-CimInstance Win32_Process | Where-Object { $_.ParentProcessId -eq $ParentProcessId } foreach ($child in $childProcesses) { $child Get-AllChildProcesses -ParentProcessId $child.ProcessId } } # 循环等待主进程和所有子进程结束 do { # 检查主进程是否存在 $mainProcess = Get-CimInstance Win32_Process | Where-Object { $_.ProcessId -eq $mainProcessId } # 获取所有关联子进程 $childProcesses = Get-AllChildProcesses -ParentProcessId $mainProcessId # 主进程和所有子进程都不存在时退出循环 if (-not $mainProcess -and (-not $childProcesses)) { break } Start-Sleep -Seconds 2 # 每2秒检查一次 } while ($true) Write-Host "所有安装相关进程已结束,继续执行后续任务..."
3. 通过计划任务间接等待(复杂场景备选)
如果上面的方法都不适用,比如安装程序需要桌面交互,或者权限限制更严格,可以创建一个临时计划任务,以域管理员身份运行安装,然后等待任务完成:
$domainAdminCred = Get-Credential -Message "请输入域管理员账号信息" $tempTaskName = "临时安装任务_$(Get-Random)" # 生成唯一任务名避免冲突 # 创建计划任务(立即执行) schtasks /create /tn $tempTaskName ` /tr "C:\你的安装程序路径\installer.exe" ` /ru $domainAdminCred.UserName ` /rp $domainAdminCred.GetNetworkCredential().Password ` /sc once /st (Get-Date).AddMinutes(1).ToString("HH:mm") /f # 立即启动任务 schtasks /run /tn $tempTaskName # 等待任务完成 do { $taskStatus = schtasks /query /tn $tempTaskName /fo csv | ConvertFrom-Csv | Select-Object -ExpandProperty Status # 任务状态为Ready或Completed时表示已完成 if ($taskStatus -eq "Ready" -or $taskStatus -eq "Completed") { break } Start-Sleep -Seconds 5 } while ($true) # 删除临时任务 schtasks /delete /tn $tempTaskName /f Write-Host "安装任务已完成,继续执行后续操作..."
为什么Wait-Process会报错?
简单来说,当前低权限用户的安全上下文没有权限访问域管理员身份启动的进程内核对象,所以直接调用Wait-Process会触发拒绝访问的错误。而上面的方法要么是让启动进程的命令本身处理等待,要么是用权限允许的方式查询进程状态,避开了这个问题。
内容的提问来源于stack exchange,提问作者SimonS




