You need to start up other Windows programs from within your Access application?for instance, to send data to Excel or to format a report in Word. If you just use the Shell command to start these programs, you may end up with multiple instances of the application. How can you tell if an application is already running before you attempt to start it?
There are a number of solutions to this problem, and none, unfortunately, are as easy as you might like. To ask Windows whether Excel is currently running and receive an answer, you must know the Windows class name for the main window of the application. This solution explains the format of the question and how to ask it. In addition, it demonstrates how to switch to a running application from your Access application.
If you have code that interacts with applications external to Access, it is often useful to be able to determine whether the application is running. The sample form, frmAppsRunning (Figure 11-10), asks Windows the question, "Is this app running?" for each of six predefined window classes, and you can add one more of your own. For each application that frmAppsRunning finds, it fills in the window handle (hWnd) column and the window caption column on the form. The AppActivate command in Access requires that you know the exact title of the window, so this form uses code from Chapter 4 (in basAccessCaption) to retrieve the caption for each running application. Finally, you can click on any of the enabled buttons in the righthand column to switch to the running application.
Try the sample form with Microsoft applications you have installed. Press F1 to bring up Help, and then switch back to Access and click on the Search button on the sample form. This will reinitiate the search for active applications, and it will find WINHELP.EXE running. Click on the question-mark icon to switch back to WinHelp.
Follow these steps to include this functionality in your own applications:
Import the modules listed in Table 11-9 from 11-08.MDB into your application.
Module |
Contains |
---|---|
basCaption |
acbGetAccessCaption, acbGetWindowCaption, acbSetAccessCaption |
basUsage |
acbIsAppLoaded |
To be able to ask Windows the question, "Is some application running?", you'll need to know the Windows class name for the main window of the application. Table 11-10 lists the names for several Windows applications.
Application |
Class name |
---|---|
Access (all versions) |
OMain |
Excel (all versions) |
XLMain |
Explorer |
ExploreWClass |
Outlook (all versions) |
rrctrl_renwnd32 |
Notepad |
Notepad |
PowerPoint 2003 |
PP11FrameClass |
WordPad |
WordPadClass |
Microsoft Word (all versions) |
OpusApp |
The class names are somewhat arbitrary. Because they're assigned by the development staff, not by the marketing or documentation departments, class names often reflect the project's code name or the state of mind of the developer.
Finding Class NamesThere are many ways to find the class names for applications' main windows. The simplest is to use the sample form for the Solution in Recipe 11.9, which displays a list of open windows and their class names. If you want to know the class name for a specific application, open it and run the sample form. The second column will list the class name for you. |
To check whether a given application is currently running, use the acbIsAppLoaded function in basUsage. Pass a class name to this function as a parameter, and it returns the window handle of the application if it's running, or 0 if it's not. For example, this will return a nonzero value if Microsoft Word is currently running (note that the class names are not case-sensitive):
hWnd = acbIsAppRunning("opusapp")
Once you know the window handle for the application, you can use the AppActivate command in Access to make that application active. To do this, you'll need to know the exact window caption. To make that easier, you can call the acbGetWindowCaption function in basCaption before attempting to activate the application. For example, this code will switch to Excel, if it's running:
Dim hWnd as Integer hWnd = acbIsAppLoaded("XLMain") If hWnd <> 0 Then AppActivate acbGetWindowCaption(hWnd) End If
If the application you want to activate isn't currently running (acbIsAppLoaded returned 0), use the Shell command to start it. In this case, you'll need to know the DOS executable filename for the given application (EXCEL.EXE, for example). The example form doesn't attempt to load the applications if they aren't already loaded, but your own application can load the program as needed.
The acbIsAppLoaded function couldn't be simpler: It calls a single Windows API function. The entire routine looks like this:
Function acbIsAppLoaded (ByVal varClassName As Variant) As Long If IsNull(varClassName) Then acbIsAppLoaded = 0 Else acbIsAppLoaded = acb_apiFindWindow(CStr(varClassName), 0&) End If End Function
This routine allows you to pass in a class name. If the class name isn't null, the function calls the FindWindow API function (aliased as acb_apiFindWindow), which takes a class name and returns the window handle of the first instance of that class it finds. acbIsAppLoaded returns that handle to its caller.
This example uses the following code from basCaption to determine the caption of a window, given its window handle. Although this code isn't the focus of this section, you'll need to include it if you want to find a window's caption.
Declare Function acb_apiSetWindowText Lib "user32" _ Alias "SetWindowTextA" (ByVal hwnd As Long, _ ByVal lpString As String) As Long Declare Function acb_apiGetWindowText _ Lib "user32" Alias "GetWindowTextA" (ByVal hwnd As Long, _ ByVal lpString As String, ByVal aint As Long) As Long Public Function acbGetWindowCaption(ByVal hwnd As Long) As Variant ' Get any window's caption, given its hWnd. Dim strBuffer As String Dim intLen As Integer Const acbcMaxLen = 128 If hwnd <> 0 Then strBuffer = Space(acbcMaxLen) intLen = acb_apiGetWindowText(hwnd, strBuffer, acbcMaxLen) acbGetWindowCaption = Left$(strBuffer, intLen) End If End Function
Don't expect acbIsAppLoaded to distinguish between multiple copies of the same application. That is, if you have two copies of Notepad running, you can't count on acbIsAppLoaded to return the handle to a specific instance of Notepad: it will return the handle of the first instance it comes across. But that shouldn't bother you, as you're simply trying to find out if any copy of the application is currently running.