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

PowerShell用Read-Host为Word设密码保护时脚本挂起问题排查

故障原因
  • 线程死锁:Word COM对象属于单线程单元(STA)对象,实例创建完成后会持有当前PowerShell会话的STA线程控制权。原脚本在COM对象初始化完成后才调用带-AsSecureString参数的Read-Host,安全输入弹窗需要占用同一个STA线程才能渲染,双方互相等待资源就会导致脚本完全挂起无响应。
  • 密码格式不匹配:ConvertFrom-SecureString返回的是经过DPAPI加密的长序列化字符串,并非用户输入的原始明文。Word COM对象的Password属性仅接受普通明文字符串,就算没有死锁问题,赋值该加密字符串也无法实现正常的密码保护。
  • 隐性逻辑错误:代码中直接使用$SaveFormat::wdFormatDocument枚举值,但未提前加载Word互操作程序集定义该枚举,运行时会触发类型找不到的报错。
修复方案
  1. 所有需要用户交互输入的操作(包括Word文档保护密码的输入),全部移到Word COM对象创建之前执行,从根源上避免STA线程抢占导致的死锁。
  2. 废弃ConvertFrom-SecureString的转换逻辑,将SecureString类型的密码输入解密为普通明文字符串后,再赋值给Word的Password属性。
  3. 直接使用枚举对应的数值代替未定义的枚举常量,避免类型加载错误,同时匹配docx格式的存储需求。
  4. 补充COM对象释放逻辑,避免脚本运行后后台残留WINWORD.EXE进程占用文件。

修正后的可运行完整代码如下:

Add-Type -AssemblyName System.Web
cls

#************ Create Document ******************************
function CreateDocument 
{
    param(
        [string]$DocPassword,
        [string]$UserName,
        [string]$UserPwd
    )
    $PL_Word = New-Object -ComObject Word.Application
    $PL_Word.Visible = $false
    $PL_Document = $PL_Word.Documents.Add()
    $PL_Report = 'C:\TEMP\MyDoc.docx'
    # wdFormatXMLDocument 对应docx格式,枚举值为12
    $PL_Document.SaveAs([ref]$PL_Report,[ref]12)
    $PL_Selection = $PL_Word.Selection

    #****************** Set Document Open Password ********
    $PL_Document.Password = $DocPassword

    #************** Write Content to Document *************************************
    $PL_Selection.TypeParagraph()
    $PL_Selection.TypeText("Username: $UserName")
    $PL_Selection.TypeParagraph()
    $PL_Selection.TypeText("Password: $UserPwd")

    #************** Close Document and Release Resource *************************************
    $PL_Document.Save()
    $PL_Document.Close()
    $PL_Word.Quit()
    [System.Runtime.Interopservices.Marshal]::ReleaseComObject($PL_Word) | Out-Null
    Remove-Variable PL_Word
}

#****************** Collect All User Input Before Initializing COM Object ************************
$PL_PWD = [System.Web.Security.Membership]::GeneratePassword(32,3)
Write-Host "`n`n"
$PL_UN = "Prime\"+(Read-Host ("Enter the username. Entering the Primelending domain is not neccessary."))

Write-Host "`nSummary of the change" -f Yellow
Write-Host "============================" -f Yellow
Write-Host "`nUsername: " -NoNewline
Write-Host "$PL_UN" -f Yellow
Write-Host "New Password: " -NoNewline
Write-Host "$PL_PWD`n" -f Yellow

# Input document password before creating Word COM instance to avoid thread deadlock
$PL_PwdEntry = Read-Host ("Enter the password for the text document record") -AsSecureString
# Decrypt SecureString to plain text
$PL_WdPWD = [System.Net.NetworkCredential]::new("", $PL_PwdEntry).Password

Write-Host "Do you want to update AD (Y/N)" -NoNewline -f Yellow
$PL_Query = Read-Host (" ")

If ($PL_Query.ToUpper() -eq "Y") {
  Write-Host "`nMaking change" -f Green
  #Set-ADAccountPassword -Identity $PL_UN -Reset -NewPassword (ConvertTo-SecureString -AsPlainText "$PL_PWD" -Force)
  # Pass all pre-collected parameters to function, no user interaction inside COM related logic
  CreateDocument -DocPassword $PL_WdPWD -UserName $PL_UN -UserPwd $PL_PWD
}
else {
    Write-Host "`nAbandoning change" -f Green 
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 02:06:22