?
Solved

Closing a Program in VB Without knowing the exact window title

Posted on 2003-03-11
5
Medium Priority
?
175 Views
Last Modified: 2010-04-07
Hi,

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 INFINITE = &HFFFFFFFF
      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?
         DoEvents
         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."
         Else
            '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?

Thanks,
Tristan

                   

0
Comment
Question by:chaplt01
[X]
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
5 Comments
 
LVL 7

Accepted Solution

by:
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

Private 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

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
          Else
                 sClean = sClean & sAtom
          End If
      Next
      sDeighton = ""
      For g = Len(sClean) To 1 Step -1
          If Mid(sClean, g, 1) = "\" Then Exit For
             sDeighton = Mid(sClean, g, 1) & sDeighton
      Next
                                       
      If UCase(sDeighton) = UCase(ExeFind) Then
           glPid = Proc.th32ProcessID
           ExeRunning = True
           Exit Function
      End If
                       
      lRet = Process32Next(hProc, Proc)
   Loop
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&)
     Next
  End If
0
 

Author Comment

by:chaplt01
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?
0
 
LVL 4

Expert Comment

by:JRCSystems
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.
0
 
LVL 7

Expert Comment

by:Smallint
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.

Cheers
0
 

Author Comment

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

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