programing

여러 Excel 인스턴스를 시작했는데, 이 모든 인스턴스에 대한 애플리케이션 개체를 어떻게 얻을 수 있습니까?

bestprogram 2023. 8. 25. 23:50

여러 Excel 인스턴스를 시작했는데, 이 모든 인스턴스에 대한 애플리케이션 개체를 어떻게 얻을 수 있습니까?

▁something다싶니습▁i와 비슷한 것을 하고 싶습니다.GetObject(,"Excel.Application")내가 만든 응용프로그램을 다시 가져옵니다.

부를게요CreateObject("Excel.Application")Excel 인스턴스를 만듭니다.나중에 VBA 프로젝트가 재설정되면 디버깅 및 코딩으로 인해 응용 프로그램 개체 변수가 손실되지만 Excel 인스턴스는 백그라운드에서 실행됩니다.일종의 메모리 누수 상황입니다.

다시 사용하거나 닫으려면 다시 연결합니다(기본 방법).

실행 중인 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

플로랑 B.의 매우 유용한 기능 중 하나로 오픈 엑셀 인스턴스 컬렉션을 반환하는 기능에 대한 의견을 제시하는 것이 가장 좋겠지만, 저는 의견을 추가하기에 충분한 평판을 가지고 있지 않습니다.테스트에서 컬렉션에는 동일한 Excel 인스턴스의 "반복"이 포함되어 있었습니다.GetExcelInstances().Count원래보다 더 컸습니다.이에 대한 해결책은 다음을 사용하는 것입니다.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")와 유사한 용도로 사용하기 위해 코드가 필요합니다.응용 프로그램")...저는 그것이 가능하다고 생각하지 않습니다.

 If checkIfExcelRunningMoreThanOneInstance() Then Exit Function

모듈에서 (일부 선언은 다른 코드에 사용될 수 있음):

Const MaxNumberOfWindows = 10

Const HWND_TOPMOST = -1
Const SWP_NOSIZE = &H1
Const SWP_NOMOVE = &H2

 Type RECT
        Left As Long
        Top As Long
        Right As Long
        Bottom As Long
End Type

Public Declare Function ShowWindow Lib "user32" (ByVal hwnd As Long, ByVal nCmdShow As Long) As Long
Global ret As Integer
Declare Function GetWindow Lib "user32" (ByVal hwnd As Long, ByVal wCmd As Long) As Long
Private Declare Function SetWindowPos Lib "user32" (ByVal hwnd As Long, ByVal hWndInsertAfter As Long, ByVal x As Long, ByVal y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long
Public Declare Function GetWindowRect Lib "user32" (ByVal hwnd As Long, lpRect As RECT) As Long
Declare Function GetKeyNameText Lib "user32" Alias "GetKeyNameTextA" (ByVal lParam As Long, ByVal lpBuffer As String, ByVal nSize As Long) As Long
Declare Function MapVirtualKey Lib "user32" Alias "MapVirtualKeyA" (ByVal wCode As Long, ByVal wMapType As Long) As Long
Declare Function GetDesktopWindow Lib "user32" () As Long
Public Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hwnd As Long, ByVal lpString As String, ByVal cch As Long) As Long
Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long) As Long
Public Declare Function GetParent Lib "user32" (ByVal hwnd As Long) As Long
      Private Declare Function FindWindow Lib "user32" _
         Alias "FindWindowA" _
         (ByVal lpClassName As String, _
         ByVal lpWindowName As String) As Long

     Private Const VK_CAPITAL = &H14
Private Declare Function GetKeyState Lib "user32" _
    (ByVal nVirtKey As Long) As Integer

