2

I'm trying to simplify a report template by writing VBA code that checks an Excel Workbook and fills in the Word document.

The code fails to terminate the Excel.exe process in Task Manager.

I tried solutions proposed here, other forums and in Microsoft's documentation. I gather it has to do with COM objects still existing when running the Application.Quit method but can't figure out where those come from.

When reducing the code down to it's most basic components there's no Excel.exe process still in Task Manager:

Private Sub Hämta_Click()

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

XL.Quit
Set XL = Nothing

End Sub

But as soon as I add to it, Excel.exe keeps running in Task Manager:

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

I also tried this code with the same result:

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

The above two macros keep creating instances of Excel.exe which are not closed.

I've seen examples where code snippets are included that kills the process via Task Manager, but I don't understand the reason for the above not working.

The only workaround I found is to not include the XL.Quit method and instead set XL.Visible = True and let the user manually close the window.

Community
  • 1
  • 1
  • 2
    According to [this](https://stackoverflow.com/questions/18738677/application-quit-command-not-closing-the-entire-excel-application), there is a hidden dialog open preventing closure, you can save the workbook in the before closing event to bypass that. – Warcupine Sep 28 '21 at 13:15
  • Or even just set the state to saved. Try looking through the answers at https://stackoverflow.com/questions/3628252/closing-excel-application-using-vba, https://stackoverflow.com/questions/49450977/application-quit-leaves-excel-running-in-the-background, https://stackoverflow.com/questions/18738677/application-quit-command-not-closing-the-entire-excel-application, https://stackoverflow.com/questions/42113082/excel-application-object-quit-leaves-excel-exe-running – ed2 Sep 28 '21 at 13:17
  • Does this answer your question? [Closing Excel Application using VBA](https://stackoverflow.com/questions/3628252/closing-excel-application-using-vba) – ed2 Sep 28 '21 at 13:18
  • @Warcupine Thanks for the reply! Unfortunately this does not seem to work in my case. – Majonnaesblod Sep 28 '21 at 13:38
  • @ed2 Thanks for the reply! Funnily enough I've combed through all threads regarding this issue that I could find, including those you've posted and have not been able to find a solution as of yet. I tried saving the workbook and setting the state to saved as you and Warcupine suggested but to no avail. – Majonnaesblod Sep 28 '21 at 13:43
  • Add `xl.visible = true` and debug the code to see what is happening. This should get you closer to the root cause. – Storax Sep 28 '21 at 13:48
  • @Storax Hi! Thanks for taking the time to respond. Can't see how that gets me any closer to the cause unfortunately. What happens is that the macro opens the workbook, closes the workbook without saving and after executing the XL.Quit method closes the window, but the process still remains in the task manager. – Majonnaesblod Sep 28 '21 at 13:58
  • Did you step through the code? – Storax Sep 28 '21 at 14:12
  • Gentle tap not working? Try the sledgehammer https://stackoverflow.com/questions/35720160/close-all-excel-workbooksseveral-instances-of-excel-might-be-open/35734389#35734389 – ed2 Sep 28 '21 at 14:45
  • @Storax Yes, I did. Same result, even when adding wkb.Saved = True befor wkb.Close (False). There's no dialog that I can see that prevents it from closing. Thanks for the tip though, it seems like good practice to step through the code to find the problem, I'll keep that in mind in the future. – Majonnaesblod Sep 29 '21 at 07:05
  • @ed2 Yes, I saw that thread before as well. The reason I'd prefer not to do that is that I don't want other open Excel workbooks to close simultaneously. I did see a post where someone described a way to ID only the current Excel.Exe process and close that, but the Terminate() method didn't work and I couldn't figure out what reference library it was in. You wouldn't happen to know? If so I'd happily investigate this method further as it right now seems like the only option available if I want to keep the excel-process hidden from the user. – Majonnaesblod Sep 29 '21 at 07:09

2 Answers2

2

Based on the comments it does not seem to be possible to find the root cause why the newly created excel instance cannot be finished in a "normal" way.

Based on the code here one can just kill the process

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

And you just use the code like that

Sub TestIt()

    Dim xlApp As Object
    Set xlApp = CreateObject("Excel.Application")
    
    ' Do something with xlApp
        
    
    'Terminate the process
    ProcessTerminate , xlApp.hwnd
End Sub
Storax
  • 11,158
  • 3
  • 16
  • 33
  • That did it! Just had to add PtrSafe between Declare and Function to update for 64-bit. Thank you so much! I really appreciate the effort! – Majonnaesblod Sep 29 '21 at 09:16
  • Of all the suggestions I was able to read in this post and related, this is the only approach which solved my problem. Thank you! – mherzog May 09 '22 at 02:58
-1

Try to declare and use a workbooks variable then set it to nothing at the end of your code

Milad
  • 77
  • 11