Recipe 11.10 Close a Running Windows Application
11.10.1 Problem
As part of some of your large
Access applications, you often allow users to start other Windows
tools (Notepad, Calculator, Calendar, etc.); once those tools are
open, your application doesn't touch them. Some
users have complained about all the
"junk" left over once your
application closes. Is there some way you can close another window
from your Access application? That way, on the way out you can close
any tools your application has opened.
11.10.2 Solution
Recipe 11.9 demonstrated the retrieval of a list of all
the running Windows applications' captions, class
names, and window handles. Once you know that information,
it's easy to close an application: given a window
handle, simply tell it to close. Using the Windows API PostMessage
function, you can close any window at any time. Of course, some
applications (those that support Automation; see Chapter 12 for more information) allow themselves to
be closed programmatically without using the Windows API. Other
applications that don't support Automation will
require either the API method described here, or
SendKeys, which is unreliable at best.Load and run frmListWindows from 11-10.MDB . This
form, shown in Figure 11-12, is similar to the sample
form in the Solution in Recipe 11.9 with the
addition of the Stop App button, which lets you close the selected
window. Try a few; you can even close Access this way, if you want.
Figure 11-12. frmListWindows includes a Stop App button
|
steps:
- Import the modules basWindowList (if you
haven't already for the Solution in Recipe 11.9) and
basCloseWindows. - Follow the steps listed in the Solution in Recipe 11.9 to create and fill in the array of top-level
windows. - Decide which window you want to close. Windows sometimes appends
document names to the application name (e.g.,
"Microsoft Word11-10.DOC"),
so check against just the first portion of the window name in your
array. For example:For intI = 0 To intCount - 1
If Left$(atypWindowList(0).strCaption, 14) = "Microsoft Word" Then
' You found a match. Do something.
End If
Next intI - When you've found the item you want to close, use
the acbCloseWindow function, passing to it the handle of the window
you care about:If acbCloseWindow(atypWindowList(intI).hWnd) = 0 Then
' If you got 0 back, it got the message!
End If
11.10.3 Discussion
The acbCloseWindow function calls the
PostMessage API function. By posting a message to a particular
window, you are telling it to do something, but you
don't bother waiting for a response. (The
corresponding API function, SendMessage,
does cause you to wait for a response. You can
use SendMessage if you want to stop and wait for
the other application to close, but we don't
recommend it.) The acbCloseWindow function sends the
WM_CLOSE message to your chosen window, telling it
to shut down. It's as if you quit your Windows shell
program with some applications running. Your shell sends a message to
each main application window to shut down because Windows is shutting
down. The acbCloseWindow function, then, looks like this:
Function acbCloseWindow (ByVal hWnd As Long)
Const WM_CLOSE = &H10
acbCloseWindow = PostMessage(hWnd, WM_CLOSE, 0, vbNullString)
End Function
The purpose of this wrapper function that calls
PostMessage is to prevent you from having to
remember how to post a message to a window. It's a
lot simpler to call acbCloseWindow than to call
PostMessage directly.Sending a WM_CLOSE message to a window
doesn't necessarily close it. If that application
has an unsaved document, it will pop up its own dialog asking what
you want to do with that unsaved document. In the sample form, if
this happens, the list box won't be updated
correctly. Once you return from your duties with the foreign
application, press the Requery button on the form to force it to
search again for all open applications.