MS Access – VBA – Kill a Process

Every once in a while, programs do not shutdown properly and thus cause headaches. So it can become necessary to terminate the process. Below are a couple different approaches you can employ to terminate, forcibly close, a process/program.
 

Shell

'---------------------------------------------------------------------------------------
' Procedure : Shell_KillProcess
' Author    : Daniel Pineault, CARDA Consultants Inc.
' Website   : http://www.cardaconsultants.com
' Purpose   : Forcibly kill all the instances of a specified process
' Copyright : The following is release as Attribution-ShareAlike 4.0 International
'             (CC BY-SA 4.0) - https://creativecommons.org/licenses/by-sa/4.0/
' Req'd Refs: None required
' References: https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/tasklist
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' sProcessName  : Name of the process to kill
'
' Usage:
' ~~~~~~
' Call Shell_KillProcess("explorer.exe")
' Call Shell_KillProcess("excel.exe")
' Call Shell_KillProcess("calculator.exe")
'
' Revision History:
' Rev       Date(yyyy-mm-dd)        Description
' **************************************************************************************
' 1         2020-08-21              Initial Release
'---------------------------------------------------------------------------------------
Public Function Shell_KillProcess(ByVal sProcessName As String) As Boolean
    'DOS/Command Prompt related commands
    'Tasklist                           'view active tasks/processes
    'Taskkill /F /PID pid_number        'by PID
    'Taskkill /IM "process name" /F     'by Process Name
    
    'Get a listing of all the existing processes
    'Call Shell("cmd.exe /K Tasklist | SORT", vbNormalFocus)
    
    'Kill the task immediately / Standard usage
    Call Shell("Taskkill /IM " & sProcessName & " /F", vbHide)
    
    'If you want to add a delay
    'Call Shell("cmd.exe /K TIMEOUT /T 5 /NOBREAK & Taskkill /IM " & sProcessName & " /F & exit", vbHide)
End Function

a variation on this would be:

'---------------------------------------------------------------------------------------
' Procedure : Shell_KillProcess
' Author    : Daniel Pineault, CARDA Consultants Inc.
' Website   : http://www.cardaconsultants.com
' Purpose   : Forcibly kill all the instances of a specified process
' Copyright : The following is release as Attribution-ShareAlike 4.0 International
'             (CC BY-SA 4.0) - https://creativecommons.org/licenses/by-sa/4.0/
' Req'd Refs: None required
' References: https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/tasklist
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' sProcessName  : Name of the process to kill
'
' Usage:
' ~~~~~~
' Call Shell_KillProcess("explorer.exe")
' Call Shell_KillProcess("excel.exe")
' Call Shell_KillProcess("calculator.exe")
'
' Revision History:
' Rev       Date(yyyy-mm-dd)        Description
' **************************************************************************************
' 1         2024-04-25              Initial Release
'---------------------------------------------------------------------------------------
Public Function Shell_KillProcess(ByVal sProcessName As String) As Boolean
    CreateObject("WScript.Shell").Run "Taskkill /IM " & sProcessName & " /F", 0, False
End Function

For demonstrative purposes, to illustrate how you can get information from DOS/Command Prompt and return it to VBA, should you wish to create a function to get a listing of running task/processes, you could do:

Public Function Shell_ListProcesses() As String
    Call Shell("cmd.exe /c Tasklist | SORT | Clip", vbHide)
    'Call Shell("cmd.exe /k Tasklist | SORT", vbNormalFocus) 'Show to the user
    DoEvents
    With CreateObject("New:{1C3B4210-F441-11CE-B9EA-00AA006B1A69}")
        .GetFromClipboard
        Shell_ListProcesses = .GetText(1)
    End With
End Function

 

WMI

