【问题标题】:How can I stop Excel processes from running in the background after a PowerShell script?如何在 PowerShell 脚本后停止 Excel 进程在后台运行?
【发布时间】:2016-12-26 23:11:22
【问题描述】:

无论我尝试什么,Excel 2013 都会继续在 Windows 10 的后台运行,无论我在 PowerShell 脚本末尾发出什么命令。我已经尝试将我发现的所有建议添加到我的脚本末尾,并且我打开的唯一 Excel 对象继续保持打开状态。这是我在脚本末尾的内容。还有其他建议吗?

## Quit Excel and Terminate Excel Application process:
$xlsxwb.quit
$xlsxobj.Quit

[System.Runtime.Interopservices.Marshal]::ReleaseComObject($xlsxobj)
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($xlsxwb)
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($xlsxSh1)
Start-Sleep 1
'Excel processes: {0}' -f @(Get-Process excel -ea 0).Count

【问题讨论】:

  • 我总是以Stop-Process 终止进程。大锤方法。似乎没有其他任何好处。
  • 请尝试使用 .quit()。另外,最后释放应用程序对象,按创建的相反顺序释放对象。
  • 解决方案呈现here in stackoverflow

标签: excel powershell windows-10 excel-2013


【解决方案1】:

我遇到了同样的问题并尝试了各种解决方案,但均未成功。当我开始释放我保存为变量的所有 COM 对象时,我更接近了,而不仅仅是工作簿、工作表和 Excel 应用程序的对象。

以如下示例代码为例:

$Excel = New-Object -ComObject Excel.Application
$Excel.Visible = $False
$Workbook = $Excel.Workbooks.Open("C:\Temp\test.xlsx")
$Worksheet = $Workbook.Worksheets.Item(1)
$UsedRange = $Worksheet.UsedRange
$Range = $Worksheet.Range("A1:B10")
$Workbook.Close()
$Excel.Quit()
[void][System.Runtime.Interopservices.Marshal]::ReleaseComObject($Range)
[void][System.Runtime.Interopservices.Marshal]::ReleaseComObject($UsedRange)
[void][System.Runtime.Interopservices.Marshal]::ReleaseComObject($Worksheet)
[void][System.Runtime.Interopservices.Marshal]::ReleaseComObject($Workbook)
[void][System.Runtime.Interopservices.Marshal]::ReleaseComObject($Excel)
[GC]::Collect()

如果您只取出一个 ReleaseComObject 语句,Excel 进程将保持打开状态。在我的代码中,我首先发布范围、表格等所有内容,然后执行工作表、工作簿,最后是 Excel 应用程序本身。然后因为这似乎只有 90% 的时间有效,所以我在最后添加了垃圾收集命令,最后有了一个似乎每次都有效的解决方案,而无需终止进程。

注意:我的系统是带有 PowerShell v5 和 Office 2013 的 Windows 8.1。

【讨论】:

  • +为 [GC]::Collect() 行竖起大拇指。
  • ReleaseComObject 方法将在对象完全释放时返回 0。只要我将每个都包装成一个循环,这对我来说非常有效。
  • 我认为垃圾回收是关键,我只需要释放 Excel 应用程序(在您的示例中为 $Excel)并调用 [GC]::Collect() 即可停止该过程。
【解决方案2】:

下面是一个简单的例子。对于更复杂的过程,它可能需要一些额外的代码。

function _FullQuit {
    while ( $this.Workbooks.Count -gt 0 ) {
        $this.Workbooks.Item(1).Close()
    }
    $this.Quit()
    [System.Runtime.InteropServices.Marshal]::ReleaseComObject($this)
    [System.GC]::Collect()
    [System.GC]::WaitForPendingFinalizers()
}

function New-Excel {
$object = New-Object -ComObject "Excel.Application"
$object | Add-Member -MemberType ScriptMethod -Name FullQuit -Value {_FullQuit}
$object
}

$xl = New-Excel

$wb1 = $xl.Workbooks.Open("C:\Data1.csv")
$wb2 = $xl.Workbooks.Open("C:\Data2.csv")

$xl.FullQuit()

【讨论】:

    【解决方案3】:

    上述解决方案对我不起作用,按顺序我需要最后一步是.saveas(file.xlsx),这意味着剩余的未保存文档仍会弹出一个 GUI 界面,需要用户交互来保存/不保存/取消。

    我最终得到了以下内容,虽然这很粗糙,但对我有用。

    在脚本的开头:

    $existingExcel = @() 
    Get-Process Excel | % {$existingExcel += $_.ID }
    function Stop-Excel
        {
        Get-process EXCEL | % {IF($_.ID -notmatch $existingExcel){Stop-Process -ID $_.ID}}
        }
    

    在脚本的结尾

    Stop-Excel
    

    这样做的好处是可以完全销毁任何挥之不去的 excel 进程,而不会终止运行该脚本的用户可能正在使用的任何其他实时 excel 进程。

    缺点是下次加载 excel 时会出现崩溃的 excel 文档恢复对话框。

    【讨论】:

      【解决方案4】:

      另一种答案:

      我知道我的回复迟了,但是,请考虑以下方法来完成此操作。

      1. 一开始,获取excel.exe的任何/所有实例的PID,使用命令:

        tasklist /FI "imagename eq excel.exe"
        
      2. 运行生成excel.exe 实例的脚本部分。使用步骤 1 中的命令识别并保存新生成的 excel.exe 实例的 PID(例如 wxyz),这将不同于步骤 1 中保存的已有 PID。

      3. 在脚本结束时,使用命令关闭特定实例(使用步骤 2 中保存的 PID):

        TASKKILL /f /PID wxyz
        

      其中wxyz 是在步骤 2 中保存的 4 位 PID。

      【讨论】:

        【解决方案5】:
        1. 创建 Excel 应用程序。
        2. 使其可见
        3. 获取应用程序的进程 ID。
        4. 隐藏 Excel 应用程序。
        5. 停止处理我的进程 ID。 示例代码

          # Create Excel Application
          $excel = New-Object -comobject Excel.Application
          
          # Make it visiable
          $excel.Visible = $true
          
          # Get Windows handle of the application
          $excelWinHwnd = $excel.Hwnd
          
          # Get Process Id of the application
          $process = Get-Process Excel | Where-Object {$_.MainWindowHandle -eq $excelWinHwnd}
          $excelProcessId = $process.Id
          
          # Hide the application : Run In background 
          $excel.Visible = $false
          
          # Kill/Stop the process by id
          Stop-Process -Id $excelProcessId
          

        【讨论】:

          【解决方案6】:

          如果没有其他方法可靠运行,请尝试在退出前将 Excel 应用程序的 DisplayAlerts 设置为 tru。

          $xlapp.DisplayAlerts=$true
          $xlapp.quit()
          [System.Runtime.Interopservices.Marshal]::ReleaseComObject([System.__ComObject]$xlapp)
          $xlapp=$null
          remove-variable xlapp
          [GC]::Collect()
          

          这总是对我有用。

          【讨论】:

            猜你喜欢
            • 2021-10-26
            • 1970-01-01
            • 2013-05-14
            • 1970-01-01
            • 2013-07-28
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2023-04-04
            相关资源
            最近更新 更多