简体   繁体   English

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

[英]Can't get all excel processes to stop when closing through Powershell

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.使用此代码,我打开 excel(使用 visible = false 因此用户看不到它),写入工作簿,然后在脚本结束后打开 excel(使其可见)或完全关闭它而不保存。 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.当我保存excel,保持打开状态,结束脚本,稍后手动关闭excel时,任务管理器中没有后台进程。 However, when I close excel with the script, it remains in the task manager.但是,当我用脚本关闭 excel 时,它仍保留在任务管理器中。

Here is how I start 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

Here is how I close it:这是我关闭它的方法:

        [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 is just a flag so it only actually closes the excel app on certain occasions. closeOnX 只是一个标志,因此它只会在某些情况下实际关闭 excel 应用程序。 The rest is executed each time the script ends.每次脚本结束时都会执行 rest。

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 时,我希望当用户手动关闭 excel 时所有进程都消失。(这是有效的)

For general guidance on how to release (Excel) COM objects, see the bottom section .有关如何释放 (Excel) COM 对象的一般指南,请参阅底部部分

$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. $excel.Quit()足够的,最终终止Excel进程,但是这种情况发生时,依赖垃圾收集器发生下一次运行。

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.您尝试使用[System.Runtime.InteropServices.Marshal]::ReleaseComObject($excel)显式释放 Excel 是不够的,因为变量$script:workbook$script:ws1仍然引用了 Excel COM 对象,这些对象直到变量已经超出范围,这些引用最终会被垃圾收集。

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.

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 & { ... } :由于手动清除/删除所有相关变量容易出错且麻烦,因此您可以通过使用& { ... }临时子作用域中创建本地引用 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()

Releasing (Excel) COM Objects:释放 (Excel) COM 对象:

  • 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).始终调用.Quit() - 没有它,在幕后创建的 Excel 进程永远不会终止,甚至在 PowerShell 会话结束时也不会终止(当然,它会在操作系统用户会话作为一个整体结束时终止)。

  • $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.Quit()通常是所有必需的(除非全局变量变量用于存储引用到Excel对象),因为脚本/函数变量引用COM对象的范围走出去,底层COM对象最终被释放也是自动的。

    • 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 .但是, Excel 进程实际终止可能需要一个变化的、不可预测的时间,具体取决于何时对超出范围的变量进行垃圾收集的对象
  • 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 :您必须释放对存储在各个变量中的所有COM 对象的引用

      • Note that there is NO NEED for [System.Runtime.InteropServices.Marshal]::ReleaseComObject() calls;请注意,不需要[System.Runtime.InteropServices.Marshal]::ReleaseComObject()调用; because there is a simpler and more robust alternative :因为有一个更简单、更强大的替代方案
      • Either: Clear all variables referencing COM objects explicitly , by (see first code snippet above):或者:通过(参见上面的第一个代码片段)清除所有显式引用 COM 对象的变量
        • either: setting them all to $null .要么:将它们全部设置为$null
        • or: passing their names to Remove-Variable或:将他们的名字传递给Remove-Variable
      • Or, preferably : Release the references implicitly (see second code snippet above):或者,最好隐式释放引用(参见上面的第二个代码片段):
        • 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.使用在子作用域中引用 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.这些方法不仅比调用[System.Runtime.InteropServices.Marshal]::ReleaseComObject()更简单、更简洁,而且可以防止以后尝试访问已发布的 COM 对象。

    • Afterwards, call [GC]::Collect() to force instant garbage collection - but note that your code is blocked while the garbage collector runs (albeit usually only briefly) .之后,调用[GC]::Collect()强制进行即时垃圾收集 - 但请注意,您的代码在垃圾收集器运行时被阻塞(尽管通常只是短暂的)。

  • If you additionally want to make sure that releasing the COM objects has completed before you continue:如果您想要确保做出释放COM对象已完成,然后再继续:

    • 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.注意:可能很少需要 this ,因为 Excel 通常在调用其.Quit()方法时释放资源,例如关闭它已打开的文件。

    • 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 ;您可以在调用[GC]::Collect() [GC]::WaitForPendingFinalizers()之后调用[GC]::WaitForPendingFinalizers() ,但它可能不起作用:管理对正在最终确定的 COM 对象本身的访问的 RCW(运行时可调用包装器)不会当时COM资源的保证释放; from the docs (emphasis added):来自文档(强调):

      • "When the reference count on the COM object becomes 0, the COM object is usually freed, although this depends on the COM object's implementation and is beyond the control of the runtime ." “当 COM 对象上的引用计数变为 0 时,通常会释放 COM 对象,尽管这取决于 COM 对象的实现并且超出了运行时的控制。”

      • Indeed, in the case at hand the Excel process does not terminate before the [GC]::WaitForPendingFinalizers() call returns;事实上,在当前情况下Excel进程终止[GC]::WaitForPendingFinalizers()调用返回; that only happens within a second or so afterwards .只有一秒钟左右内发生。

Another very hacky way of doing this is to show the excel window, then use the hwnd to identify and shut down the process:)另一种非常 hacky 的方法是显示 excel window,然后使用 hwnd 识别并关闭进程:)

# Create the Excel object
$Excel = New-Object -Com Excel.Application

### Do your stuff ###

# Show the spreadsheet so that a HWND value exists in the COM Object
$Excel.Visible = $true

# Save spreadsheet
$Workbook.Save()

# Close spreadsheet using generated HWND
Get-Process -Name "*excel*" | Where-Object {$_.MainWindowHandle -eq $Excel.hwnd} | Stop-Process

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何在 PowerShell 脚本后阻止 Excel 进程在后台运行? - How can I stop Excel processes from running in the background after a PowerShell script? Excel中的Powershell格式-机器中有2个物理驱动器时无法正确对齐 - Powershell formatting in Excel - Can't get proper alignment when 2 physical drives are in a machine 关闭Windows窗体应用程序和excel.exe进程如何进行交互(可能通过垃圾收集器进行交互)? - How does closing a windows forms applications and excel.exe processes interact (potentially through the garbage collector)? Visual Basic - 关闭 excel 仍然留下进程 - Visual Basic - Closing excel still leaves processes Excel没有关闭 - Excel isn't closing 使用 powershell 时停止将数字转换为 Excel 中的文本 - Stop Numeric converting to text in Excel when using powershell 无法从 PowerShell 中选择 Excel 工作表 - Can't select Excel sheet from PowerShell 如何阻止excel运行所有错误 - how to stop excel from running through all the errors Powershell - 如果没有Excel进程,添加catch以获取 - Powershell - add catch to pick up if there are no Excel processes Excel 宏通过 powershell 运行,但在 Windows 任务计划程序运行时不运行 - Excel macros run through powershell but not when run by windows task scheduler
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM