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.
Thanks
Very good script. I used this to prevent having multiple instances of iexplore.exe while parsing web pages.
Thank you very much!
Thanks That Help HEAPS
I glad you found it useful.
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) …..
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!
Very useful, thanks.
how can i detected the process that are in status “Not Responding”?
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.
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.
could I use this same process to kill a vba loop in ms access?
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?
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