Excel.exe 使用 Application.Quit 后仍然 运行

Excel.exe still running after using Application.Quit

我试图通过编写 VBA 代码检查 Excel 工作簿并填写 Word 文档来简化报告模板。

代码无法终止任务管理器中的 Excel.exe 进程。

我尝试了此处、其他论坛和 Microsoft 文档中提出的解决方案。我收集到它与 运行 Application.Quit 方法时仍然存在的 COM 对象有关,但无法弄清楚它们来自哪里。

当将代码缩减为最基本的组件时,任务管理器中没有 Excel.exe 进程:

Private Sub Hämta_Click()

Dim XL As Excel.Application
Set XL = New Excel.Application

XL.Quit
Set XL = Nothing

End Sub

但是一旦我添加进去,Excel.exe 就会在任务管理器中保留 运行:

Private Sub Hämta_Click()

Dim XL As Excel.Application
Set XL = New Excel.Application

Dim wkb As Excel.Workbook
Set wkb = XL.Workbooks.Open("C:\Example.xls")

wkb.Close (False)
Set wkb = Nothing

XL.Quit
Set XL = Nothing

End Sub

我也试过这段代码,结果相同:

Private Sub Hämta_Click()

Dim XL As Object
Set XL = CreateObject("Excel.Application")

Dim wkb As Object
Set wkb = XL.Workbooks.Open("K:\Uppdrag.xls")

wkb.Close (False)
Set wkb = Nothing

XL.Quit
Set XL = Nothing

End Sub

以上两个宏不断创建未关闭的Excel.exe实例。

我见过一些示例,其中包含通过任务管理器终止进程的代码片段,但我不明白上述方法不起作用的原因。

我发现的唯一解决方法是不包含 XL.Quit 方法,而是设置 XL.Visible = True 并让用户手动关闭 window.

根据评论,似乎无法找到新创建的 excel 实例无法以“正常”方式完成的根本原因。

根据代码here 可以终止进程

Option Explicit
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 TerminateProcess Lib "kernel32" (ByVal hProcess As Long, ByVal uExitCode As Long) As Long
Private Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hwnd As Long, lpdwProcessId As Long) As Long
Private Declare Function AdjustTokenPrivileges Lib "advapi32.dll" (ByVal TokenHandle As Long, ByVal DisableAllPrivileges As Long, NewState As TOKEN_PRIVILEGES, ByVal BufferLength As Long, PreviousState As TOKEN_PRIVILEGES, ReturnLength As Long) As Long
Private Declare Function OpenProcessToken Lib "advapi32.dll" (ByVal ProcessHandle As Long, ByVal DesiredAccess As Long, TokenHandle As Long) As Long
Private Declare Function LookupPrivilegeValue Lib "advapi32.dll" Alias "LookupPrivilegeValueA" (ByVal lpSystemName As String, ByVal lpName As String, lpLuid As LUID) As Long
Private Declare Function GetCurrentProcess Lib "kernel32" () As Long

Private Type LUID
    LowPart As Long
    HighPart As Long
End Type

Private Type LUID_AND_ATTRIBUTES
    pLuid As LUID
    Attributes As Long
End Type

Private Type TOKEN_PRIVILEGES
    PrivilegeCount As Long
    TheLuid As LUID
    Attributes As Long
End Type

Function ProcessTerminate(Optional lProcessID As Long, Optional lHwndWindow As Long) As Boolean
    Dim lhwndProcess As Long
    Dim lExitCode As Long
    Dim lRetVal As Long
    Dim lhThisProc As Long
    Dim lhTokenHandle As Long
    Dim tLuid As LUID
    Dim tTokenPriv As TOKEN_PRIVILEGES, tTokenPrivNew As TOKEN_PRIVILEGES
    Dim lBufferNeeded As Long
    
    Const PROCESS_ALL_ACCESS = &H1F0FFF, PROCESS_TERMINATE = &H1
    Const ANYSIZE_ARRAY = 1, TOKEN_ADJUST_PRIVILEGES = &H20
    Const TOKEN_QUERY = &H8, SE_DEBUG_NAME As String = "SeDebugPrivilege"
    Const SE_PRIVILEGE_ENABLED = &H2

    On Error Resume Next
    If lHwndWindow Then
        'Get the process ID from the window handle
        lRetVal = GetWindowThreadProcessId(lHwndWindow, lProcessID)
    End If
    
    If lProcessID Then
        'Give Kill permissions to this process
        lhThisProc = GetCurrentProcess
        
        OpenProcessToken lhThisProc, TOKEN_ADJUST_PRIVILEGES Or TOKEN_QUERY, lhTokenHandle
        LookupPrivilegeValue "", SE_DEBUG_NAME, tLuid
        'Set the number of privileges to be change
        tTokenPriv.PrivilegeCount = 1
        tTokenPriv.TheLuid = tLuid
        tTokenPriv.Attributes = SE_PRIVILEGE_ENABLED
        'Enable the kill privilege in the access token of this process
        AdjustTokenPrivileges lhTokenHandle, False, tTokenPriv, Len(tTokenPrivNew), tTokenPrivNew, lBufferNeeded

        'Open the process to kill
        lhwndProcess = OpenProcess(PROCESS_TERMINATE, 0, lProcessID)
    
        If lhwndProcess Then
            'Obtained process handle, kill the process
            ProcessTerminate = CBool(TerminateProcess(lhwndProcess, lExitCode))
            Call CloseHandle(lhwndProcess)
        End If
    End If
    On Error GoTo 0
End Function

你只需要像那样使用代码

Sub TestIt()

    Dim xlApp As Object
    Set xlApp = CreateObject("Excel.Application")
    
    ' Do something with xlApp
        
    
    'Terminate the process
    ProcessTerminate , xlApp.hwnd
End Sub