tl;dr
使用以下内容idiom确保释放对 COM 对象的所有引用,这与调用相结合$excel.Quit()
,确保 Excel 进程(最终)终止:
& { # Create a temporary child scope.
$excel = New-Object -ComObject excel.application # create excel object
# ... work with the $excel object and its object model,
# using whatever local variables needed.
# You must *always* call .Quit(), otherwise the Excel process lingers
# for the entire OS user session.
$excel.Quit()
} # All variables created inside { ... } go out of scope
# when this block is exited, ensuring release of all COM objects.
For 一般指导有关如何释放 (Excel) COM 对象,请参阅底部.
$excel.Quit()
is足够最终终止Excel进程, but when发生这种情况取决于垃圾收集器下次运行的时间。
您尝试显式释放 Excel[System.Runtime.InteropServices.Marshal]::ReleaseComObject($excel)
是不够的,因为变量$script:workbook
and $script:ws1
仍然引用 Excel COM 对象直到变量超出范围并且这些引用最终被垃圾收集时才会被释放。
因此,为了speed up释放时,您也必须显式释放这些引用,before运行垃圾收集器:
$script:excel = new-object -ComObject excel.application # create excel object
$script:workbook = $excel.Workbooks.Add() # add a workbook
$script:ws1 = $workbook.Worksheets.Item(1) # reference the 1st sheet
# ...
# You must *always* call .Quit(), otherwise the Excel process lingers
# for the entire OS user session.
$script.excel.Quit()
# Relinquish references to *all* Excel objects.
$script:excel = $script:workbook = $script:ws1 = $null
# Alternative:
# Remove-Variable -Scope Script excel, workbook, ws1
# With all references released, running the garbage collector
# should now release the COM objects and terminate Excel
# shortly after.
[GC]::Collect()
# Note that calling [GC]::WaitForPendingFinalizers() afterwards
# to wait for *completion* of the *doesn't work here*,
# because the CLR-managed RCWs (Runtime-Callable Wrappers for COM objects)
# do not guarantee deterministic release of the underlying COM objects.
Because manually清除/删除所有相关变量容易出错且麻烦, 你可以instead automate该过程中,通过在本地创建引用 COM 对象的所有变量临时子范围, using & { ... }
:
# PREFERABLE ALTERNATIVE to the code above:
& { # Create a temporary child scope.
$excel = new-object -ComObject excel.application # create excel object
$workbook = $excel.Workbooks.Add() # add a workbook
$ws1 = $workbook.Worksheets.Item(1) # reference the 1st sheet
# You must *always* call .Quit(), otherwise the Excel process lingers
# for the entire OS users session.
$excel.Quit()
} # On exiting this block, $excel, $workbook, and $ws1
# go out of scope and release the COM objects when the
# garbage collector runs next.
# Run the garbage collector now.
# The Excel process should terminate shortly after.
[GC]::Collect()
释放 (Excel) COM 对象:
-
总是打电话.Quit()
- 没有它,在幕后创建的 Excel 进程永远不会终止,即使 PowerShell 会话结束时也不会终止(当然,当操作系统用户会话整体结束时它也会终止)。
-
$excel.Quit()
通常是all这是需要的(除非global变量(变量)用于存储对 Excel 对象的引用),因为当引用 COM 对象的脚本/函数变量超出范围时,底层 COM 对象最终也会自动释放。
- 然而,Excel 进程可能需要一段不同的、不可预测的时间才能真正终止,取决于超出范围的变量所在的对象的时间垃圾收集.
-
如果你想释放COM对象尽快:
-
您必须发布对以下内容的引用all您存储的 COM 对象个体变量:
- 请注意,有不需要
[System.Runtime.InteropServices.Marshal]::ReleaseComObject()
来电;因为有一个更简单且更稳健的替代方案:
- Either: Clear all variables referencing COM objects explicitly, by (see first code snippet above):
- 要么:将它们全部设置为
$null
.
- 或者:将他们的名字传递给
Remove-Variable
-
Or, preferably: Release the references implicitly (see second code snippet above):
-
使用引用 COM 对象的变量子范围, via a
& { ... }
block,这意味着引用将在离开子作用域时隐式释放。
-
这些方法不仅比调用更简单、更简洁[System.Runtime.InteropServices.Marshal]::ReleaseComObject()
,而且还可以防止以后尝试访问已发布的 COM 对象。
-
然后,call [GC]::Collect()
强制即时垃圾收集 - 但请注意您的代码是blocked当垃圾收集器运行时(尽管通常只是短暂的)。
-
If you 此外想要确保释放 COM 对象已完全的在继续之前:
-
注意:有可能很少需要这个,因为 Excel 通常会在其.Quit()
方法被调用,例如关闭它已打开的文件。
-
You can call [GC]::WaitForPendingFinalizers()
打电话后[GC]::Collect()
, 但它是可能不起作用:管理对 COM 对象的访问的 RCW(运行时可调用包装器)他们自己最终确定并不保证释放COM资源当时; from the docs https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.marshal.finalreleasecomobject?view=netframework-4.8(强调):