'---------------------------------------------------------------------------------------
' Procedure : WMI_KillProcess
' Author    : Daniel Pineault, CARDA Consultants Inc.
' Website   : http://www.cardaconsultants.com
' Purpose   : Forcibly kill all the instances of a specified process
' Copyright : The following is release as Attribution-ShareAlike 4.0 International
'             (CC BY-SA 4.0) - https://creativecommons.org/licenses/by-sa/4.0/
' Req'd Refs: Early Binding -> Microsoft WMI Scripting VX.X Library
'             Late Binding  -> None required
' References: https://learn.microsoft.com/en-us/windows/win32/cimwin32prov/win32-process
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' sProcessName  : Name of the process to kill
' sHost         : Host computer to query, omit for the local PC
'
' Usage:
' ~~~~~~
' Call WMI_KillProcess("explorer.exe")
' Call WMI_KillProcess("excel.exe")
' Call WMI_KillProcess("calculator.exe")
'
' Revision History:
' Rev       Date(yyyy-mm-dd)        Description
' **************************************************************************************
' 1         2015-05-28              Initial Release
' 2         2020-08-21              Added Proc Header
'                                   Code updated
'                                   Updated Error Handler
'                                   Made it Option Explicit compliant
' 3         2024-04-24              Added ExecQuery flags
' 4         2024-04-25              Added conditional compilation (Early/Late Binding)
'                                   Restricted WMI Query by adding WHERE clause
'---------------------------------------------------------------------------------------
Public Function WMI_KillProcess(ByVal sProcessName As String, _
                                Optional sHost As String = ".") As Boolean
    On Error GoTo Error_Handler
    #Const WMI_EarlyBind = False    'True => Early Binding / False => Late Binding
    #If WMI_EarlyBind = True Then
        Dim oWMI              As WbemScripting.SWbemServices
        Dim oCols             As WbemScripting.SWbemObjectSet
        Dim oCol              As WbemScripting.SWbemObject
    #Else
        Dim oWMI              As Object
        Dim oCols             As Object
        Dim oCol              As Object
        Const wbemFlagReturnImmediately = 16    '(&H10)
        Const wbemFlagForwardOnly = 32          '(&H20)
    #End If
    Dim sWMIQuery             As String         'WMI Query

    Set oWMI = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & sHost & "\root\cimv2")
    sWMIQuery = "SELECT Name FROM Win32_Process WHERE Name = '" & sProcessName & "'"
    Set oCols = oWMI.ExecQuery(sWMIQuery, , wbemFlagReturnImmediately Or wbemFlagForwardOnly)
    For Each oCol In oCols
        If LCase(sProcessName) = LCase(oCol.Name) Then
            oCol.Terminate    ' Kill this instances of the process
        End If
    Next oCol
    WMI_KillProcess = True

Error_Handler_Exit:
    On Error Resume Next
    Set oCol = Nothing
    Set oCols = Nothing
    Set oWMI = Nothing
    Exit Function

Error_Handler:
    MsgBox "The following error has occurred" & vbCrLf & vbCrLf & _
           "Error Number: " & Err.Number & vbCrLf & _
           "Error Source: WMI_KillProcess" & vbCrLf & _
           "Error Description: " & Err.Description & _
           Switch(Erl = 0, "", Erl <> 0, vbCrLf & "Line No: " & Erl) _
           , vbOKOnly + vbCritical, "An Error has Occurred!"
    Resume Error_Handler_Exit
End Function

 

APIs

This is an API approach I first saw mentioned by Brent Spaulding’s (aka datAdrenaline) in an UtterAccess thread many years ago.

Type PROCESSENTRY32
    dwSize                    As Long
    cntUsage                  As Long
    th32ProcessID             As Long
    th32DefaultHeapID         As Long
    th32ModuleID              As Long
    cntThreads                As Long
    th32ParentProcessID       As Long
    pcPriClassBase            As Long
    dwFlags                   As Long
    szexeFile                 As String * 260
End Type

Declare PtrSafe Function CloseHandle Lib "kernel32.dll" (ByVal hObject As Long) As Long
Declare PtrSafe Function CreateToolhelpSnapshot Lib "kernel32.dll" Alias "CreateToolhelp32Snapshot" (ByVal lFlags As Long, lProcessID As Long) As Long
Declare PtrSafe Function OpenProcess Lib "kernel32.dll" (ByVal dwDesiredAccess As Long, ByVal blnheritHandle As Long, ByVal dwAppProcessId As Long) As Long
Declare PtrSafe Function ProcessFirst Lib "kernel32.dll" Alias "Process32First" (ByVal hSnapshot As Long, uProcess As PROCESSENTRY32) As Long
Declare PtrSafe Function ProcessNext Lib "kernel32.dll" Alias "Process32Next" (ByVal hSnapshot As Long, uProcess As PROCESSENTRY32) As Long
Declare PtrSafe Function TerminateProcess Lib "kernel32.dll" (ByVal ApphProcess As Long, ByVal uExitCode As Long) As Long


Public Sub KillProcess(ByVal sProcessName As String)
    Dim uProcess              As PROCESSENTRY32
    Dim RProcessFound         As Long
    Dim hSnapshot             As Long
    Dim SzExename             As String
    Dim ExitCode              As Long
    Dim MyProcess             As Long
    Dim AppKill               As Boolean
    Dim AppCount              As Integer
    Dim i                     As Integer
    Dim WinDirEnv             As String
