【问题标题】:How to Open; SaveAs; then Close an Excel 2013 (macro-enabled) workbook from PowerShell4如何打开;另存为;然后从 PowerShell4 关闭 Excel 2013(启用宏)工作簿
【发布时间】:2016-05-07 08:14:23
【问题描述】:

对上述 Com 操作进行搜索会产生可追溯到 09 年甚至更早的链接。也许它没有改变,但我发现自己遇到了“它正在被另一个进程使用”的错误。 - 即使我的桌面上没有打开 Excel 应用程序。我必须重新启动才能恢复。

明确一点 - 我正在尝试打开现有文件;立即SaveAs()(非常有效),添加一张表,Save(); Close() - 然后,重要的是,重复该循环。实际上,我在执行上述“Open Master;”的循环中创建了几十个新工作表; SaveAs();编辑东西;节省;关闭;

从我看到的示例中,这不是 PowerShell 的典型工作流程。粘贴在最底部的是我的临时脚本 - 非常粗糙和不完整,但事情正在打开他们需要打开的内容并且添加工作表也可以 - 直到我知道我有正确的方法来干净地关闭东西我不担心迭代.

我发现了几个解决关闭的例子:

From http://theolddogscriptingblog.wordpress.com/2012/06/07/get-rid-of-the-excel-com-object-once-and-for-all/

$x = New-Object -com Excel.Application
$x.Visible = $True
Start-Sleep 5
$x.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($x)
Remove-Variable x

And from http://social.technet.microsoft.com/Forums/windowsserver/en-US/24e57b61-e792-40c1-8aff-b0a8205f48ab/updated-opened-excel-using-powershell?forum=winserverpowershell

Set-ItemProperty $path -name IsReadOnly -value $false
$Excel.ActiveWorkBook.Save()
$openfile.Close() 
$openfile = $null 
$Excel.Quit() 
$Excel = $null 
[GC]::Collect() 

function MakeNewBook($theWeek, $AffID){
    $ExcelFile = "C:\csv\InvoiceTemplate.xlsm"
    $Excel = New-Object -Com Excel.Application
    $Excel.Visible = $True  
    $Workbook = $Excel.Workbooks.Open($ExcelFile)
    $theWeek = $theWeek  -replace "C:\\csv\\", ""
    $theWeek = $theWeek  -replace "\.csv", ""

    $theWeek = "c:\csv\Invoices\" +$AffID +"_" + $theWeek + ".xlsm"

    $SummaryWorksheet = $Workbook.worksheets.Item(1)

    $Workbook.SaveAs($theWeek)
    return $Excel
}

function MakeNewSheet($myBook, $ClassID){
    $SheetName = "w"+$ClassID
    #$Excel = New-Object -Com Excel.Application
    #$Excel.Visible = $True  
    $wSheet = $myBook.WorkSheets.Add()

}

function SaveSheet ($myExcel)
{
    #$WorkBook.EntireColumn.AutoFit()
    #Set-ItemProperty $path -name IsReadOnly -value $false
    $myExcel.ActiveWorkBook.Save()

    $openfile= $myExcel.ActiveWorkBook
    $openfile.Close() 
    $openfile = $null 
    $myExcel.Quit() 
    $myExcel = $null 
    [System.Runtime.Interopservices.Marshal]::ReleaseComObject($myExcel)
    Remove-Variable $myExcel
    [GC]::Collect() 

}

$theWeek = "C:\csv\wkStart2013-11-04.csv"
$x = Import-Csv $theWeek

foreach ($xLine in $x){
    if ($x[0]){
        $AffID = $x[1].idAffiliate
        $myExcel = MakeNewBook $theWeek  $x[1].idAffiliate

        $ClassID = $x[1].idClass
        MakeNewSheet $myExcel $ClassID
        continue
    }
    SaveSheet($myExcel)
    $AffID = $_.$AffID
    $wID = $xLine.idClass
    #MakeNewSheet($wID)
    Echo "$wID"

}

