如何在 Powershell 约束模式下关闭 excel ComObject

How to Close excel ComObject in Powershell Constrained mode

我在网上找不到任何关于在约束模式下如何关闭 ComObject 的信息。正常程序是使用 $com.Quit(),但在受限模式下不允许这样做。此外,[System.Runtime.InteropServices.Marshal]::ReleaseComObject() 在约束模式下是不允许的。

mklement0 建议我在以下线程中提出一个新问题:

自从我在另一个线程中提出问题后,我想出了一个方法来做到这一点。它不像调用 .Quit() 那样整洁,但它可以工作。在我调用新的 ComObject 之前,我获得了应用程序的所有打开进程的列表(在本例中为 excel)并将其存储在一个数组中。然后,我在保存文件后关闭所有不在初始数组中的打开的 pid。

示例:

$StartOpenExcel = get-process excel | ForEach-Object {$_.id} #make an array with the pids;
​$excel=New-Object -ComObject excel.application;
{...do something here }
$workbook.SaveAs($fileName,51);#save the excel doc
Get-Process excel | ForEach-Object{
    if($StartOpenExcel -contains $_.id -eq $false){
        kill -Id $_.Id;
    }
 }
II $fileName; #open the excel

让我提供一个更符合 PowerShell 习惯的解决方案,它的性能应该也更好:

# Capture the PIDs (process IDs) of all *preexisting* Excel processes
# In PowerShell 7+, you can simplify to (see bottom section for a discussion):
#    $exelPidsBefore = (Get-Process -ErrorAction Ignore Excel).Id ?? @()
$excelPidsBefore = @(
  Get-Process -ErrorAction Ignore Excel | Select-Object -ExpandProperty Id
)

# Perform the desired programmatic Excel operations:

# Create an Excel COM Automation object, which
# invariably creates a *new* Excel process.
$excel = New-Object -ComObject Excel.Application

# Determine the PID of the just-launched new Excel process.
# Note: This assumes that no *other* processes on your system have 
#       simultaneously launched Excel processes (which seems unlikely).
$excelComPid = 
  Compare-Object -PassThru $excelPidsBefore (Get-Process -ErrorAction Ignore Excel).Id

# Work with the Excel Automation object.
# ...

# Clean up by terminating the COM-created Excel process.
# NOTE: 
#  * As stated in your question, you would normally use $excel.Quit()
#    but given that your running in *constrained language mode*, you
#    are not permitted to invoke *methods*.
Stop-Process -Id $excelComPid

作为一个 - 完全可选的 - 除了:

  • 通常Get-Process -ErrorAction Ignore Excel | Select-Object -ExpandProperty Id可以简化为(Get-Process -ErrorAction Ignore Excel).Id(后面就是这么做的),感谢member-access enumeration - 成员枚举表达式不仅更简洁,而且更高效

  • 在这种情况下无法按预期工作的原因是,如果 GetProcess、[=15= 没有 no 输出] 被返回,然后 @() 包装成一个数组,产生一个单元素数组,其唯一元素是 $null,并将这样的数组传递给 Compare-Object 失败.

  • 成员访问枚举在无输入和单输入对象情况下的行为很不幸,但无法修复,以免破坏向后兼容性。

  • PowerShell (Core) 7+ 中,可以使用空合并运算符 ?? 解决以下问题:

    $exelPidsBefore = (Get-Process -ErrorAction Ignore Excel).Id ?? @()