'    Const PROCESS_ALL_ACCESS = 0
    Const PROCESS_TERMINATE = (&H1)
    Const TH32CS_SNAPPROCESS  As Long = 2&

    If sProcessName <> "" Then
        AppCount = 0

        uProcess.dwSize = Len(uProcess)
        hSnapshot = CreateToolhelpSnapshot(TH32CS_SNAPPROCESS, 0&)
        RProcessFound = ProcessFirst(hSnapshot, uProcess)

        Do
            i = InStr(1, uProcess.szexeFile, Chr(0))
            SzExename = LCase$(Left$(uProcess.szexeFile, i - 1))
            WinDirEnv = Environ("Windir") + "\"
            WinDirEnv = LCase$(WinDirEnv)

            If Right$(SzExename, Len(sProcessName)) = LCase$(sProcessName) Then
                AppCount = AppCount + 1
                MyProcess = OpenProcess(PROCESS_TERMINATE, False, uProcess.th32ProcessID)
                AppKill = TerminateProcess(MyProcess, ExitCode)
                Call CloseHandle(MyProcess)
            End If
            RProcessFound = ProcessNext(hSnapshot, uProcess)
        Loop While RProcessFound
        Call CloseHandle(hSnapshot)
    End If
End Sub

 

PowerShell

'---------------------------------------------------------------------------------------
' Procedure : PS_KillProcess
' Author    : Daniel Pineault, CARDA Consultants Inc.
' Website   : http://www.cardaconsultants.com
' Purpose   : Forcibly kill all the instances of a specified process
' Copyright : The following is release as Attribution-ShareAlike 4.0 International
'             (CC BY-SA 4.0) - https://creativecommons.org/licenses/by-sa/4.0/
' Req'd Refs: None required
' Dependencies: PS_Execute() function
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' sProcessName  : Name of the process to kill
'                 !!Careful - unlike other approaches sProcessName does not include '.exe'!!
'
' Usage:
' ~~~~~~
' Call PS_KillProcess("explorer")
' Call PS_KillProcess("excel")
' Call PS_KillProcess("CalculatorApp")
'
' Revision History:
' Rev       Date(yyyy-mm-dd)        Description
' **************************************************************************************
' 1         2020-08-21              Initial Release
'---------------------------------------------------------------------------------------
Public Function PS_KillProcess(ByVal sProcessName As String) As Boolean
    'PowerShell related commands
    'Get-Process |Select-Object * | Format-Table    'view active processes
    'Stop-Process -ID PID -Force                    'by PID
    'Stop-Process -Name "ProcessName" -Force        'by Process Name
    
    Call PS_Execute("Stop-Process -Name """ & sProcessName & """ -Force")
End Function

 

Not VBA!

Sometimes we need a solution outside of VBA, so here is one option you could employ.

Batch File

We can easily create a simple batch file to kill any task we want by doing something like:

taskkill /IM "msaccess.exe" /F

where:
/IM = Specifies the image name of the process to be terminated.
/F = Specifies to forcefully terminate the process(es).

If you want more information on the various command line switches available to you, simply open a command prompt window and enter

taskkill /?

 

A Final Word

Do be advised these approaches will close all the instances of a given process. So say you have 3 instances of Excel running and you terminate the excel.exe, all three instances will be terminated.

Also, they use brute force termination of a process, so you do not get prompted to save any unsaved work… These are last resort approaches, typically for hung processes. Thus, first always attempt to close the application gracefully using built-in automation approaches (.Quit, .Close, …) and only resort to these when other techniques fails.

13 responses on “MS Access – VBA – Kill a Process

  1. Joni

    Very good script. I used this to prevent having multiple instances of iexplore.exe while parsing web pages.

    Thank you very much!

  2. Wolfgang Kern

    Can someone tell me which library needs to be referenced (Windows 7)in order to use the objects used in this vba code (namely oprocess) …..

    1. Daniel Pineault Post author

      No Libraries are required. The variable is defined as an Object so it is using Late Binding which means no Reference Libraries need to be set. Drop into a code module and use. It is that simple!

    1. Daniel Pineault Post author

      Offhand, I’m not sure. You could try iterating through the process properties when this occurs to see if you can use one in particular as an extra criteria.

  3. Cindy

    Thank you – all of the other “solutions” I looked at for several hours did not work – yours did.
    I am looping through queries and adding tabs to a spreadsheet using
    DoCmd.TransferSpreadsheet acExport, , strQueryName, strFileName, False
    Then I format the columns and rows on that tab and go to the next query. It was leaving Excel open for each tab.

    1. Daniel Pineault Post author

      No. A VBA loop would be associated to an exe, say msaccess.exe and by killing it you would be terminating the entire Access instance, not just the loop.

      Why would you code you loop to self exit based on some criteria?

  4. Chris Greaves

    Christmas Day 2020.
    Daniel Pineault, thank you for this superlative Christmas Gift!
    I have a Word2003 application that scours files and attempts to open them and obtain project code. My code was leaving numerous instances of PowerPnt.exe lying around – up to two dozen copies. Bad code on my part, I know.

    You procedure means that I can implement a quick fix and get the analysis done and (we hope before the new year!) clean up my code. I shall be plagiarizing your code, for sure!
    Best wishes for the new year
    Chris Greaves