启动多个Excel实例后,如何获取所有实例的应用程序对象?

启动多个Excel实例后,如何获取所有实例的应用程序对象?,excel,vba,Excel,Vba,我想用类似的东西 GetObject(,“Excel.Application”)获取我创建的应用程序 我调用CreateObject(“Excel.Application”)来创建Excel实例。稍后,如果VBA项目由于调试和编码而重置,则应用程序对象变量将丢失,但Excel实例将在后台运行。有点内存泄漏的情况 我想重新附加到重用(首选方式)或关闭它们。创建一个对象数组,并将新创建的Excel.Application存储在数组中。这样,您可以在需要时引用它们。让我们举一个很快的例子: 在模块中:

我想用类似的东西
GetObject(,“Excel.Application”)
获取我创建的应用程序

我调用
CreateObject(“Excel.Application”)
来创建Excel实例。稍后,如果VBA项目由于调试和编码而重置,则应用程序对象变量将丢失,但Excel实例将在后台运行。有点内存泄漏的情况


我想重新附加到重用(首选方式)或关闭它们。

创建一个对象数组,并将新创建的Excel.Application存储在数组中。这样,您可以在需要时引用它们。让我们举一个很快的例子:

在模块中:

Dim ExcelApp(2) As Object

Sub Test()
    Set ExcelApp(1) = CreateObject("Excel.Application")
    ExcelApp(1).Visible = True

    Set ExcelApp(2) = CreateObject("Excel.Application")
    ExcelApp(2).Visible = True
End Sub

Sub AnotherTest()
    ExcelApp(1).Quit
    ExcelApp(2).Quit
End Sub
运行Test()宏,您将看到弹出两个Excel应用程序。然后运行另一个test(),Excel应用程序将退出。完成后,甚至可以将数组设置为“无”


您可以使用上发布的脚本处理运行Excel应用程序。这会让你到达你想去的地方。

这可以实现你想要的。 确定Excel实例是否打开:

Dim xlApp As Excel.Application
Set xlApp = GetObject(, "Excel.Application")
如果实例正在运行,您可以使用
xlApp
对象访问它。如果一个实例没有运行,您将得到一个运行时错误(您可能需要/想要一个错误处理程序)。函数获取已加载的Excel的第一个实例。你可以用它来完成你的工作,如果你想找到其他人,你可以关闭它,然后再次尝试
GetObject
来获取下一个,以此类推。 因此,您将实现您的确定但第二个首选目标 (摘自)


为了实现您的首选目标,我认为这向您展示了如何实现。

要列出Excel的运行实例:

#If VBA7 Then
  Private Declare PtrSafe Function AccessibleObjectFromWindow Lib "oleacc" ( _
    ByVal hwnd As LongPtr, ByVal dwId As Long, riid As Any, ppvObject As Object) As Long

  Private Declare PtrSafe Function FindWindowExA Lib "user32" ( _
    ByVal hwndParent As LongPtr, ByVal hwndChildAfter As LongPtr, _
    ByVal lpszClass As String, ByVal lpszWindow As String) As LongPtr
#Else
  Private Declare Function AccessibleObjectFromWindow Lib "oleacc" ( _
    ByVal hwnd As Long, ByVal dwId As Long, riid As Any, ppvObject As Object) As Long

  Private Declare Function FindWindowExA Lib "user32" ( _
    ByVal hwndParent As Long, ByVal hwndChildAfter As Long, _
    ByVal lpszClass As String, ByVal lpszWindow As String) As Long
#End If

Sub Test()
  Dim xl As Application
  For Each xl In GetExcelInstances()
    Debug.Print "Handle: " & xl.ActiveWorkbook.FullName
  Next
End Sub

Public Function GetExcelInstances() As Collection
  Dim guid&(0 To 3), acc As Object, hwnd, hwnd2, hwnd3
  guid(0) = &H20400
  guid(1) = &H0
  guid(2) = &HC0
  guid(3) = &H46000000

  Set GetExcelInstances = New Collection
  Do
    hwnd = FindWindowExA(0, hwnd, "XLMAIN", vbNullString)
    If hwnd = 0 Then Exit Do
    hwnd2 = FindWindowExA(hwnd, 0, "XLDESK", vbNullString)
    hwnd3 = FindWindowExA(hwnd2, 0, "EXCEL7", vbNullString)
    If AccessibleObjectFromWindow(hwnd3, &HFFFFFFF0, guid(0), acc) = 0 Then
      GetExcelInstances.Add acc.Application
    End If
  Loop
End Function

这最好是对Florent B.非常有用的函数(返回一组打开的Excel实例)的评论,但我并没有足够的声誉来添加评论。在我的测试中,集合包含相同Excel实例的“重复”,即
GetExcelInstances()。计数
大于它应该的值。解决方法是在以下版本中使用
AlreadyThere
变量

Private Function GetExcelInstances() As Collection
    Dim guid&(0 To 3), acc As Object, hwnd, hwnd2, hwnd3
    guid(0) = &H20400
    guid(1) = &H0
    guid(2) = &HC0
    guid(3) = &H46000000
    Dim AlreadyThere As Boolean
    Dim xl As Application
    Set GetExcelInstances = New Collection
    Do
        hwnd = FindWindowExA(0, hwnd, "XLMAIN", vbNullString)
        If hwnd = 0 Then Exit Do
        hwnd2 = FindWindowExA(hwnd, 0, "XLDESK", vbNullString)
        hwnd3 = FindWindowExA(hwnd2, 0, "EXCEL7", vbNullString)
        If AccessibleObjectFromWindow(hwnd3, &HFFFFFFF0, guid(0), acc) = 0 Then
            AlreadyThere = False
            For Each xl In GetExcelInstances
                If xl Is acc.Application Then
                    AlreadyThere = True
                    Exit For
                End If
            Next
            If Not AlreadyThere Then
                GetExcelInstances.Add acc.Application
            End If
        End If
    Loop
