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

标签 excel powershell vbscript

我需要帮助创建一个 PowerShell 脚本来查找所有打开的 Excel 文档,然后首先如果文档是只读的则关闭文档而不保存并在进行更改时抑制提示,或者其次如果文档不是只读的然后保存并关闭它们。该脚本将从计划任务运行。

我想要这样做的原因是因为我需要一种在我们的共享生产区域计算机上保存和关闭文档的自动化方法。我们的制造车间有大约 80 台计算机,这些计算机像信息亭一样设置,供我们的生产 worker 访问完成工作所需的程序和文档。这些计算机被组策略严格锁定,只能访问特定的应用程序。其中一款应用程序链接了 Excel 文档,供生产 worker 打开和编辑(他们从不创建文档)。但问题是这些文档经常打开太久,而且很多时候也没有保存。因此,我的解决方案是创建一个计划任务(在我们的生产类次之间运行)来保存并关闭所有打开的 Excel 文档。

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

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

如果使用 PowerShell 无法完成此操作,我愿意使用 VBScript 来完成此操作。

最佳答案

@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 documentation GetObject()方法应该也有效:

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

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

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

关于excel - 查找然后保存并关闭所有打开的 Excel 文档,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35662470/

相关文章:

excel - 将数据从多个 Excel 文件复制到 Masterfile

windows - 使用向上箭头访问 PowerShell 历史记录

powershell - 使用 Powershell 中的另一个列表在列表中搜索部分匹配项

powershell - 使用设置的内容创建多个输出文件

javascript - exceljs - 将填充应用于单元格范围

excel - 如何更改地址属性的 'Column' 部分

excel - 使用apache POI读取单元格样式以及数据-Excel表格

email - 发送电子邮件时出现 VBScript 错误 '80040211'

javascript - Onclick命令不会将ID发送到文本框,onclick需要 "escaping"才能被识别为代码

sql-server - ADODB.记录集 : Operation is not allowed when the object is closed