然后查找保存并关闭所有打开的Excel文档

时间:2016-02-26 21:54:19

标签: excel powershell vbscript

我需要帮助创建一个PowerShell脚本,它将找到所有打开的Excel文档,然后首先是文件是只读取文件而不保存,如果进行了更改则取消提示,如果文档不是只读,则需要保存并关闭它们。此脚本将从计划任务运行。

我之所以这样做是因为我需要一种在共享生产区域计算机上保存和关闭文档的自动方式。我们的制造车间区域有大约80台计算机,这些计算机设置为信息亭,供我们的生产工人访问完成工作所需的程序和文件。这些计算机与组策略紧密锁定,只能访问特定的应用程序。其中一个应用程序在其中链接了Excel文档,供生产工作人员打开和编辑(他们不会创建文档)。但问题是这些文件经常被打开的时间太长,很多时候它们也没有被保存。所以我的解决方案是创建一个计划任务(在我们的生产班次之间运行)来保存和关闭所有打开的Excel文档。

我已经使用下面的PowerShell脚本来关闭其他应用程序,如果没有进行任何更改,它也适用于Excel ...但显然如果有更改,您将得到保存提示,所以我需要一种方法来保存Excel文档在此运行之前首先(或者如果只读取抑制提示并且只是关闭)。

Get-Process EXCEL | Foreach-Object { $_.CloseMainWindow() | Out-Null }

如果使用PowerShell无法做到这一点,我打算用VBScript做这件事。

2 个答案:

答案 0 :(得分:3)

@bgalea有正确的想法,但答案非常不完整。首先,重新附加到COM对象只能在创建对象的用户的上下文中工作,因此您必须创建作为特定用户运行的计划任务或注销脚本(自动在用户的运行中运行)注销时的上下文)。

由于您显然希望定期运行此操作,因此注销脚本可能不是一个选项,因此您可能希望使用登录脚本为每个用户创建相应的计划任务,例如:像这样:

schtasks /query /tn "Excel Cleanup" >nul 2>&1
if %errorlevel% neq 0 schtasks /create /tn "Excel Cleanup" /sc DAILY /tr "wscript.exe \"C:\path\to\your.vbs\"" /f

这些任务运行的VBScript可能看起来像这样:

On Error Resume Next
'attach to running Excel instance
Set xl = GetObject(, "Excel.Application")
If Err Then
  If Err.Number = 429 Then
    'Excel not running (nothing to do)
    WScript.Quit 0
  Else
    'unexpected error: log and terminate
    CreateObject("WScript.Shell").LogEvent 1, Err.Description & _
      " (0x" & Hex(Err.Number) & ")"
    WScript.Quit 1
  End If
End If
On Error Goto 0

xl.DisplayAlerts = False  'prevent Save method from asking for confirmation
                          'about overwriting existing files (when saving new
                          'workbooks)
                          'WARNING: this might cause data loss!

For Each wb In xl.Workbooks
  wb.Save
  wb.Close False
Next

xl.Quit
Set xl = Nothing

如果您需要PowerShell脚本而不是VBScript,则需要使用GetActiveObject()方法附加到正在运行的Excel实例。

try {
  # attach to running Excel instance
  $xl = [Runtime.InteropServices.Marshal]::GetActiveObject('Excel.Application')
} catch {
  if ($_.Exception.HResult -eq -2146233087) {
    # Excel not running (nothing to do)
    exit 0
  } else {
    # unexpected error: log and terminate
    Write-EventLog -LogName Application `
      -Source 'Application Error' `
      -EventId 500 `
      -EntryType Error `
      -Message $_.Exception.Message
    exit 1
  }
}

$xl.DisplayAlerts = $false  # prevent Save() method from asking for confirmation
                            # about overwriting existing files (when saving new
                            # workbooks)
                            # WARNING: this might cause data loss!

foreach ($wb in $xl.Workbooks) {
  $wb.Save()
  $wb.Close($false)
}

$xl.Quit()
[void][Runtime.InteropServices.Marshal]::ReleaseComObject($xl)
[GC]::Collect()
[GC]::WaitForPendingFinalizers()

根据Microsoft's documentationGetObject()方法也可以正常工作:

[void][Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic')
$xl = [Microsoft.VisualBasic.Interaction]::GetObject($null, 'Excel.Application')

但是当我尝试它时,我最终得到了一个额外的(隐藏的)Excel实例,而不是附加到已经运行的实例上。

注意:如果由于某种原因用户启动了多个Excel实例,则需要为每个实例运行一次代码。

答案 1 :(得分:0)

Set xlBook = GetObject("C:\Users\David Candy\Documents\Super.xls", "Excel.Application")
For each wsheet in xlbook.worksheets
    msgbox wsheet.name
next

VBScript,JScript,PS,VBA都可以做到。以上是VBScript(因此也是VBA)。该技术不是COM语言。

修改

如上所述使用app对象在excel中不起作用。

打开了一个excel文件。

EXCEL.EXE                    13560 Console                    1     19,228 K Running         DESKTOP-UCDGI39\David Candy
                             0:00:00 Microsoft Excel - Super.xls

使用Excel.Application运行GetObject

EXCEL.EXE                    13560 Console                    1     19,236 K Running         DESKTOP-UCDGI39\David Candy
                             0:00:00 Microsoft Excel - Super.xls
EXCEL.EXE                    15124 Console                    1     12,772 K Running         DESKTOP-UCDGI39\David Candy
                             0:00:00 N/A

注意

  • 单独处理
  • 未启动COM开关(/ a或/自动化和 / embedding)
  • 无法访问其他excel

这是excel的其他错误行为的补充,它在释放引用时不会退出内存。