End Function

@PGS62/@Philip Swannell有返回收藏的正确答案;我可以迭代所有实例;正如@M1chael所评论的那样,这是一部精彩的作品


让我们不要混淆应用程序对象和工作簿对象。。。属于 当然,可以编写一个嵌套循环,在 每个应用程序对象的工作簿集合

这是实现的嵌套循环,功能齐全:

Sub Test2XL()
  Dim xl As Excel.Application
  Dim i As Integer
  For Each xl In GetExcelInstances()
    Debug.Print "Handle: " & xl.Application.hwnd
    Debug.Print "# workbooks: " & xl.Application.Workbooks.Count
    For i = 1 To xl.Application.Workbooks.Count
        Debug.Print "Workbook: " & xl.Application.Workbooks(i).Name
        Debug.Print "Workbook path: " & xl.Application.Workbooks(i).path
    Next i
  Next
  Set xl = Nothing
End Sub
对于Word实例,嵌套循环:

Sub Test2Wd()
  Dim wd As Word.Application
  Dim i As Integer
  For Each wd In GetWordInstancesCol()
    Debug.Print "Version: " & wd.System.Version
    Debug.Print "# Documents: " & wd.Application.Documents.Count
    For i = 1 To wd.Application.Documents.Count
        Debug.Print "Document: " & wd.Application.Documents(i).Name
        Debug.Print "Document path: " & wd.Application.Documents(i).path
    Next i
  Next
  Set wd = Nothing
End Sub

对于Word,您必须使用本文末尾解释的内容

我使用以下内容检查两个实例是否正在运行,并显示一条消息。可以将其更改为关闭其他实例。。。这可能会有帮助。。。我需要返回特定实例的代码,并返回类似于GetObject(,“Excel.Application”)。。。但我认为这是不可能的

 If checkIfExcelRunningMoreThanOneInstance() Then Exit Function
在模块中(某些声明可能用于其他代码):

我发现:

Dim xlApp As Excel.Application
Set xlApp = GetObject("ExampleBook.xlsx").Application

如果知道Excel实例中当前活动的工作表的名称,则获取对象。我想这可以从应用程序标题中使用第一位代码得到。在我的应用程序中,我确实知道文件名。

每次需要Excel应用程序对象时,都应该使用此代码。这样,您的代码将只使用一个应用程序对象或使用预先存在的对象。如果用户启动了多个应用程序,则最终只能使用多个应用程序。这既是打开Excel的代码,也是附加和重用的代码,就像您想要的那样

Public Function GetExcelApplication() As Object
    On Error GoTo openExcel
    
    Set GetExcelApplication = GetObject(, "Excel.Application")
    Exit Function
    
openExcel:
    If Err.Number = 429 Then
        Set GetExcelApplication = CreateObject("Excel.Application")
    Else
        Debug.Print "Unhandled exception: " & Err.Number & " " & Err.Description
    End If
End Function
如果要关闭多个实例,则需要在循环中调用
GetObject
,然后调用
。close
,直到抛出错误429


详细信息可在此

中找到,而我将它们存储在变量中。但有时我需要更改VBA程序的其他部分。VBA项目有时会被重置,所有变量都会丢失。但启动的Excel实例仍在后台运行。这很棘手。GetObject和finding窗口句柄是两个选项。sancho.s在该答案中有一些链接,您可以使用它们。最好从一开始就避免这个问题。看到了吗?你觉得有什么有用的东西吗?请根据您的发现发布反馈、投票和/或接受。以下4个答案均不能正确回答问题。最接近的是Florent's,它列出了工作簿(即使在多个实例中),但没有确定是否实际有多个实例在运行,也不允许用户为每个实例获取
应用程序
对象(至少据我所知)。我还没有找到一种方法来实际列出实例的数量。澄清一下,一个实例不仅仅是“另一个工作簿”;它实际上是在一个单独的内存段中运行进程,等等。例如,Excel的新实例可以由打开,也可以通过启动Excel打开,或者可以从Microsoft打开。@ashleedawg如果我理解,可以使用xl.ActiveWorkbook.Application,另一个选项是Florent B从代码返回的完整路径文件字符串。该字符串可用于使用GetObject函数访问应用程序,如本文所建议的。有用的东西,谢谢:)应该被POVery cool+1接受,但澄清一下,这并不是列出Excel实例,而是列出Excel窗口。例如,如果我有两个Excel实例,第一个打开了2个工作簿,第二个打开了1个工作簿,这将列出3个窗口,[我认为]无法区分哪个是哪个实例。@ashleedawg,这个示例列出了所有窗口的所有实例。如果一个实例有多个窗口,则最终会出现重复的实例。如果要列出所有打开的工作簿,请阅读
acc.Parent
Dim wb As WorkBook
Set wb=acc.Parent<
Public Function GetExcelApplication() As Object
    On Error GoTo openExcel
    
    Set GetExcelApplication = GetObject(, "Excel.Application")
    Exit Function
    
openExcel:
    If Err.Number = 429 Then
        Set GetExcelApplication = CreateObject("Excel.Application")
    Else
        Debug.Print "Unhandled exception: " & Err.Number & " " & Err.Description
    End If
End Function