Solved

Terminate Application by Knowing its .exe name

Posted on 2000-04-21
22
297 Views
Last Modified: 2011-09-20
Using VB 6.0, how can i terminate an application knowing its exe name? I do have code that searches the running applications and returns true if the application is running. As the caption text in the running application changes frequently, I would like to stay away from determining its handle based on its caption (ie. FindWindow API) Function. Is there an API call available that I can use to just pass the exe name to it such as terminate "WINWORD.EXE"?? IF so which one and how? I am very new to using API calls, so any and all help is very much appreciated.
0
Comment
Question by:techconcepts
  • 10
  • 5
  • 3
  • +3
22 Comments
 
LVL 28

Expert Comment

by:AzraSound
ID: 2738395
Declare Function FindWindow Lib "user32" Alias _
"FindWindowA" (ByVal lpClassName As String, _
ByVal lpWindowName As String) As Long

Declare Function PostMessage Lib "user32" Alias _
"PostMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, _
ByVal wParam As Long, lParam As Any) As Long

Const WM_CLOSE = &H10

Dim winHwnd As Long
Dim RetVal As Long

winHwnd = FindWindow(vbNullString, "WinWord")
RetVal = PostMessage(winHwnd, WM_CLOSE, 0&, 0&)
0
 
LVL 9

Expert Comment

by:Ruchi
ID: 2738397
0
 
LVL 9

Expert Comment

by:Ruchi
ID: 2738405

Option Explicit

Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long

Private Declare Function PostMessage Lib "user32" Alias "PostMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long

Private Const WM_CLOSE = &H10

Private Sub Form_Load()
Dim winHwnd As Long
Dim RetVal As Long
winHwnd = FindWindow(vbNullString, "WinWord")
Debug.Print winHwnd
If winHwnd <> 0 Then
    RetVal = PostMessage(winHwnd, WM_CLOSE, 0&, 0&)
    If RetVal = 0 Then
        MsgBox "There was an error posting message."
    End If
Else
    MsgBox "The WinWord is not open."
End If
0
 
LVL 9

Expert Comment

by:Ruchi
ID: 2738411

Option Explicit

Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long

Private Declare Function PostMessage Lib "user32" Alias "PostMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long

Private Const WM_CLOSE = &H10

Private Sub Form_Load()
Dim winHwnd As Long
Dim RetVal As Long
winHwnd = FindWindow(vbNullString, "WinWord")
Debug.Print winHwnd
If winHwnd <> 0 Then
    RetVal = PostMessage(winHwnd, WM_CLOSE, 0&, 0&)
    If RetVal = 0 Then
        MsgBox "There was an error posting message."
    End If
Else
    MsgBox "The WinWord is not open."
End If
0
 

Author Comment

by:techconcepts
ID: 2738525
Unless I am missing something, findwindow only works if the criteria matches the caption title exactly. I need to terminate by just knowing its exe name or at the very most its class name. In the Winword example, you would have to have have typed "Microsoft Word - Document1"  to have made this work (If you havent named the document after first opening word). I need a better way!!!!
0
 
LVL 28

Expert Comment

by:AzraSound
ID: 2738536
you are correct, so here is the workaround:

Function FindWindowPartial(TitleStart As String, Method As Integer) As Long
   Dim hWndTmp
   Dim nRet
   Dim TitleTmp As String

   hWndTmp = FindWindow(vbNullString, vbNullString)
   Do Until hWndTmp = 0
     
      ' Make sure the window has no parent
     
      If GetParent(hWndTmp) = 0 Then
         
         ' Get the window caption
         
         TitleTmp = Space(256)
         nRet = GetWindowText(hWndTmp, TitleTmp, Len(TitleTmp))
         If nRet Then
           
            ' Clean up return string, preparing for
            ' case-insensitive comparison.
           
            TitleTmp = UCase(Left(TitleTmp, nRet))

            If InStr(TitleTmp, UCase(TitleStart)) Then
                FindWindowPartial = hWndTmp
                Exit Do
            End If
         End If
      End If
     
      ' Get next window in master window list and continue
     
      hWndTmp = GetWindow(hWndTmp, GW_HWNDNEXT)
   Loop
End Function



Function EndTask()
hwnd = FindWindowPartial("WinWord", FWP_CONTAINS)
RetVal = PostMessage(winHwnd, WM_CLOSE, 0&, 0&)
End Function
0
 
LVL 28

Expert Comment

by:AzraSound
ID: 2738549
sorry forgot to give you the needed declarations, i think this is all of them:

