【问题标题】:Find then save and close all open Excel documents查找并保存并关闭所有打开的 Excel 文档
【发布时间】:2016-06-10 07:16:45
【问题描述】:

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

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

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

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

如果这不能用 PowerShell 完成,我愿意用 VBScript 代替。

【问题讨论】:

  • 你可以用你自己的“主”工作簿做this with vba吗?
  • 您是说使用 vba 脚本创建一个“主”工作簿以保存和关闭所有其他工作簿...然后在计划任务中使用 PowerShell 或 vbscript 启动该“主”工作簿,然后保存并关闭所有其他工作簿?
  • 您可以通过其他方式启动主服务器,但 vba 也支持计时器。显然,主盒需要 24/7 全天候运行。
  • 这必须是我可以使用 Windows 计划任务启动的东西。我将使用“主”工作簿尝试这种方法。但理想情况下,我希望得到回复,说这可以完全使用 PowerShell 完成。
  • 可以的,把vbscript贴在下面。任何和所有支持 COM 的语言都可以连接到该文件。您需要 GetObject 到文件(您应该知道)。

标签: excel powershell vbscript


【解决方案1】:

@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 实例,您需要为每个实例运行一次代码。

【讨论】:

  • 测试getobject后,只使用文件名不连接类名。
  • @bgalea 我使用GetObject 直接附加到应用程序对象,而不是特定的工作簿,因此代码不需要知道文件名。
  • @AnsgarWiechers 的答案正是我想要的。 Ansgar 的这个脚本不需要知道文件名,这是我需要的,因为在 100 个不同的目录中有超过 1000 个不同的 Excel 文件可以打开。非常感谢!
  • 除非连接到应用对象会创建一个新进程。我重复我的cmets。你的代码不起作用。请参阅我的编辑。 @Ansgar Wiechers
  • @bgalea 我发布的代码已经过测试并且可以工作。这不是我编的。
【解决方案2】:
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 或 /automation 和 /嵌入)
  • 无法访问其他 excel

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

【讨论】:

    猜你喜欢
    • 2013-10-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-30
    相关资源
    最近更新 更多