VBA скрипт для закрытия всех экземпляров Excel кроме себя - PullRequest
3 голосов
/ 20 августа 2010

В моей функции обработки ошибок есть подпрограмма, которая пытается закрыть каждую книгу, открытую в каждом экземпляре Excel.В противном случае, это может остаться в памяти и сломать мой следующий VBScript.Также следует закрыть каждую рабочую книгу без , сохранив любые изменения.

Sub CloseAllExcel()
On Error Resume Next
    Dim ObjXL As Excel.Application
    Set ObjXL = GetObject(, "Excel.Application")
    If Not (ObjXL Is Nothing) Then
        Debug.Print "Closing XL"
        ObjXL.Application.DisplayAlerts = False
        ObjXL.Workbooks.Close
        ObjXL.Quit
        Set ObjXL = Nothing
    Else
        Debug.Print "XL not open"
    End If
End Sub

Однако этот код не является оптимальным.Например, он может закрыть 2 книги в одном экземпляре Excel, но если вы откроете 2 экземпляра Excel, он закроет только 1.

Как переписать это, чтобы закрыть все Excel без сохранения каких-либо изменений?

Дополнительные кредиты:

Как это сделать и для Access, не закрывая файл Access, на котором размещен этот скрипт?

Ответы [ 6 ]

4 голосов
/ 20 августа 2010

Вы должны быть в состоянии использовать дескрипторы окна для этого.

Public Sub CloseAllOtherAccess()
    Dim objAccess As Object
    Dim lngMyHandle As Long
    Dim strMsg As String

On Error GoTo ErrorHandler
    lngMyHandle = Application.hWndAccessApp

    Set objAccess = GetObject(, "Access.Application")
    Do While TypeName(objAccess) = "Application"
        If objAccess.hWndAccessApp <> lngMyHandle Then
            Debug.Print "found another Access instance: " & _
                objAccess.hWndAccessApp
            objAccess.Quit acQuitSaveNone
        Else
            Debug.Print "found myself"
            Exit Do
        End If
        Set objAccess = GetObject(, "Access.Application")
    Loop

ExitHere:
    Set objAccess = Nothing
    On Error GoTo 0
    Exit Sub

ErrorHandler:
    strMsg = "Error " & Err.Number & " (" & Err.Description _
        & ") in procedure CloseAllOtherAccess"
    MsgBox strMsg
    GoTo ExitHere
End Sub

Мне кажется, GetObject возвращает «самый старый» экземпляр Access.Таким образом, подпрограмма закрывает все экземпляры Access, запущенные до того, в котором запускается подпрограмма.Как только он находит себя, он останавливается.Может быть, это хорошо для вашей ситуации.Но если вам нужно также закрыть экземпляры Access, запущенные после того, который выполняет код, посмотрите на функции дескриптора окна Windows API.

Я не пробовал этот подход для Excel.Но я видел, что Excel предоставляет Application.Hwnd и Application.Hinstance ... поэтому я подозреваю, что вы можете сделать что-то подобное там.

Также обратите внимание, что я избавился от On Error Resume Next.GetObject всегда будет возвращать объект Application в этом подпрограмме, поэтому он не служил какой-либо цели.Кроме того, я стараюсь избегать On Error Resume Next в целом.

Обновление : поскольку GetObject не выполнит эту работу за вас, используйте другой метод для получения дескрипторов окна всех экземпляров Access.Закройте каждый из них, чей дескриптор окна не совпадает с тем, который вы хотите оставить запущенным (Application.hWndAccessApp).

Public Sub CloseAllAccessExceptMe()
'FindWindowLike from: '
'How To Get a Window Handle Without Specifying an Exact Title '
'http://support.microsoft.com/kb/147659 '

'ProcessTerminate from: '
'Kill a Process through VB by its PID '
'http://en.allexperts.com/q/Visual-Basic-1048/Kill-Process-VB-its-1.htm '

    Dim lngMyHandle As Long
    Dim i As Long
    Dim hWnds() As Long

    lngMyHandle = Application.hWndAccessApp

    ' get array of window handles for all Access top level windows '
    FindWindowLike hWnds(), 0, "*", "OMain", Null

    For i = 1 To UBound(hWnds())
        If hWnds(i) = lngMyHandle Then
            Debug.Print hWnds(i) & " -> leave myself running"
        Else
            Debug.Print hWnds(i) & " -> close this one"
            ProcessTerminate , hWnds(i)
        End If
    Next i