【问题讨论】:

    标签: powershell excel-interop


    【解决方案1】:

    作为我自己解决这个问题后的后续行动。我的解决方案是围绕 Ron Thompson 的评论减去函数调用:

    # collect excel process ids before and after new excel background process is started
    $priorExcelProcesses = Get-Process -name "*Excel*" | % { $_.Id }
    $Excel = New-Object -ComObject Excel.Application
    $postExcelProcesses = Get-Process -name "*Excel*" | % { $_.Id }
    
    # your code here (probably looping through the Excel document in some way
    
    # try to gently quit
    $Excel.Quit()
    [System.Runtime.Interopservices.Marshal]::ReleaseComObject($Excel)
    
    # otherwise allow stop-process to clean up
    $postExcelProcesses | ? { $priorExcelProcesses -eq $null -or $priorExcelProcesses -notcontains $_ } | % { Stop-Process -Id $_ }
    

    【讨论】:

      【解决方案2】:

      我的经验是 Quit 方法效果不佳,尤其是在循环时。当您收到错误时,不要重新启动,而是打开任务管理器并查看“进程”选项卡。我敢打赌你会看到 Excel 仍然打开——甚至可能有多个实例。我通过使用 Get-Process 查找 Excel 的所有实例并将它们传送到 Stop-Process 解决了这个问题。似乎没有必要这样做,但它对我有用。

      【讨论】:

      • 我已经准备好告诉你我已经检查了正在运行的进程 - 但是通过命令行运行显示 Excel 进程未通过任务管理器显示。谢谢!
      • 在其他用户打开 Excel 的服务器上使用 Stop-Process -Name EXCEL 会导致 PowerShell 终止这些 Excel 实例。相反,请使用While ([System.Runtime.Interopservices.Marshal]::ReleaseComObject($Excel)) {'cleanup Excel'}[gc]::collect() | Out-Null;[gc]::WaitForPendingFinalizers() | Out-Null
      • 我解决这个问题的方法是用function startExcel() { $beforeExcel = @(); $afterExcel = @(); Get-Process -name "*Excel*" | %{$beforeExcel += $_.Id}; $excel = New-Object -ComObject Excel.Application; Get-Process -name "*Excel*" | %{$afterExcel += $_.Id}; }function murderExcel() {foreach($id in $afterExcel) { if($beforeExcel -notcontains $id) { Stop-Process -Id $id} } }收集所有以前运行的Excel实例
      • 只要上面注释的代码没有在收集 PID 之间的毫秒内启动 Excel 实例,通常可以安全地杀死其余的代码。我还没有让我失望。 /诺克森伍德。
      • 大锤方法:taskkill /F /IM EXCEL.EXE 但是,我是 Ron Thompson 方法的粉丝。它更干净,不会杀死在运行脚本之前打开的 excel 进程。
      【解决方案3】:

      您不必跟踪进程并终止它们。

      我的经验是,要正确和完全关闭 Excel(包括循环),您还需要释放 COM 引用。在我自己的测试中发现,删除 Excel 的变量还可以确保不存在剩余的引用,这将使 Excel.exe 保持打开状态(就像您在 ISE 中进行调试一样)。

      如果不执行上述操作,如果您在任务管理器中查看,您可能会看到 Excel 仍在运行...在某些情况下,有很多副本。

      这与 COM 对象如何包装在“运行时可调用包装器”中有关。

      这是应该使用的骨架代码:

      $excel = New-Object -ComObject Excel.Application
      $excel.Visible = $true
      $workbook = $excel.Workbooks.Add()
      # or $workbook = $excel.Workbooks.Open($xlsxPath)
      
      # do work with Excel...
      
      $workbook.SaveAs($xlsxPath)
      $excel.Quit()
      [System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel)
      # no $ needed on variable name in Remove-Variable call
      Remove-Variable excel
      

      【讨论】:

        【解决方案4】:

        试试这个

        $filePath = "E:\TSMBackup\TSMDATAPull\ODCLXTSM01_VM.xlsx"
        
        $excelObj = New-Object -ComObject Excel.Application
        
        $excelObj.Visible = $true
        
        $workBook = $excelObj.Workbooks.Open($filePath)
        
        $workSheet = $workBook.Sheets.Item("Sheet1")
        
        $workSheet.Select()
        
        $workBook.RefreshAll()
        
        $workBook.Save()
        
        $excelObj.Quit()
        

        【讨论】:

        • 在答案中编写代码时,使用代码格式化选项很有帮助。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-10-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-02-26
        相关资源
        最近更新 更多