Private Declare Function GetWindow Lib "user32" (ByVal hwnd As Long, ByVal wCmd As Long) As Long
Private Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hwnd As Long, ByVal lpString As String, ByVal cch As Long) As Long
Private Declare Function GetParent Lib "user32" (ByVal hwnd As Long) As Long


Const GW_HWNDNEXT = 2
Const FWP_STARTSWITH = 0
Const FWP_CONTAINS = 1
Const WM_GETTEXT = &HD
Const WM_GETTEXTLENGTH = &HE
0
 

Author Comment

by:techconcepts
ID: 2738611
There are times when the window caption has nothing to do with the progam that it is part of. I need to do this without using the findwindow via caption method. What other ways can I do this?
0
 
LVL 28

Expert Comment

by:AzraSound
ID: 2738759
sorry i always use that method since mostly the window caption contains the program name
0
 
LVL 27

Expert Comment

by:Ark
ID: 2738944
Hi
>I do have code that searches the running applications and returns true if the application is running<
What do you searching for? Captions or process ID? If you use first way (like Azra and Ruchi shown), you already have window handle (this handle don't change while App running) and you need not loop through windows again, just send message WM_CLOSE to this window. BTW, you need not only close this windows, but all its parents.
If you use second way, you know processID. Then:
'---bas module code---

Option Explicit
Public glPid     As Long
Public glHandle  As Long
Public colHandle As New Collection

Public Const WM_CLOSE = &H10
Public Const WM_DESTROY = &H2

Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Declare Function GetParent Lib "user32" (ByVal hwnd As Long) As Long
Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hwnd As Long, lpdwProcessId As Long) As Long
Declare Function EnumWindows Lib "user32" (ByVal lpEnumFunc As Long, ByVal lParam As Long) As Long

Public Function fEnumWindowsCallBack(ByVal hwnd As Long, ByVal lpData As Long) As Long
Dim lParent    As Long
Dim lThreadId  As Long
Dim lProcessId As Long
fEnumWindowsCallBack = 1
lThreadId = GetWindowThreadProcessId(hwnd, lProcessId)

If glPid = lProcessId Then
    lParent = GetParent(hwnd)
    If lParent = 0 Then
        colHandle.Add hwnd
    End If
End If
End Function

Public Function fEnumWindows() As Boolean
Dim hwnd As Long
Call EnumWindows(AddressOf fEnumWindowsCallBack, hwnd)
End Function

'--Form code--
Option Explicit
Private Sub Command1_Click()
' I started process using Shell, you already have this glPid when searching running apps
glPid = Shell("c:\windows\calc.exe", vbNormalFocus)
End Sub
Private Sub Command2_Click()
Dim i As Long
Call fEnumWindows
For i = 1 To colHandle.Count
    glHandle = colHandle.Item(i)
    Call SendMessage(glHandle, WM_CLOSE, 0&, 0&)
Next
End Sub


Cheers
0
 
LVL 3

Expert Comment

by:y2kwacko
ID: 2739071
Heres how to do it. This will only work if it has a window

add this to your declaration section:

Option Explicit
Public Declare Function GetWindow Lib "user32" (ByVal hwnd As Long, ByVal wCmd As Long) As Long
Public Declare Function PostMessage Lib "user32" Alias "PostMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Public Declare Function GetModuleFileName Lib "Kernel32" Alias "GetModuleFileNameA" (ByVal hModule As Long, ByVal lpFileName As String, ByVal nSize As Long) As Long
Public Declare Function GetParent Lib "user32" (ByVal hwnd As Long) As Long
Public Const WM_CLOSE = &H10
Public Const GW_HWNDFIRST = 0
Public Const GW_HWNDNEXT = 2

Public Const TH32CS_SNAPPROCESS As Long = 2&
Public Const MAX_PATH As Integer = 260

Public 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 * MAX_PATH
End Type
   
Public Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hwnd As Long, lpdwProcessId As Long) As Long

Public Declare Function CreateToolhelpSnapshot Lib "Kernel32" Alias "CreateToolhelp32Snapshot" (ByVal lFlags As Long, ByVal lProcessID As Long) As Long
Public Declare Function ProcessFirst Lib "Kernel32" Alias "Process32First" (ByVal hSnapShot As Long, uProcess As PROCESSENTRY32) As Long
Public Declare Function ProcessNext Lib "Kernel32" Alias "Process32Next" (ByVal hSnapShot As Long, uProcess As PROCESSENTRY32) As Long
Public Declare Sub CloseHandle Lib "Kernel32" (ByVal hPass As Long)



add this in your module:

Sub close_by_exe(your_frm As Form, exe_name As String)
    Dim CurrWnd As Long
    Dim Length As Long
    Dim TaskName As String
    Dim Parent As Long
    Dim exename As String
    Dim SlashPos As Integer
    Dim RetVal As Long
    CurrWnd = GetWindow(your_frm.hwnd, GW_HWNDFIRST)


    While CurrWnd <> 0
        exename = GetPathFromHandle(CurrWnd)
        SlashPos = InStr(exename, "\")
        Do While SlashPos <> 0
            DoEvents
            exename = Right(exename, Len(exename) - SlashPos)
            SlashPos = InStr(exename, "\")
        Loop
        Debug.Print exename & " - exename"
        If exename = exe_name Then RetVal = PostMessage(CurrWnd, WM_CLOSE, 0&, 0&): Exit Sub
        CurrWnd = GetWindow(CurrWnd, GW_HWNDNEXT)
        DoEvents
        Wend
        MsgBox "" & exe_name & " was not found running on your computer."
    End Sub


Public Function GetPathFromHandle(hwnd As Long) As String

Dim uProcess As PROCESSENTRY32
Dim r As Long, hSnapShot As Long, lProcess As Long, x As Long
Dim sAOLPath As String
x = GetWindowThreadProcessId(hwnd, lProcess)
hSnapShot = CreateToolhelpSnapshot(TH32CS_SNAPPROCESS, 0&)
If hSnapShot = 0 Then Exit Function
uProcess.dwSize = Len(uProcess)
r = ProcessFirst(hSnapShot, uProcess)
Do While r
    If uProcess.th32ProcessID = lProcess Then
        sAOLPath = uProcess.szExeFile
        x = InStr(1, LCase$(sAOLPath), ".exe")
        GetPathFromHandle = Left$(CStr(uProcess.szExeFile), x + 3)
        Exit Do
    End If
    r = ProcessNext(hSnapShot, uProcess)
Loop
Call CloseHandle(hSnapShot)
End Function


syntax:
call close_by_exe(FormName,"ExeName.Exe")

I tested it with AOL running the syntax was

Call close_by_exe(Me,"WAOL.EXE")

worked like a dream

Good Luck,
Kevin
0
What Is Threat Intelligence?

Threat intelligence is often discussed, but rarely understood. Starting with a precise definition, along with clear business goals, is essential.

 
LVL 28

Expert Comment

by:AzraSound
ID: 2739097
i hesitate to use snapshot b/c it does not run on WinNT.  If this is not a problem then it is a usable solution.
0
 

Accepted Solution

by:
LeXien earned 200 total points
ID: 2739740
'-----DECLARES---------------------------
Const MAX_PATH& = 260


Declare Function TerminateProcess Lib "kernel32" (ByVal ApphProcess As Long, ByVal uExitCode As Long) As Long


Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, ByVal blnheritHandle As Long, ByVal dwAppProcessId As Long) As Long


Declare Function ProcessFirst Lib "kernel32" Alias "Process32First" (ByVal hSnapshot As Long, uProcess As PROCESSENTRY32) As Long


Declare Function ProcessNext Lib "kernel32" Alias "Process32Next" (ByVal hSnapshot As Long, uProcess As PROCESSENTRY32) As Long


Declare Function CreateToolhelpSnapshot Lib "kernel32" Alias "CreateToolhelp32Snapshot" (ByVal lFlags As Long, lProcessID As Long) As Long


Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long


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 * MAX_PATH
    End Type
'---------------------------------------

Public Function KillApp(myName As String) As Boolean


   
    Const PROCESS_ALL_ACCESS = 0
    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
    On Local Error GoTo Finish
    appCount = 0
   
    Const TH32CS_SNAPPROCESS As Long = 2&
   
    uProcess.dwSize = Len(uProcess)
    hSnapshot = CreateToolhelpSnapshot(TH32CS_SNAPPROCESS, 0&)
    rProcessFound = ProcessFirst(hSnapshot, uProcess)


    Do While rProcessFound
        i = InStr(1, uProcess.szexeFile, Chr(0))
        szExename = LCase$(Left$(uProcess.szexeFile, i - 1))


        If Right$(szExename, Len(myName)) = LCase$(myName) Then
            KillApp = True
            appCount = appCount + 1
            myProcess = OpenProcess(PROCESS_ALL_ACCESS, False, uProcess.th32ProcessID)
            AppKill = TerminateProcess(myProcess, exitCode)
            Call CloseHandle(myProcess)
        End If


        rProcessFound = ProcessNext(hSnapshot, uProcess)
    Loop


    Call CloseHandle(hSnapshot)
    Finish:
End Function

'-----------------------------


'>to test it, Load up something eg: c:\windows\notepath.exe. Then call:

KillApp "c:\windows\notepad.exe"


Thanks,
LeXien
0
 

Author Comment

by:techconcepts
ID: 2740122
LeXien,

Your code worked excellent!!! It even appears that I don't need to specify the entire path to the exe to kill the application (which is a great feature). I dont have NT installed on this system, How will this work for me in the NT environment? If this doesnt work for NT, can you advise the alternative code to handle that? If you need me to open another question with points, I could do that.

Thanks to everyone else for your help, Lexien's code is what I was looking for!! The find window function is a good one for the windows you can predict the caption of, but if someone saves a word document and then opens it directly, you can't know exactly what their caption would say. Thanks again...
0
 
LVL 28

Expert Comment

by:AzraSound
ID: 2740128
yes but the caption still states the words Microsoft Word in it...
As I mentioned before, the snapshot functions do not work with winNT
0
 

Expert Comment

by:LeXien
ID: 2740135
thanks!

this code will work in Win95, Win98 and Windows 2000. I don't think it will work on NT4.

If you want to make it work on NT4, which there isn't actually much point, because Windows 2000 is now the norm, you will need to use EnumProcesses API. I don't actually have a sample for this, but someone else might.
0
 
LVL 28

Expert Comment

by:AzraSound
ID: 2740141
I disagree with the fact that Windows 2000 is "the norm".  Besides, you never want to make assumptions like "no one will ever use this who will be using NT". Thats just being lazy to try and work out an idea that will be functional across all platforms.
0
 

Author Comment

by:techconcepts
ID: 2742088
AzraSound,

I agree with you that win200 isn't the norm.. I do need the alternative to provide customers that do have NT. There is a vast market that have that  currently and will have that for years to come. As I explained before, you can never be certain that the name of the app will be in the caption. We as VB programmers know that we can make the caption read whatever we care to. I will at times have to shut down programs that I cannot predict what their captions will be. Thats why shutting them down by their exe file is a better choice for me. If you have any ideas similar to LeXien's solution but for NT, I would be perfectly happy to open a new question posting points for you for your help.

Thanks
0
 
LVL 28

Expert Comment

by:AzraSound
ID: 2743102
lexien mentioned EnumProcesses...here is code that will show you every process running (including those you had no idea were running) and their associated handle.  i also provided buttons to kill each process so that you can see how it works.  perhaps it can be applied to your app.  and it is compatible with all platforms (32-bit)


'KILL ANY PROCESS
'ADD FOUR COMMAND BUTTONS TO THE FORM AND A LIST BOX
'************************************************************************
'
'MODULE CODE
'
'************************************************************************

Public Const WM_CLOSE = &H10

Public TitleCnt As Long
Public TopFndCnt As Long
Public ChildFndCnt As Long
Public TopDispCnt As Long
Public ChildDispCnt As Long

Declare Function EnumWindows Lib "user32" (ByVal lpEnumFunc As Long, ByVal lParam As Long) As Long
Declare Function EnumChildWindows Lib "user32" (ByVal hWndParent As Long, ByVal lpEnumFunc As Long, ByVal lParam As Long) As Long
Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hwnd As Long, ByVal lpString As String, ByVal cch As Long) As Long
Declare Function PostMessage Lib "user32" Alias "PostMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Public Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hwnd As Long, lpdwProcessId As Long) As Long


