excel - 通过Powershell关闭时无法停止所有Excel进程

标签 excel powershell user-interface com rcw

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

这是我启动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应用程序。其余的每次脚本结束时执行。

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

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

最佳答案

有关如何释放(Excel)COM对象的一般指南,请参见底部

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

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

因此,为了加快发布速度,您还必须在运行垃圾收集器之前显式地发布这些引用:

$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对象的所有变量来实现的自动化:
& {  # 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() -没有它,即使在PowerShell session 结束时,幕后创建的Excel进程也永远不会终止(当然,在OS用户 session 整体结束时也会终止)。
  • 通常是所需的全部(除非使用全局变量变量存储对Excel对象的引用),因为引用COM对象的脚本/函数变量超出范围,最终COM对象最终也会自动释放。
  • 但是,可能需要-变化,不可预测-才能使Excel进程实际终止,具体取决于何时超出范围变量的对象被垃圾回收。
  • 如果您希望尽快释放COM对象:
  • 必须释放对存储在单个变量中的所有COM对象的引用:
  • 注意,$excel.Quit() 调用不需要;因为有一个更简单且更健壮的替代:
  • 要么:清除所有显式引用COM对象的变量,方法是(请参见上面的第一个代码段):
  • 之一:将它们全部设置为[System.Runtime.InteropServices.Marshal]::ReleaseComObject()
  • 或:将其名称传递给$null
  • 或者,最好是:隐式释放引用(请参见上面的第二代码段):
  • 使用通过Remove-Variable引用子范围中的COM对象的变量,这意味着引用将在离开子范围时隐式释放。
  • 这些方法不仅比调用& { ... }更简单和简洁,而且可以防止以后尝试访问已发布的COM对象。
  • 之后,调用[System.Runtime.InteropServices.Marshal]::ReleaseComObject() 以强制进行即时垃圾回收-但请注意,在垃圾回收器运行时,您的代码(通常只是短暂地)被阻塞。
  • 如果您还想确保在继续之前释放COM对象已经完成:
  • 注意:可能很少需要,因为Excel通常在调用[GC]::Collect()方法时释放资源,例如关闭已打开的文件。
  • 您可以在调用.Quit()之后调用[GC]::WaitForPendingFinalizers() ,但是可能不起作用:负责管理对COM对象本身已完成的访问的RCW(运行时可调用包装器)不能保证当时释放COM资源。 ;来自the docs(添加了重点):
  • “当COM对象上的引用计数变为0时,通常会释放COM对象,尽管这取决于COM对象的实现,并且超出了运行时的控制范围。”
  • 实际上,在手头的情况下,Excel流程不会在[GC]::Collect()调用返回之前终止。那只会在一秒钟后发生
  • 关于excel - 通过Powershell关闭时无法停止所有Excel进程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55423220/

    相关文章:

    excel - 单击组合框时运行宏

    windows - 通过 shell 脚本使用用户名和密码 SSH 到远程 Windows 机器

    整数的 Powershell 输入验证

    c# - 检测弹出窗口何时/是否决定重新定位自身

    javascript - 有没有一种巧妙的方法可以使元素 float 以创建具有可扩展元素的网格,而不会使用 HTML/CSS/JS 浪费太多空间?

    excel - 将工作簿设置为只读

    excel - 使用VBA从一行Excel中删除所有空格

    excel - 跨工作表突出显示重复项跳过第一个实例

    Powershell 无法绑定(bind)参数 ForegroundColor

    qt - 如何制作带有一组按钮的拆分器?