通过Powershell关闭时无法停止所有Excel进程 [英] Can't get all excel processes to stop when closing through Powershell

查看:98
本文介绍了通过Powershell关闭时无法停止所有Excel进程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用此代码,我将打开excel(可见= false,因此用户无法看到它),写入工作簿,然后在脚本结束后打开excel(使其可见)或完全关闭而不保存。当我保存excel时,将其保持打开状态,结束脚本,然后稍后手动关闭excel,任务管理器中没有后台进程。但是,当我用脚本关闭excel时,它仍保留在任务管理器中。

With this code, I am opening excel(with visible = false so the user cannot see it), writing to a workbook, and then either opening excel(making it visible) after the script ends or closing it completely without saving. When I save excel, leave it open, end the script, and then manually close excel later, there are no background processes in the task manager. However, when I close excel with the script, it remains in the task manager.

这是我开始使用excel的方式:

Here is how I start excel:

    $script:excel = new-object -ComObject excel.application # create excel object
    $excel.visible = $false # hide excel window
    $script:workbook = $excel.Workbooks.Add() # add excel file
    $script:ws1 = $workbook.Worksheets.Item(1) # create new sheet

这是我的关闭方法:

        [gc]::Collect()
        [gc]::WaitForPendingFinalizers()
        if ($script:closeOnX) {
            #only do this if not keeping excel open
            Write-Host "Closing Excel"
            $excel.Quit()
        }
        [System.Runtime.InteropServices.Marshal]::ReleaseComObject($excel)

closeOnX只是一个标志,因此它实际上仅在某些情况下关闭excel应用程序。其余的在脚本每次结束时执行。

closeOnX is just a flag so it only actually closes the excel app on certain occasions. The rest is executed each time the script ends.

当我结束脚本并同时关闭excel时,我只希望当前的excel进程关闭(这就是为什么我不想停止进程的原因),不要关闭用户可能正在处理的其他工作簿。

