12.5 Add an Item to the Startup Group
12.5.1 Problem
As part of your application, you would like to be able to
allow users to add an application to the Startup menu so that your application
will start up when Windows does. You just can't figure out how to put the
information into the Startup group. Is there a way to communicate between Access
and the Windows shell so you can do this?
12.5.2 Solution
This is a case where the old technology called DDE comes in
handy. The Windows shell accepts commands using DDE that allow you to create and
delete groups and items. You can also retrieve lists of existing groups and
items within those groups. This solution explains most of the Windows shell's
DDE interface.
To test out the DDE interface, load and run the form frmShell
from 12-05.MDB. This form, shown in Figure 12-7, allows you to view
groups and their items, create and delete groups and items, and display a
particular group. It will decide whether to use the group/item or the
folder/shortcut terminology after determining whether you are using the Windows
9x shell or the Windows NT/Windows 2000 Program Manager, respectively.
|
Figure 12-7. frmShell allows you to communicate with
the Windows shell via DDE
Once you select a group from the list on the left in Figure
12-7, the form will display the group's items in the list on the right. If you
select the first item in the righthand list梩he group itself梩he form will
display the information Windows stored about that group. Once you've selected a
group in the righthand list box, you can click the Show button to have Windows
display that group. The code attached to the Show button requests Windows to
open the group window using style 3 (see Table 12-8 for a list of window
styles). As described later, in the sidebar Switching Focus," Windows may grab
the focus, depending on the previous state of the group window you've selected.
|
Select an item in the group (any row except the first in the
righthand list box), and the form will display all the information that Windows
stores about that item. Figure 12-8 shows frmShell with an item selected.
Figure 12-8. frmShell with a group selected and its
information displayed
With either a group or an item selected, you can create or
delete a group or an item. If you've selected a group, pressing the Delete
button will instruct Windows to delete that group; if you've selected an item,
Windows will delete that item. Regardless of what's selected, pressing the
Create button will pop up a dialog asking whether you want to create a new item
or a new group. Either choice will pop up the appropriate dialog requesting the
necessary information.
The following sections describe how to use the sample forms
in your own applications, and then explain most of the DDE interface to the
Windows shell. Although more DDE options are available, the most useful tasks
can be accomplished with the tools provided here.
12.5.2.1 Using the sample forms
To include the sample forms from 12-05.MDB in your own
applications, follow these steps:
Import the objects shown in Table 12-3 into your
application.
Table 12-3. Objects to import from 12-05.MDB
Object
Name
Purpose
Form
frmNew
Choose new group or new item
Form
frmNewGroup
Enter new group information
Form
frmNewItem
Enter new item information
Form
frmShell
Main form
Module
basShell
Perform DDE conversations with Windows shell
Module
basSortArray
Sort arrays (list of program groups)
Module
basToken
Pull apart strings (item and group information on
frmShell)
Load and run frmShell.
As described previously, you can use the form to manipulate
shell groups and items from your Access application. If you want to use only
some parts of frmShell in your application instead of the whole thing, that's
fine too. If you use the group list (lstGroups), you'll also need to include
the function that fills it, FillGroups. If
you want the item list (lstItems), you'll also need
FillItems. In addition, place code in lstGroup's AfterUpdate event that
requeries lstItems once you've made a selection in lstGroups. You'll end up
with an event procedure like this:
Private Sub lstGroups_AfterUpdate ( )
Me!lstItems.Requery
End Sub
To use other bits and pieces of the functionality of
frmShell, you'll need to investigate its form module.
12.5.2.2 Using DDE with the Windows shell
If your main interest is simply to use DDE to control the
Windows shell, follow these steps:
Import the module basShell from 12-05.MDB into your
own application. This module is completely self-contained and includes a
number of functions that will set up the DDE conversation, do the work or
retrieve the information you need, and then terminate the conversation.
Because we've hidden all the details of the DDE, you needn't worry about
getting all the syntax and parameters correct.
Depending on your needs, call one or more of the wrapper
procedures described in Table 12-4. All of these functions are covered in
detail in Section 12.4.3 (see Table 12-9).
Table 12-4. Procedures in basShell to aid in using
DDE between Access and Windows shell
Procedure
Purpose
acbPMCreateGroup
Create a group, given a group name and a pathname for
the group file.
acbPMCreateItem
Create a new item, given the group name, the item name,
the command line, the default directory, and whether or not to run the
application minimized.
acbPMDeleteGroup
Delete a group, given the name of the group to delete.
acbPMDeleteItem
Delete an item from a group, given the name of the
group and the name of the item.
acbPMGetGroups
Fill a dynamic array with all the groups.
acbPMGetItems
Fill a dynamic array with all the items for a
particular group.
acbPMShowGroup
Show a particular group, given the name of the group
and the window mode to use.
acbPMShowMessages
Allow callers outside this module to show or hide
messages. Pass in True to show messages, False to hide them (no DDE
involved).
12.5.3 Discussion
The Windows shell supports two operations: you can either
request information using the DDERequest
function (Table 12-5 lists the DDERequest
items) or execute actions using the DDEExecute
subroutine (Table 12-6 lists the most useful subset of the shell's
DDEExecute command-string interface). DDE
conversations between Access and the shell involve three steps:
Initiate the conversation.
Perform the necessary tasks.
Terminate the conversation.
12.5.3.1 Retrieving information from the Windows
shell
Table 12-5 describes the two groups of information you can
request from Windows. The sample form, frmShell, uses both to fill its two list
boxes.
To retrieve | Program | Topic | Item | Returns |
---|---|---|---|---|
List of groups | PROGMAN, or Folders | PROGMAN, or AppProperties | PROGMAN | List of existing groups, separated with CR/LF pair |
List of items in a group | PROGMAN, or Folders | PROGMAN, or AppProperties | <Group Name> | List of items in the specified group, separated with |
To retrieve a list of groups from Windows using the Access
DDERequest function, you must first initiate a
conversation with the PROGMAN program on the PROGMAN topic,
requesting information on the PROGMAN item; even if you use the
undocumented "Folders" program name and "AppProperties" topic, it still expects
you to request information on the PROGMAN item. The
DDERequest call returns a
carriage-return/line-feed (CR/LF) delimited string of group names. It's up to
your code to pull apart the list of groups and place them into whatever data
structure is most convenient for you. To simplify this task, you can use the
acbPMGetGroups function in basShell. It
accepts, as a parameter, a dynamic array to fill in with the list of groups.
This function performs the DDERequest for you
and calls the private CopyToArray function to
break apart the returned stream of groups and fill the array you've sent it. It
returns the number of items in the array. Its source code is:
Public Function acbPMGetGroups(avarGroups( ) As Variant)
' Fill a dynamic array with all the Program Manager groups.
Dim lngChannel As Long
Dim strGroups As String
Dim intCount As Integer
On Error GoTo HandleErr
' Most replacement shells will start PROGMAN for you if you attempt
' to start up a DDE conversation with it. That is, you won't need
' to Shell( ) PROGMAN if you're using a replacement shell.
lngChannel = DDEInitiate("PROGMAN", "PROGMAN")
strGroups = DDERequest(lngChannel, "PROGMAN")
intCount = CopyToArray(strGroups, avarGroups( ))
ExitHere:
acbPMGetGroups = intCount
On Error Resume Next
DDETerminate lngChannel
On Error GoTo 0
Exit Function
HandleErr:
MsgBox Err.Number & ": " & Err.Description, , "acbGetProgmanItems"
Resume ExitHere
End Function
To call this function from your own code, use code like this:
Dim avarGroups( ) as Variant
Dim intCount as Integer
intCount = acbPMGetGroups(avarGroups( ))
' If you want the list sorted, call acbSortArray, in basSortArray.
acbSortArray avarGroups( )
To retrieve a list of items within a selected group, use the
acbPMGetItems function, which works almost
exactly as acbPMGetGroups does. This time,
however, pass in a group name along with the dynamic array to be filled in; the
function uses the group name as the topic, instead of PROGMAN (see
Table 12-5). It calls the CopyToArray function
to move the items into the dynamic array. You generally won't sort the array,
however, unless you store the first item; this first item returns information
about the group window itself. The rest of the rows contain information about
the individual items. To use acbPMGetItems, you
might use code like this:
Dim avarGroups( ) as Variant
Dim avarItems( ) as Variant
Dim intCount as Integer
intCount = acbPMGetGroups(avarGroups( ))
intCount = acbPMGetItems(avarGroups(0), avarItems( ))
' List all the item information for the specified group.
For intI = 0 To intCount - 1
Debug.Print avarItems(intI)
Next intI
12.5.3.2 Executing tasks
The Windows shell includes a command-string interface, which
you can access via DDE, that allows you to execute tasks involving groups and
items within those groups. Table 12-6 lists the functions addressed in this
solution. Other commands are available (they're documented in the Windows SDK
documentation), but they're not as useful for Access programmers.
Function | Parameters | Comments |
---|---|---|
AddItem | See Table 12-7 | Uses CreateGroup first |
CreateGroup | GroupName[, GroupPath] | Selects the group if it exists; otherwise, creates it |
DeleteGroup | GroupName | |
DeleteItem | ItemName | Uses CreateGroup first |
ShowGroup | GroupName, ShowCommand | See Table 12-8 for ShowCommand |
In each case, you use the Access
DDEExecute procedure to communicate with the shell. You must construct a
string containing the function name, parentheses, and any arguments for the
function. For example, to create a group from within Access, you can use code
like this:
Dim intChannel as Integer
intChannel = DDEInitiate("PROGMAN", "PROGMAN")
DDEExecute intChannel, "[CreateGroup(My Group, MYGROUP.GRP)]"
The command string must be surrounded by square bracket
delimiters ([]). Luckily, the Windows shell is far more relaxed about
the use of embedded quotes than almost any other DDE-enabled application. For
example, WinFax Pro's implementation of DDE requires quotes embedded in command
strings you send to it; the Windows shell accepts embedded quotes but doesn't
require them.
Some functions, such as AddItem,
allow quite a few parameters, almost all of which can be left blank (see Table
12-7). To use the AddItem command to add a new
item, you must first select a group in which to add the item. To do this, use
the CreateGroup command, which creates a group
if necessary or selects it if it already exists. The only required
AddItem parameter is the command line. Note
that both X- and Y-coordinates are necessary if you choose to specify
coordinates for the icon. For example, to create a new icon to run C:\EDIT\MYEDIT.EXE
with the description My Editor minimized in the My New Group group, use code
like this (you'd normally include error-handling code, too):
Dim intChan As Integer
intChan = DDEInitiate("PROGMAN", "PROGMAN")
' First select the group (or create it).
DDEExecute intChan, "[CreateGroup(My New Group)]"
' Use commas to delimit parameters (even missing ones).
DDEExecute intChan, "[AddItem(C:\EDIT\MYEDIT,My Editor,,,,,,1)]"
Parameter | Required? | Used in sample? | Description |
---|---|---|---|
CmdLine | Yes | Yes | Command line to run the application. Must be at least the |
Name | No | Yes | Name that appears below the icon in the group. |
IconPath | No | No | Name and path of the icon file to use. If an executable |
IconIndex | No | No | Index of the icon in the specified IconPath |
Xpos | No | No | X-position of the icon within the group, as an integer. |
Ypos | No | No | Y-position of the icon within the group, as an integer. |
DefDir | No | Yes | Default (or working) directory for the application. |
HotKey | No | No | Hot key for this application, stored as an integer. |
fMinimize | No | Yes | Run Minimized (1 = True, 0 = False). |
fSeparateMemSpace | No | No | In Windows NT only, run the application in a separate |
Switching FocusUsing the ShowGroup |
Window style value | Action |
---|---|
1 | Activate and display the group window. If it was |
2 | Activate the group window and display it as an icon. |
3 | Activate the group window and display it maximized. |
4 | Display the group window normalized and leave the current |
5 | Activate the group window and display it in its current |
6 | Minimize the group window. |
7 | Minimize the group window and leave the current group |
8 | Display the group window in its current placement and |
12.5.3.3 Using the wrapper procedures
To make your DDE programming simpler, the module basShell
includes wrapper procedures that handle all the details for you. (Table 12-4
provides a description of each of the wrapper procedures; Table 12-9 lists the
parameters.) The module also provides functions that handle each of the commands
described in Table 12-6. In some cases (AddItem,
for example), the wrapper functions don't allow you to specify all the possible
parameters for the command string. If you find these wrapper functions too
limiting, you can modify them so they allow you to pass in whatever parameters
you like.
All the wrapper procedures (except
acbPMShowMessages) in Table 12-9 perform the same set of steps to
communicate with the Windows shell. To simplify the code and centralize error
handling, those steps have been pulled into a single private procedure in
basShell, DDEExecutePM, which is shown in the
following code example:
Private Function DDEExecutePM(strCommand As String) As Boolean
' DDEExecute with the passed-in command. If it succeeds,
' return True. If it fails, return False.
' At this point, this function handles error messages itself.
' You could move this out of here to a higher level, if you
' want, by setting the SHOW_MESSAGES constant to False.
Dim lngChannel As Long
On Error GoTo HandleErr
lngChannel = DDEInitiate("PROGMAN", "PROGMAN")
DDEExecute lngChannel, strCommand
DDEExecutePM = True
ExitHere:
On Error Resume Next
DDETerminate lngChannel
On Error GoTo 0
Exit Function
HandleErr:
If Not mfHideMessages Then
MsgBox Err.Number & ": " & Err.Description, , "DDEExecutePM"
End If
DDEExecutePM = False
Resume ExitHere
End Function
Given a string to execute, this code initiates the DDE
channel, uses DDEExecute to execute the
command, and then terminates the connection. If all goes according to plan, the
procedure returns a True value. If an error occurs, it displays a
message box (unless you've used the acbPMShowMessages
procedure to disable warning messages) and then returns False.
Table 12-9 lists the parameters for the wrapper procedures in
basShell. Each of these procedures (except
acbPMShowMessages) returns True if the function succeeded, or
False if it failed. Unless you've called the
acbPMShowMessages subroutine to disable messages, a message will appear
before deleting a group or item or if any error occurs.
Procedure | Parameter | Data type | Parameter description |
---|---|---|---|
acbPMCreateGroup | varName | Variant | Name of the new group. |
varGroupPath | Variant | Name of the group file (can be Null, in which case | |
acbPMCreateItem | varGroup | Variant | Name of the group in which to create the new item. |
varName | Variant | Descriptive name for the new item; appears under the | |
varCommandLine | Variant | Command line to execute when this icon is chosen. Cannot | |
varDirectory | Variant | Default (working) directory when the application starts | |
varMinimized | Variant | Logical value: run the app minimized? | |
acbPMDeleteGroup | varName | Variant | Group to delete. |
acbPMDeleteItem | varGroup | Variant | Group from which to delete the item. |
varName | Variant | Name of the item to delete. | |
acbPMShowGroup | varName | Variant | Name of the group to show. |
intMode | Integer | Window mode, as listed in Table 12-8. | |
acbPMShowMessages | fShow | Integer | Logical value: display messages during DDE wrapper |
For example, to use the wrapper functions to add an icon to
the My Group group that will run C:\EDIT\MYEDIT.EXE minimized with the
description My Editor (as in the example that called
AddItem directly), you could use code like this:
Dim fSuccess As Boolean
' Disable error messages.
acbPMShowMessages False
fSuccess = acbPMCreateItem("My Group", "My Editor", _
"C:\EDIT\MYEDIT.EXE", Null, True)
If Not fSuccess Then MsgBox "Unable to create new item!"
This example also calls
acbPMShowMessages to disable error messages from within
acbCreateItem, so the code fragment itself can
handle them.
For examples of each of the wrapper functions, check out the
code in frmShell's module.
12.5.4 Comments
Though this solution covers a great deal more than the
original question required, all the information here will be useful to Access
programmers working with the DDE interface to the Windows shell.
The sample form, frmShell, is not only a good example of
using DDE to converse with Windows, it's also a useful tool on its own. Because
it allows you to see what's in each group without having to open and close each
group's window, it's a quick and easy way to clean out your groups. Of course,
some extra work would be required for it to be a really useful tool, but it's a
good start.
In 16-bit applications, DDEInitiate
returns a short integer (16-bit) handle. In Access 2002 (and other 32-bit
applications), this function returns a long integer (32-bit) handle. If you have
existing code that uses DDE, you'll want to convert the variables containing the
return values into long integers.
The Windows shell has an undocumented DDE application
topic pair that is not supported by the original Program Manager or any of the
major third-party shell substitutes: Folders
AppProperties. This syntax seems to be just an alias for the regularly
documented DDE interface, because the item name syntax and all the operations
are identical in both cases.
This undocumented syntax can be of some benefit. If you are
going to add the functionality to interact with the shell, you can use code like
the following to determine if your user is running the Windows 9x shell:
Public Function acbNewShell ( ) as Boolean
Dim lngChannel as Long
On Error Resume Next
lngChannel = DDEInitiate("Folders","AppProperties")
acbNewShell = (lngChannel <> 0)
DDETerminate lngChannel
End Function
You'll notice that the example uses this function (as well as
a public flag) to decide whether to call the various shell objects "groups" and
"items" (as in the Windows NT Program Manager) or "folders" and "shortcuts" (as
in the Windows 9x shell).
To shield you from the details of the DDE conversation and to
isolate the DDE code in one routine, each of the command-string replacement
functions calls the DDEExecutePM function. This
makes the code neat and easy to understand, but it does have a potential
disadvantage: calling DDEInitiate and
DDETerminate every time you call a wrapper
function adds substantial time and overhead to your application. If you make
many calls to Window via DDE, you'll want to reconsider this design. For most
applications, though, this shouldn't be a problem.
No comments:
Post a Comment