Public Function EnumWinProc(ByVal lhWnd As Long, ByVal lParam As Long) As Long
    Dim retval As Long
    Dim WinTitleBuf As String * 255
    Dim WinTitle As String

    retval = GetWindowText(lhWnd, WinTitleBuf, 255)
    WinTitle = Trim$(StripNulls(WinTitleBuf))
    If WinTitle > " " Then
        Form1.List1.AddItem lhWnd & " " & WinTitle
        Form1.List1.ItemData(Form1.List1.NewIndex) = lhWnd
        TopDispCnt = TopDispCnt + 1
    End If
    TitleCnt = TitleCnt + 1
    TopFndCnt = TopFndCnt + 1

    retval = EnumChildWindows(lhWnd, AddressOf EnumChildProc, lParam)
    EnumWinProc = True
End Function
Public Function EnumChildProc(ByVal lhWnd As Long, ByVal lParam As Long) As Long
    Dim retval As Long
    Dim WinTitleBuf As String * 255
    Dim WinTitle As String
    Dim s As String
    Dim bHide As Boolean

    retval = GetWindowText(lhWnd, WinTitleBuf, 255)
    WinTitle = Trim$(StripNulls(WinTitleBuf))

    If WinTitle > " " Then
        Form1.List1.AddItem lhWnd & " " & WinTitle
        Form1.List1.ItemData(Form1.List1.NewIndex) = lhWnd
        ChildDispCnt = ChildDispCnt + 1
    End If

    ChildFndCnt = ChildFndCnt + 1
    EnumChildProc = True