End Sub
3 голосов
/ 20 августа 2010

Я только что попробовал следующее с Excel и Access:

Dim sKill As String

sKill = "TASKKILL /F /IM msaccess.exe"
Shell sKill, vbHide

Если вы измените msaccess.exe на excel.exe, Excel будет убит.

Если вы хотите немного больше контролировать процесс, проверьте:

http://www.vbaexpress.com/kb/getarticle.php?kb_id=811

2 голосов
/ 24 сентября 2012

Я знаю, что это старый пост, но для тех, кто посещает здесь с поисков, может оказаться полезным.Этот код был найден и изменен.Это даст вам каждый ЛИСТ в каждой РАБОЧЕЙ КНИГЕ в каждом МОМЕНТЕ.Оттуда вы можете определить активный экземпляр.

Модуль ..............

Declare Function FindWindowEx Lib "User32" Alias "FindWindowExA" (ByVal hWnd1 As Long, ByVal hWnd2 As Long, ByVal lpsz1 As String, ByVal lpsz2 As String) As Long
Declare Function GetClassName Lib "User32" Alias "GetClassNameA" (ByVal hWnd As Long, ByVal lpClassName As String, ByVal nMaxCount As Long) As Long
Declare Function IIDFromString Lib "ole32" (ByVal lpsz As Long, ByRef lpiid As UUID) As Long
Declare Function AccessibleObjectFromWindow Lib "oleacc" (ByVal hWnd As Long, ByVal dwId As Long, ByRef riid As UUID, ByRef ppvObject As Object) As Long

Type UUID 'GUID
    Data1 As Long
    Data2 As Integer
    Data3 As Integer
    Data4(7) As Byte
End Type

Код ………………… ...

Const IID_IDispatch As String = "{00020400-0000-0000-C000-000000000046}"
Const OBJID_NATIVEOM As Long = &HFFFFFFF0

Sub ListAll()
    Dim I As Integer
    Dim hWndMain As Long
    On Error GoTo MyErrorHandler
        hWndMain = FindWindowEx(0&, 0&, "XLMAIN", vbNullString)
        I = 1
        Do While hWndMain <> 0
            Debug.Print "Excel Instance " & I
            GetWbkWindows hWndMain
            hWndMain = FindWindowEx(0&, hWndMain, "XLMAIN", vbNullString)
            I = I + 1
        Loop
        Exit Sub
    MyErrorHandler:
    MsgBox "GetAllWorkbookWindowNames" & vbCrLf & vbCrLf & "Err = " & Err.Number & vbCrLf & "Description: " & Err.Description
End Sub

Sub GetWbkWindows(ByVal hWndMain As Long)
    Dim hWndDesk As Long
    Dim hWnd As Long
    Dim strText As String
    Dim lngRet As Long
    On Error GoTo MyErrorHandler     
        hWndDesk = FindWindowEx(hWndMain, 0&, "XLDESK", vbNullString)
        If hWndDesk <> 0 Then
            hWnd = FindWindowEx(hWndDesk, 0, vbNullString, vbNullString) 
            Do While hWnd <> 0
                strText = String$(100, Chr$(0))
                lngRet = GetClassName(hWnd, strText, 100)
                If Left$(strText, lngRet) = "EXCEL7" Then
                    GetExcelObjectFromHwnd hWnd
                    Exit Sub
                End If
                hWnd = FindWindowEx(hWndDesk, hWnd, vbNullString, vbNullString)
            Loop
            On Error Resume Next
        End If
            Exit Sub
    MyErrorHandler:
        MsgBox "GetWbkWindows" & vbCrLf & vbCrLf & "Err = " & Err.Number & vbCrLf & "Description: " & Err.Description
End Sub