Private Declare Function OpenProcess Lib "kernel32" ( _
    ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long

Private Declare Function CloseHandle Lib "kernel32" ( _
    ByVal hObject As Long) As Long

Private Declare Function EnumProcesses Lib "PSAPI.DLL" ( _
   lpidProcess As Long, ByVal cb As Long, cbNeeded As Long) As Long

Private Declare Function EnumProcessModules Lib "PSAPI.DLL" ( _
    ByVal hProcess As Long, lphModule As Long, ByVal cb As Long, lpcbNeeded As Long) As Long

Private Declare Function GetModuleBaseName Lib "PSAPI.DLL" Alias "GetModuleBaseNameA" ( _
    ByVal hProcess As Long, ByVal hModule As Long, ByVal lpFileName As String, ByVal nSize As Long) As Long

Private Const PROCESS_VM_READ = &H10
Private Const PROCESS_QUERY_INFORMATION = &H400

Global ExcelWindowName$   'Used to switch back to later


Function checkIfExcelRunningMoreThanOneInstance()
    'Check instance it is 1, else ask user to reboot excel, return TRUE to abort
    ExcelWindowName = excel.Application.Caption  'Used to switch back to window later

    If countProcessRunning("excel.exe") > 1 Then
        Dim t$
        t = "Two copies of 'Excel.exe' are running, which may stop in cell searching from working!" & vbCrLf & vbCrLf & "Please close all copies of Excel." & vbCrLf & _
        "   (1 Then press Alt+Ctrl+Del to go to task manager." & vbCrLf & _
        "   (2 Search the processes running to find 'Excel.exe'" & vbCrLf & _
        "   (3 Select it and press [End Task] button." & vbCrLf & _
        "   (4 Then reopen and use PostTrans"
        MsgBox t, vbCritical, ApplicationName
    End If
End Function

   Private Function countProcessRunning(ByVal sProcess As String) As Long
    Const MAX_PATH As Long = 260
    Dim lProcesses() As Long, lModules() As Long, N As Long, lRet As Long, hProcess As Long
    Dim sName As String
    countProcessRunning = 0
    sProcess = UCase$(sProcess)

    ReDim lProcesses(1023) As Long
    If EnumProcesses(lProcesses(0), 1024 * 4, lRet) Then
        For N = 0 To (lRet \ 4) - 1
            hProcess = OpenProcess(PROCESS_QUERY_INFORMATION Or PROCESS_VM_READ, 0, lProcesses(N))
            If hProcess Then
                ReDim lModules(1023)
                If EnumProcessModules(hProcess, lModules(0), 1024 * 4, lRet) Then
                    sName = String$(MAX_PATH, vbNullChar)
                    GetModuleBaseName hProcess, lModules(0), sName, MAX_PATH
                    sName = Left$(sName, InStr(sName, vbNullChar) - 1)
                    If Len(sName) = Len(sProcess) Then
                        If sProcess = UCase$(sName) Then
                            countProcessRunning = countProcessRunning + 1
                        End If
                    End If
                End If
            End If
            CloseHandle hProcess
        Next N
    End If

End Function

찾은 항목:

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

Excel 인스턴스에서 현재 활성화된 시트의 이름을 알고 있는 경우 객체를 가져옵니다.이것은 첫 번째 코드를 사용하여 응용 프로그램 제목에서 얻을 수 있을 것 같습니다.내 앱에서 나는 파일 이름을 알고 있습니다.

저는 항상 API 기능을 LAST 수단으로만 사용하는 것을 선호합니다.저는 이 폭로와 형식이 유사한 한 효과가 있을 방법을 고안했습니다.API 명령을 사용하지 않는 전체 솔루션은 다음과 같습니다.

사실 그것은 꽤 간단합니다.각 응용 프로그램 인스턴스 내에서 로드될 것으로 예상되는 워크북 중 하나에서 매우 기본적인 용도로 사용할 공용 서브루틴을 저장해야 합니다.

각 서브루틴은 전체 프로그램 체인의 링크로만 존재합니다.각 "링크"는 "체인"이 완료될 때까지 서브루틴 간에 전달되는 컬렉션 개체에 현재 응용 프로그램의 인스턴스를 추가합니다.

1단계. 프로그래밍 방식으로 새 Excel 인스턴스를 만듭니다.

2단계. 새 앱의 워크북 열기 방법에 워크북 변수를 할당합니다.

3단계. WB 변수.어플."서브루틴", 앱 실행

3단계에서는 앱 모음이 별도의 응용 프로그램 인스턴스에 로드된 워크북에 변수로 전달되는 것을 볼 수 있습니다.일단 "catcher" 서브루틴이 이 컬렉션 오브젝트를 수신하면, 그 서브루틴은 현재 애플리케이션 오브젝트를 컬렉션에 추가할 수 있습니다.2단계와 3단계는 최종 목적지에서 멈출 때까지 미리 결정된 각 "링크"에서 반복할 수 있습니다.

최종 인스턴스는 이론적으로 원래 워크북의 "catcher" 서브루틴으로 보내지거나 선택적 인수를 통해 최종 컬렉션 개체를 원래 서브루틴으로 재귀적으로 보낼 수 있으며, 여기서 포인트 체크는 서브루틴이 이전 포인트를 지나 계속하도록 허용할 수 있습니다.

복잡하게 들릴 수도 있지만, 약간의 독창성만 있으면 API 호출 없이도 를 달성하기가 매우 쉽습니다.

이것은 당신이 원하는 것을 이룰 수 있습니다.Excel 인스턴스가 열려 있는지 확인합니다.

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

중인 인턴스중경다우사음있수다액습을 수 .xlApp되고 있지 오류가 합니다(처리기가 필요하거나 할 수 ).인스턴스가 실행되고 있지 않으면 런타임 오류가 발생합니다(오류 핸들러가 필요하거나 필요할 수 있음).GetObjectfunction은 로드된 Excel의 첫 번째 인스턴스를 가져옵니다.당신은 그것으로 당신의 일을 할 수 있고, 다른 사람들에게 접근하기 위해, 당신은 그것을 닫고 시도할 수 있습니다.GetObject다시 다음 것을 얻기 위해 등.따라서 http://excelribbon.tips.net/T009452_Finding_Other_Instances_of_Excel_in_a_Macro.html) 에서 제공하는 ok-but-second-time 목표를 달성하게 될 것입니다.

당신이 선호하는 목표를 달성하기 위해, 저는 https://stackoverflow.com/a/3303016/2707864 이 당신에게 방법을 보여준다고 생각합니다.

개체 배열을 만들고 새로 만든 Excel을 저장합니다.어레이의 응용 프로그램입니다.그런 식으로 필요할 때 필요한 항목을 참조할 수 있습니다.간단한 예를 들어 보겠습니다.

모듈:

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

테스트() 매크로를 실행하면 두 개의 Excel 응용 프로그램이 팝업됩니다.그런 다음 다른 테스트()를 실행하면 Excel 응용 프로그램이 종료됩니다.완료 후 배열을 없음으로 설정할 수도 있습니다.

http://www.ozgrid.com/forum/showthread.php?t=182853 에 게시된 스크립트를 사용하여 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가 발생할 때까지 반복합니다.

자세한 내용은 이 문서에서 확인할 수 있습니다.

언급URL : https://stackoverflow.com/questions/30363748/having-multiple-excel-instances-launched-how-can-i-get-the-application-object-f