End Function
Public Function StripNulls(OriginalStr As String) As String
    ' This removes the extra Nulls so String comparisons will work
    If (InStr(OriginalStr, Chr(0)) > 0) Then
        OriginalStr = Left(OriginalStr, InStr(OriginalStr, Chr(0)) - 1)
    End If
    StripNulls = OriginalStr
End Function


'************************************************************************
'
'FORM CODE
'
'************************************************************************

Dim Scanning As Boolean

Private Sub Command2_Click()
    Dim retval As Long
    Dim pos As Long
    Dim hwnd As Long
    pos = InStr(1, List1.List(List1.ListIndex), " ", vbBinaryCompare)
    hwnd = Left(List1.List(List1.ListIndex), pos)
    retval = PostMessage(hwnd, WM_CLOSE, 0&, 0&)
End Sub

Private Sub Command3_Click()
    Scan
End Sub

Private Sub Command4_Click()
    Unload Me
End Sub


Private Sub Form_Load()
    Command1.Caption = "Minimize"
    Command2.Caption = "End Task"
    Command3.Caption = "Refresh"
    Command4.Caption = "Close"
    Scan
End Sub

Private Sub Scan()
    If Scanning Then Exit Sub
    Scanning = True
   
    List1.Clear
    TopFndCnt = 0
    TopDispCnt = 0
    ChildFndCnt = 0
    ChildDispCnt = 0
    TitleCnt = 0
   
    EnumWindows AddressOf EnumWinProc, 0

    If List1.ListCount > 0 Then List1.ListIndex = 0
    Scanning = False
End Sub



if it lists too many unwanted processes then you can remove the function that enumerates all the child processes as well.



0
 
LVL 28

Expert Comment

by:AzraSound
ID: 2743110
to better demonstrate which processes are child ones in the list, just add some spaces in front of the line that adds them to the list, i.e.



Public Function EnumChildProc(ByVal lhWnd As Long, ByVal lParam As Long) As Long
    Dim retval As Long
    Dim WinTitleBuf As String * 255
    Dim WinTitle As String
    Dim s As String
    Dim bHide As Boolean

    retval = GetWindowText(lhWnd, WinTitleBuf, 255)
    WinTitle = Trim$(StripNulls(WinTitleBuf))

    If WinTitle > " " Then
        Form1.List1.AddItem "     " & lhWnd & " " & WinTitle
        Form1.List1.ItemData(Form1.List1.NewIndex) = lhWnd
        ChildDispCnt = ChildDispCnt + 1
    End If
....
....



   
0
 

Author Comment

by:techconcepts
ID: 2743777
AzraSound,

Is there a way to get the exe file name from this technique?
0
 
LVL 28

Expert Comment

by:AzraSound
ID: 2744069
i'd have to look into it...dont know off the top of my head
0

Featured Post

6 Surprising Benefits of Threat Intelligence

All sorts of threat intelligence is available on the web. Intelligence you can learn from, and use to anticipate and prepare for future attacks.

Join & Write a Comment

Introduction I needed to skip over some file processing within a For...Next loop in some old production code and wished that VB (classic) had a statement that would drop down to the end of the current iteration, bypassing the statements that were c…
Most everyone who has done any programming in VB6 knows that you can do something in code like Debug.Print MyVar and that when the program runs from the IDE, the value of MyVar will be displayed in the Immediate Window. Less well known is Debug.Asse…
Get people started with the process of using Access VBA to control Excel using automation, Microsoft Access can control other applications. An example is the ability to programmatically talk to Excel. Using automation, an Access application can laun…
Get people started with the utilization of class modules. Class modules can be a powerful tool in Microsoft Access. They allow you to create self-contained objects that encapsulate functionality. They can easily hide the complexity of a process from…

758 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

19 Experts available now in Live!

Get 1:1 Help Now