Closing a Program in VB Without knowing the exact window title

Posted on 2003-03-11
Medium Priority
Last Modified: 2010-04-07

I need to close another program from my vb app.  I was going to use this code ....

      Option Explicit

      Private Declare Function WaitForSingleObject Lib "kernel32" _
         (ByVal hHandle As Long, _
         ByVal dwMilliseconds As Long) As Long

      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, _
         ByVal lParam As Long) As Long

      Private Declare Function IsWindow Lib "user32" _
         (ByVal hwnd As Long) As Long

      Private Declare Function OpenProcess Lib "kernel32" _
         (ByVal dwDesiredAccess As Long, _
         ByVal bInheritHandle As Long, _
         ByVal dwProcessId As Long) As Long
      Private Declare Function GetWindowThreadProcessId Lib "user32" _
         (ByVal hwnd As Long, _
         lpdwProcessId As Long) As Long

      'Constants that are used by the API
      Const WM_CLOSE = &H10
      Const SYNCHRONIZE = &H100000

      Private Sub Form_Load()
         Command1.Caption = "Start the Notepad"
         Command2.Caption = "Close the Notepad"
      End Sub

      Private Sub Command1_Click()
      'Starts Windows Notepad
         Shell "notepad.exe", vbNormalNoFocus
      End Sub

*********** Important Part **********************************
      Private Sub Command2_Click()
      'Closes Windows Notepad
         Dim hWindow As Long
         Dim hThread As Long
         Dim hProcess As Long
         Dim lProcessId As Long
         Dim lngResult As Long
         Dim lngReturnValue As Long

         hWindow = FindWindow(vbNullString, "Untitled - Notepad")
         hThread = GetWindowThreadProcessId(hWindow, lProcessId)
         hProcess = OpenProcess(SYNCHRONIZE, 0&, lProcessId)
         lngReturnValue = PostMessage(hWindow, WM_CLOSE, 0&, 0&)
         lngResult = WaitForSingleObject(hProcess, INFINITE)

         'Does the handle still exist?
         hWindow = FindWindow(vbNullString, "Untitled - Notepad")
         If IsWindow(hWindow) = 1 Then
            'The handle still exists. Use the TerminateProcess function
            'to close all related processes to this handle. See the
            'article for more information.
            MsgBox "Handle still exists."
            'Handle does not exist.
            MsgBox "All Program Instances Closed."
         End If
      End Sub

Except my problem is that that the window doesn't always start with "Untitled".  I will never know what file the user has open, except that it will always end in  "- Notepad", and there will only ever be one window title ending in "- Notepad" (ie only one instance at anytime).

Does anyone know how to do this?



Question by:chaplt01
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 2
  • 2

Accepted Solution

Smallint earned 300 total points
ID: 8118049
This example closes all notepads running....

Private Declare Function Process32First Lib "kernel32" _
(ByVal hSnapshot As Long, lppe As PROCESSENTRY32) As Long

Private Declare Function Process32Next Lib "kernel32" _
(ByVal hSnapshot As Long, lppe As PROCESSENTRY32) As Long

Private Declare Function CreateToolhelp32Snapshot Lib _
"kernel32" (ByVal dwFlags As Long, ByVal th32ProcessID As Long) As Long

            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

Private Const TH32CS_SNAPPROCESS = &H2&
Private Const hNull = 0

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
' This callback function is called by Windows (from the EnumWindows
' API call) for EVERY top-level window that exists.  It populates a
' collection with the handles of all parent windows owned by the
' process that we started.
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
' The EnumWindows function enumerates all top-level windows
' by passing the handle of each window, in turn, to an
' application-defined callback function. EnumWindows
' continues until the last top-level window is enumerated or
' the callback function returns FALSE.
Call EnumWindows(AddressOf fEnumWindowsCallBack, hwnd)
End Function

Public Function ExeRunning(ByVal ExeFind As String) As Boolean
   Dim hProc As Long, ExeName As String
   Dim lRet As Long, Proc As PROCESSENTRY32
   Dim g As Long, sDeighton As String
   Dim sClean As String
   Dim sAtom As String
   hProc = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)
   ExeRunning = False
   If hProc = 0 Then
       Exit Function
   End If
   Proc.dwSize = Len(Proc)
   lRet = Process32First(hProc, Proc)
   Do While lRet
      ExeName = Left(Proc.szExeFile, Len(Proc.szExeFile) - 1)
      sClean = ""
      For g = 1 To Len(ExeName)
          sAtom = Mid(ExeName, g, 1)
          If sAtom = Chr(0) Then
                 Exit For
                 sClean = sClean & sAtom
          End If
      sDeighton = ""
      For g = Len(sClean) To 1 Step -1
          If Mid(sClean, g, 1) = "\" Then Exit For
             sDeighton = Mid(sClean, g, 1) & sDeighton
      If UCase(sDeighton) = UCase(ExeFind) Then
           glPid = Proc.th32ProcessID
           ExeRunning = True
           Exit Function
      End If
      lRet = Process32Next(hProc, Proc)
End Function

Dim i As Long
  If ExeRunning("NOTEPAD.EXE") Then
' Enumerate all parent windows for the process.
     Call fEnumWindows
' Send a close command to each parent window.
' The app may issue a close confirmation dialog
' depending on how it handles the WM_CLOSE message.
     For i = 1 To colHandle.Count
       glHandle = colHandle.Item(i)
       Call SendMessage(glHandle, WM_CLOSE, 0&, 0&)
  End If

Author Comment

ID: 8118145
Thanks, but I'm having some trouble with you code.

I'm using visual basic 6.0

Problem 1

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

These can't be public, according to the compiler.  So i change them to private.  Then ...

>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

These must become private.  Then  in ...

>Public Function fEnumWindows() As Boolean
>Call EnumWindows(AddressOf fEnumWindowsCallBack, hwnd)

"AddressOf fEnumWindowsCallBack" is invalid, presumable because "fEnumWindowsCallBack" is now private.

Any Suggestions?

Expert Comment

ID: 8118913
Everything listed above should work.
However, make your declarations in a BAS module.
NOT in a form.  Then the compiler won't bitch at you.

Also: Callback using AddressOf ONLY work with
      public routines found in BAS modules.

Expert Comment

ID: 8119785
JRCSystems is right.

Put all functions and declarations in a bas module.

Rest of code,

Dim i As Long

you can place it, for example in form_load of your main form.


Author Comment

ID: 8123991
Fantastic! Very much appreciated, and thanks to JRCSystems for the clarification.

Featured Post

Enroll in August's Course of the Month

August's CompTIA IT Fundamentals course includes 19 hours of basic computer principle modules and prepares you for the certification exam. It's free for Premium Members, Team Accounts, and Qualified Experts!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Have you ever wanted to restrict the users input in a textbox to numbers, and while doing that make sure that they can't 'cheat' by pasting in non-numeric text? Of course you can do that with code you write yourself but it's tedious and error-prone …
You can of course define an array to hold data that is of a particular type like an array of Strings to hold customer names or an array of Doubles to hold customer sales, but what do you do if you want to coordinate that data? This article describes…
Get people started with the process of using Access VBA to control Outlook using automation, Microsoft Access can control other applications. An example is the ability to programmatically talk to Microsoft Outlook. Using automation, an Access applic…
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…
Suggested Courses
Course of the Month10 days, 14 hours left to enroll

770 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