如何在 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
失败.
成员访问枚举在无输入和单输入对象情况下的行为很不幸,但无法修复,以免破坏向后兼容性。
- 请参阅 GitHub issue #6802 进行讨论。
在 PowerShell (Core) 7+ 中,可以使用空合并运算符 ??
解决以下问题:
$exelPidsBefore = (Get-Process -ErrorAction Ignore Excel).Id ?? @()
我在网上找不到任何关于在约束模式下如何关闭 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
失败.成员访问枚举在无输入和单输入对象情况下的行为很不幸,但无法修复,以免破坏向后兼容性。
- 请参阅 GitHub issue #6802 进行讨论。
在 PowerShell (Core) 7+ 中,可以使用空合并运算符
??
解决以下问题:$exelPidsBefore = (Get-Process -ErrorAction Ignore Excel).Id ?? @()