Function GetExcelObjectFromHwnd(ByVal hWnd As Long) As Boolean
    Dim fOk As Boolean
    Dim I As Integer
    Dim obj As Object
    Dim iid As UUID
    Dim objApp As Excel.Application
    Dim myWorksheet As Worksheet
    On Error GoTo MyErrorHandler        
        fOk = False
        Call IIDFromString(StrPtr(IID_IDispatch), iid)
        If AccessibleObjectFromWindow(hWnd, OBJID_NATIVEOM, iid, obj) = 0 Then 'S_OK
            Set objApp = obj.Application
            For I = 1 To objApp.Workbooks.Count
                Debug.Print "     " & objApp.Workbooks(I).Name
                For Each myWorksheet In objApp.Workbooks(I).Worksheets
                    Debug.Print "          " & myWorksheet.Name
                    DoEvents
                Next
                fOk = True
            Next I
        End If
        GetExcelObjectFromHwnd = fOk
        Exit Function
    MyErrorHandler:
        MsgBox "GetExcelObjectFromHwnd" & vbCrLf & vbCrLf & "Err = " & Err.Number & vbCrLf & "Description: " & Err.Description
End Function

Надеюсь, это кому-нибудь поможет:)

2 голосов
/ 20 августа 2010

Разграничение открытых экземпляров приложения - очень старая проблема, и она не уникальна для VBA.

Я пытался выяснить это сам на протяжении многих лет, но никогда с большим успехом, чем когда-либо прежде.

Я думаю, что в общем и целом, вы никогда не узнаете, является ли экземпляр приложения, на который вы ссылаетесь, тем, в котором выполняется код (поэтому при его завершении другие экземпляры могут остаться открытыми).

1 голос
/ 19 июня 2014

Это ответ на старый пост, но так же, как постер 2012 года, надеюсь, он поможет кому-то, кто может прийти сюда на основе общего поиска в Интернете.

Фон Моя компания использует XLSX «модели», чтобы автоматически превращать наши данные в «красивые». Экспорт данных из SAS в виде XLS; у нас нет лицензии или надстроек для экспорта в формате XLSX. Обычный процесс - копирование / вставка каждого из 14 выходов SAS в XLSX. Приведенный ниже код перебирает первые два экспорта, где данные копируются из XLS, вставляются в XLSX, а XLS закрывается.

Обратите внимание: файл XLSX сохраняется на жесткий диск. Файлы XLS НЕ СОХРАНЕНЫ, то есть путь идет к "My Documents/", но там нет имени файла или файла, видимого.

Sub Get_data_from_XLS_to_XLSX ()
    Dim xlApp1 As Excel.Application
    Dim xlApp2 As Excel.Application

'Speed up processing by turning off Automatic Calculations and Screen Updating
    Application.Calculation = xlCalculationManual
    Application.ScreenUpdating = False


'Copies data from Book1 (xls) and pastes into ThisWorkbook (xlsx), then closes xls file
    Set xlApp1 = GetObject("Book1").Application

    xlApp1.Workbooks("Book1").Sheets("Sheet1").Range("A2:E2").Copy
    Application.ThisWorkbook.Worksheets("Data1").Cells(5, 2).PasteSpecialPaste:=xlPasteValues

'Clears the clipboard, turns off the "do you want to save" alert, closes xls file
    xlApp1.CutCopyMode = False
    xlApp1.DisplayAlerts = False
    xlApp1.Quit
    xlApp1.DisplayAlerts = True



'Same as the first one above, but now it's a second/different xls file, i.e. Book2
    Set xlApp2 = GetObject("Book2").Application

    xlApp2.Workbooks("Book2").Sheets("Sheet1").Range("A2:E2").Copy
    Application.ThisWorkbook.Sheets("Data2").Cells(10, 2).PasteSpecial Paste:=xlPasteValues

'Clears the clipboard, turns off the "do you want to save" alert, closes xls file
    xlApp2.CutCopyMode = False
    xlApp2.DisplayAlerts = False
    xlApp2.Quit
    xlApp2.DisplayAlerts = True


'Sub continues for 12 more iterations of similar code
End Sub

Вы должны четко указать свои утверждения. то есть вместо Workbooks("Book_Name") убедитесь, что вы указали приложение, на которое ссылаетесь, будь то Application.Workbooks("Book_Name") или xlApp1.Workbooks("Book_Name")

0 голосов
/ 20 августа 2010

попробуйте поставить его в цикл

Set ObjXL = GetObject(, "Excel.Application")
do until ObjXL Is Nothing
        Debug.Print "Closing XL"
        ObjXL.Application.DisplayAlerts = False
        ObjXL.Workbooks.Close
        ObjXL.Quit
        Set ObjXL = Nothing
        Set ObjXL = GetObject(, "Excel.Application")  ' important!
loop
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...