More Undocumented WinHelp Macros

Jim Mischel

This article first appeared in Windows/DOS Developer's Journal, December 1994

My article, "Undocumented WinHelp Macros", which appeared in the January 1994 issue of Windows/DOS Developer's Journal, revealed a number of useful but undocumented macros and identifiers that exist in Windows 3.1 WINHELP.EXE. In that article, I solicited information from readers, and many of you responded. With your input, and some further experimentation, I've discovered some further information about some of those macros, and a few other tricks that might come in handy.

Command() Revealed

In the January article, I mentioned that I was unable to determine the use of the Command() and Generate() macros. Shortly after the magazine was published, I got a letter from Tom Gibson, a reader in Houston, TX. Tom writes:

I just read your article on undocumented WinHelp macros. It's nice to see that I'm not the only one who has ever heard of these. Microsoft support people have been no help in trying to decipher the macros. The ones that stumped me also seem to have given you some problems; however, after reading the article, I sat down and cracked one of them. The Command() macro activates a menu command from WinHelp. The macro takes as a parameter the WM_COMMAND number, which I've listed below:

He then lists the command values, which I've duplicated in Table 1. I'm not sure how Tom discovered this (I always like to see people's methods, hoping that I can duplicate them in the future), but the information certainly is useful. Further experimentation revealed that items added to menus are assigned numbers, starting with 10004, in the order in which they are added. So, if you were to add five items to WinHelp menus (either to the standard menus, the floating menu, or new menus), those items would be assigned numbers 10004, 10005, 10006, 10007, and 10008, in the order in which you add them.

It's not clear to me why this macro even exists. All of the standard WinHelp menu commands have corresponding macros, and it doesn't seem any easier to write Command(1101), than FileOpen(). The only reasonable explanation that I can come up is that Command() makes it possible for your help files to execute custom menu commands that you've added. Without Command(), you'd have to create a custom macro in order to activate your menu commands.

Menu Item Command Number
File|Open 1101
File|Print Topic 1103
File|Print Setup 1104
File|Exit 1105
Edit|Copy 1203
Edit|Annotate 1202
Bookmark|Define 1301
Bookmark|More 1302
Help|How to Use Help 10003
Help|Always on Top 10002
Help|About 1503
Defined Bookmarks 1303, 1304, etc.
Custom menu items 10004, 10005, etc.

Table 1 -- command-number values for the Command() macro

Generate() Demystified

Shortly after my book was published, I mentioned on the CompuServe WINSDK forum that I still hadn't figured out what the Generate() macro does. I got a number of responses to that query, the first from John Bradnam. He writes:

Command(u) just invokes the Generate(uUU) macro.

The first parameter in the Generate() macro is set to WM_COMMAND, i.e. 111h. The second is the u parameter from the Command() macro converted to a long. The third parameter is always 0.

So, Command(u) is just a special case for the macro Generate(), which posts or sends a message to the main help window. The message is sent if it is greater than WM_USER+100 (464h). Otherwise, it is posted. So Command() is just Generate(wMsg, dParam1, dParam2) and equates to:

procedure Generate(wMsg:THandle;dparam1:longint;dParam2:longint);
begin
if wMsg > WM_USER+100 then
SendMessage(hHelpWnd,wMsg,LOWORD(dParam1),dParam2)
else
PostMessage(hHelpWnd,wMsg,LOWORD(dParam1),dParam2);
end;

So Generate() gives you a way to send Windows messages directly to the help window. You could play some interesting games sending messages to the help window with Generate(). I'd be interested in hearing from you if you come up with a good use for this macro.

More Info on ExtInsertItem() and ExtInsertMenu()

As I pointed out in the January article, the ExtInsertItem() and ExtInsertMenu() macros work exactly like their documented counterparts, InsertItem() and InsertMenu(), except the Ext... versions accept one more parameter, which I called enabled-state. In my article, I said that the enabled-state parameter should be 0 to enable the menu or item, or 1 to disable it. That's true as far as it goes, but I learned of a number of other values for this parameter from another reader, Paul A. O'Rear, with whom I traded a number of CompuServe messages. Paul writes:

The last last parameter to the ExtInsertItem() and ExtInsertMenu() undocumented macros is more broad than you imply in your docs and help file. Initially, I thought that it was limited to three possibilities:
0x0000 MF_ENABLED
0x0001 MF_GRAYED
0x0002 MF_DISABLED
The third possibility at least is missing from your docs. It provides a normal looking, but non-functional menu item. After I played around with them all day though, I found out that you can use almost all of the MF_ identifier values to accomplish more wonderful things. For instance:
0x0008 MF_CHECKED Checks the item
0x0020 MF_MENUBARBREAK Creates a two column menu
0x0040 MF_MENUBREAK Creates a two column menu without a vertical bar
0x0080 MF_HILITE Hilights the inserted item, but does not give it the focus
0x0800 MF_SEPARATOR Inserts a menu separator
Each of these are able to be used with each other and the previous three:

0x0029 MF_MENUBARBRAEK | MF_CHECKED | MF_GRAYED

0x0004 is the MF_BITMAP identifier. When you try to use this it seems as if WinHelp actually tries to load a bitmap, but doesn't know where to find it. If there were a way to pass a pointer to a bitmap in the functions, I'm sure it would display it. These could then also be used in conjunction with the MF_ENABLED, MF_MENUBAR, MF_CHECKED, and other values.

For some reason, ExtAbleItem() only accepts the first three parameter values: MF_ENABLED, MF_GRAYED, and MF_DISABLED. It ignores all other values.

Paul then provides a table of identifiers and combinations that is quite useful if you want to experiment with adding menus and menu items.. This information is reproduced in Table 2.

Value MF_xxx Constant Description
0x0000 MF_ENABLED Creates an enabled menu item
0x0001 MF_GRAYED Creates a grayed menu item
0x0002 MF_DISABLED Creates a disabled menu item
0x0004 MF_BITMAP Not useable in WinHelp
0x0008 MF_CHECKED Creates a checked menu item
0x0020 MF_MENUBARBREAK Creates a two-column menu
0x0040 MF_MENUBREAK Creates a two-column menu without a vertical bar
0x0080 MF_HILITE Creates a highlighted menu item
0x0100 MF_OWNERDRAW Not useable in WinHelp
0x0200 MF_USECHECKBITMAPS Not useable in WinHelp
0x0400 MF_SEPARATOR Creates a menu separator
0x1000 MF_REMOVE No visible effect in WinHelp
0x2000 MF_SYSMENU No visible effect in WinHelp
0x4000 MF_HELP No visible effect in WinHelp
0x8000 MF_MOUSESELECT No visible effect in WinHelp

Table 2 -- enabled-state parameters for ExtInsertItem() and ExtInsertMenu()

The SeqTopicKeys WIN.INI Variable

In my book, I mentioned all of the WinHelp WIN.INI variables that I knew of at the time. Shortly thereafter, I found a reference to SeqTopicKeys, and discovered its use. In the [Windows Help] section of WIN.INI, setting the SeqTopicKeys variable to 1 allows you to single-step through all of the topics in a Windows Help file (including popups). Pressing Ctrl+Shift+Right arrow moves to the next topic, and Ctrl+Shift+Home moves to the first topic in the help file. Ctrl+Shift+Left arrow doesn't do anything, but you can move backwards through the topics that you've viewed by pressing the WinHelp Back button.

There is an annoying bug with SeqTopicKeys. When you press Ctrl+Shift+Right arrow on the last topic, you get a blank topic. Pressing Ctrl+Shift+Right again results in a "Help topic does not exist" error message from WinHelp. This message will be displayed twice. I don't know of any way around this error.

Undocumented Macros and Windows 4.0

According to information that was posted by Microsoft on section 16 of the WINSDK forum on CompuServe, WINHELP 4.0 will support all of the macros and identifiers that were supported by WINHELP 3.1--including the undocumented macros and identifiers that I and others have discovered. I also understand that all of these previously undocumented features (and any new features) will now be fully documented, which makes my job as a help developer much easier, but robs me of article opportunities.

Your Turn, Again

As before, I solicit your input. With an application as complex as WinHelp, it's unlikely that we've discovered everything that it can do. If you or somebody that you know has discovered an undocumented feature, please leave me a message me on CompuServe, or contact me through the magazine.