When I end the script and close excel at the same time, I want only the current excel process to close (which is why I don't want to stop-process) and not close other workbooks that the user may be working on.

当我结束脚本时,保存并打开excel,我希望当用户手动关闭excel时所有进程都消失了。 (正在运行)

When I end the script, save and open the excel, I want all the processes to be gone when the user manually closes excel. (This is working)

推荐答案

有关如何发布(Excel)COM对象的一般指南,请参阅底部部分

For general guidance on how to release (Excel) COM objects, see the bottom section.

$ excel.Quit() 足以最终终止 ,但是何时发生取决于垃圾收集器下次运行的时间。

$excel.Quit() is enough to eventually terminate the Excel process, but when that happens depends on when the garbage collector happens to run the next time.

您尝试使用 [System.Runtime.InteropServices.Marshal] :: ReleaseComObject($ excel)是不足的,因为变量 $ script:workbook $ script:ws1 仍具有对Excel COM的引用对象,直到变量超出范围,这些引用最终都会被垃圾回收。

Your attempt to explicitly release Excel with [System.Runtime.InteropServices.Marshal]::ReleaseComObject($excel) is insufficient, because variables $script:workbook and $script:ws1 still have references to Excel COM objects that will not be released until the variables have gone out of scope and these references are eventually garbage collected.

因此,为了提高速度在发布版本中,您还必须在运行垃圾收集器之前显式发布这些引用。 r:

Therefore, in order to speed up the release, you must release these references explicitly too, before running the garbage collector:

$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 users 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
# at 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.

由于手动清除/删除所有相关变量容易出错且麻烦 ,您可以使用&通过创建临时子作用域中本地引用COM对象的所有变量来自动化该过程。 {...}

Because manually clearing / removing all relevant variables is error-prone and cumbersome, you can automate the process by creating all variables that reference COM objects locally in a temporary child scope, using & { ... }:

& {  # 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()






< h3>释放(Excel)COM对象:


  • 始终调用 .Quit() -如果没有它,即使在PowerShell会话结束时,幕后创建的Excel进程也永远不会终止(当然,在OS用户会话整体结束时终止) )。


    Releasing (Excel) COM Objects:

    • ALWAYS call .Quit() - without it, the Excel process that is created behind the scenes is never terminated, not even when the PowerShell session ends (of course, it is terminated when the OS user session as a whole ends).

      $ excel.Quit()通常是全部 (除非使用 global 变量变量存储对Excel对象的引用),因为引用COM对象的脚本/函数变量超出了范围,因此基础COM对象也会最终自动释放。

      $excel.Quit() is usually all that is needed (unless global variables variables are used to store references to Excel objects), because with the script / function variables that reference COM objects going out of scope, the underlying COM objects are eventually released automatically too.


      • 但是,这可能需要-变化,不可预测-而Excel流程实际上需要终止,具体取决于何时超出范围变量的对象垃圾收集

      • However, it may take a - varying, unpredictable - while for the Excel process to actually terminate, depending on when the objects that that the gone-out-of-scope variables are garbage-collected.

      如果要尽快释放COM对象

      If you want the COM objects to be released as quickly as possible:


      • 必须释放对存储在中的所有所有 COM对象的引用>单个变量

      • You must release references to all COM objects you've stored in individual variables:


      • 请注意,不需要 [System.Runtime.InteropServices.Marshal] :: ReleaseComObject() 调用;因为有一个更简单且更健壮的替代方法

      • 要么:清除所有显式引用COM对象的变量,方法是(请参见第一个代码上面的代码段):


        • 其中之一:将它们全部设置为 $ null

        • 或:将其名称传递给 Remove-Variable

        • Note that there is NO NEED for [System.Runtime.InteropServices.Marshal]::ReleaseComObject() calls; because there is a simpler and more robust alternative:
        • Either: Clear all variables referencing COM objects explicitly, by (see first code snippet above):
          • either: setting them all to $null.
          • or: passing their names to Remove-Variable

          • 通过&使用在子范围中引用COM对象的变量。 {...} ,这意味着引用将在离开子范围时隐式释放。

          • Use variables that reference COM objects in a child scope, via a & { ... } block, which means that the references will implicitly be released on leaving the child scope.

          这些方法不仅比调用 [System.Runtime.InteropServices.Marshal] :: ReleaseComObject更简单,更简洁(),但也可以防止以后尝试访问已发布的COM对象。

          These approaches are not only simpler and more concise than calling [System.Runtime.InteropServices.Marshal]::ReleaseComObject(), but also prevent later attempts at accessing already-released COM objects.

          然后,调用 [GC] :: Collect() 强制进行即时垃圾收集-但请注意,垃圾收集器运行时,您的代码(通常只是短暂地)被阻塞。

          Afterwards, call [GC]::Collect() to force instant garbage collection - but note that your code is (usually only briefly) blocked while the garbage collector runs.

          如果您另外要确保释放COM对象具有已完成,然后继续:

          If you additionally want to make sure that releasing the COM objects has completed before you continue:


          • 注意:几乎不需要为此,因为Excel通常会在调用 .Quit()方法时释放资源,例如关闭它已打开的文件。

          • Note: There's probably rarely a need for this, because Excel usually releases resources when its .Quit() method is called, such as closing files it has open.

          您可以在调用<$ c $后调用 [GC] :: WaitForPendingFinalizers() c> [GC] :: Collect(),但可能不起作用:管理对COM对象不能保证释放COM资源 ;来自文档(强调):

          You can call [GC]::WaitForPendingFinalizers() after calling [GC]::Collect(), but it is likely not to work: The RCWs (runtime-callable wrappers) that manage access to the COM objects themselves being finalized does not guarantee release of the COM resources at that time; from the docs (emphasis added):


          • 当COM对象上的引用计数变为0时,COM对象为<通常会释放它们,尽管该取决于COM对象的实现,并且不受运行时的控制。

          实际上,在当前情况下,Excel进程在 [GC] :: WaitForPendingFinalizers()终止 c>呼叫返回;


        • Indeed, in the case at hand the Excel process does not terminate before the [GC]::WaitForPendingFinalizers() call returns; that only happens within a second or so afterwards.

          这篇关于通过Powershell关闭时无法停止所有Excel进程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