July, 2008

  • The Old New Thing

    The evolution of menu templates: 32-bit extended menus

    • 2 Comments

    At last we reach the 32-bit extended menu template. Introduced in Windows 95, this remains the most advanced menu template format through Windows Vista. As you might expect, the 32-bit extended menu template is just a 32-bit version of the 16-bit extended menu template, so if you've been following along, you should find no real surprises here; all the pieces have been telegraphed far in advance.

    The header remains the same:

    struct MENUHEADER32 {
     WORD wVersion;
     WORD cbHeaderSize;
     BYTE rgbExtra[cbHeaderSize-4];
    };
    

    The differences here from the 32-bit classic menu template header are analogous to the changes between the 16-bit classic menu template and the 16-bit extended menu template. The wVersion is set to one for extended templates, and the cbHeaderSize includes the wVersion and cbHeaderSize fields themselves, so the number of extra bytes is four less than the value specified in cbHeaderSize. There is one additional constraint: The cbHeaderSize must be a multiple of four because extended menu item templates must be aligned on DWORD boundaries. But as with 32-bit classic templates, the cbHeaderSize must be four in order to avoid a bug in the Windows 95 family of operating systems.

    After the header comes the menu itself, and like the 16-bit extended menu template, there is a prefix structure that comes before the items and which serves the same purpose as in the 16-bit extended menu template:

    struct MENUPREFIX32 {
     DWORD dwContextHelpID;
    };
    

    The list of menu items is basically the same as the 16-bit version, just with some expanded fields.

    struct MENUITEMEX32 {
     DWORD dwType;
     DWORD dwState;
     DWORD dwID;
     WORD  wFlags;
     WCHAR szText[]; // null terminated UNICODE string
    };
    

    As we saw before when we studied the 16-bit extended menu template, the big difference between classic and extended menu items is that classic menu items were designed for the InsertMenu function, whereas extended menu items were designed for the InsertMenuItem function. The dwType, dwState, and dwID members correspond to the fType, fState, and wID members of the MENUITEMINFO structure, and the the szText goes into the dwItemData if the item requires a string.

    One additional quirk of 32-bit extended menu item templates which the 16-bit version does not have is that 32-bit extended menu item templates must begin on a 32-bit boundary; therefore, you must insert a WORD of padding after the menu text if the text is an odd number of characters long. (Fourteen bytes of the fixed-length part of the MENUITEMEX32 plus an odd number of WCHARs plus the null terminator WCHAR leaves a value that is 2 mod 4; therefore, you need an additional WORD to return to a DWORD boundary.)

    The wFlags field has the same values as in the 16-bit extended menu item templates; the high byte is always zero. And, as before, if the bottom bit is set, then the menu item describes a pop-up submenu, which is inserted directly after the extended menu item template.

    That's all there is to it. Let's see how our example menu resource looks when converted to a 32-bit extended menu template:

    1 MENUEX 1000
    BEGIN
      POPUP "&File", 200,,, 1001
      BEGIN
        MENUITEM "&Open\tCtrl+O", 100
        MENUITEM "", -1, MFT_SEPARATOR
        MENUITEM "&Exit\tAlt+X",  101
      END
      POPUP "&View", 201,,, 1002
      BEGIN
        MENUITEM "&Status Bar", 102,, MFS_CHECKED
      END
    END
    

    First comes the header, whose contents are fixed:

    0000  01 00          // wVersion = 1
    0002  04 00          // cbHeaderSize = 4
    

    Before the list of extended menu item templates, we have the context help ID:

    0004  E8 03 00 00    // dwContextHelpID = 1000
    

    Since our first menu item is a pop-up submenu, the wFlags will have the bottom bit set:

    0008  00 00 00 00    // dwType = MFT_STRING
    000C  00 00 00 00    // dwState = 0
    0010  C8 00 00 00    // wID = 200
    0014  01 00          // wFlags = "pop-up submenu"
    0016  26 00 46 00 69 00 6C 00 65 00 00 00
                         // "&File" + null terminator
    0022  00 00          // Padding to restore alignment
    

    Notice the two bytes of padding so that we return to DWORD alignment.

    The wFlags promised a pop-up submenu, so here it is.

    0024  E9 03 00 00    // dwContextHelpID = 1001
    
    // First item
    0028  00 00 00 00    // dwType = MFT_STRING
    002C  00 00 00 00    // dwState = 0
    0030  64 00 00 00    // dwID = 100
    0034  00 00          // wFlags = 0
    0036  26 00 4F 00 70 00 65 00 6E 00 09 00
          43 00 74 00 72 00 6C 00 2B 00 4F 00 00 00
                         // "&Open\tCtrl+O" + null terminator
    
    // Second item
    0050  00 08 00 00     // dwType = MFT_SEPARATOR
    0054  00 00 00 00     // dwState = 0
    0058  FF FF FF FF     // dwID = -1
    005C  00 00           // wFlags = 0
    005E  00 00           // ""
    
    // Third (final) item
    0060  00 00 00 00     // dwType = MFT_STRING
    0064  00 00 00 00     // dwState = 0
    0068  65 00 00 00     // dwID = 101
    006C  80 00           // wFlags = "this is the last menu item"
    0070  26 00 45 00 78 00 69 00 74 00 09 00
          41 00 6C 00 74 00 2B 00 58 00 00 00
                          // "&Exit\tAlt+X" + null terminator
    0086  00 00          // Padding to restore alignment
    

    When we see the "end" marker, we pop one level back to the main menu.

    0088  00 00 00 00     // dwType = MFT_STRING
    008C  00 00 00 00     // dwState = 0
    0090  C9 00 00 00     // dwID = 201
    0094  81 00           // wFlags = "pop-up submenu" |
                          //          "this is the last menu item"
    0096  26 00 56 00 69 00 65 00 77 00 00 00
                          // "&View" + null terminator
    00A2  00 00          // Padding to restore alignment
    

    The set bottom bit in the wFlags indicates that another pop-up submenu is coming, and the "end" marker means that once the submenu is finished, we are done.

    00A4  EA 03 00 00    // dwContextHelpID = 1002
    
    00A8  00 00 00 00    // dwType = MFT_STRING
    00AC  08 00 00 00    // dwState = MFS_CHECKED
    00B0  66 00 00 00    // dwID = 102
    00B4  80 00          // wFlags = "this is the last menu item"
    00B6  26 00 53 00 74 00 61 00 74 00 75 00
          73 00 20 00 42 00 61 00 72 00 00 00
                         // "&Status Bar" + null terminator
    00CE  00 00          // Padding to restore alignment
    

    Since the pop-up submenu has only one item, the first item is also the last.

    That's it for the evolution of menu templates, starting from a series of calls to the ANSI version of InsertMenu to a series of calls to the Unicode version of InsertMenuItem. Menu templates get much less attention than dialog templates, but if you wanted to know how they work, well, there you have it.

  • The Old New Thing

    The evolution of menu templates: 16-bit extended menus

    • 12 Comments

    Windows 95 introduced a new menu format, known as "extended menus". You declare these in a resource file with the MENUEX keyword. The 16-bit extended menu is really just a temporary stopping point on the way to the 32-bit extended menu, since the 16-bit form is supported only by the Windows 95 family of operating systems. It's sort of the missing link of menu templates.

    Things start off the same as the 16-bit classic menu, with a structure I've been calling MENUHEADER16:

    struct MENUHEADER16 {
     WORD wVersion;
     WORD cbHeaderSize;
     BYTE rgbExtra[cbHeaderSize-4];
    };
    

    The version number for extended menus is one instead of zero, and the cbHeaderSize now includes the size of the wVersion and cbHeaderSize fields in the header size count; therefore, the number of interstitial bytes four less than the value specified by the cbHeaderSize member.

    Due to a bug in Windows 95 (and its descendants), the cbHeaderSize is ignored, and its value is assumed to be four. Fortunately, every version of the 16-bit resource compiler that supports 16-bit extended menu templates sets the cbHeaderSize to four. Consequently, nothing goes wrong in practice. And I suspect nobody has noticed this bug in the over fifteen years (not twenty-five as I had originally written) the code has been in existence.

    Unlike the classic menu, there is a prefix structure that comes before the list of menu items.

    struct MENUPREFIX16 {
     DWORD dwContextHelpID;
    };
    

    New to extended menus is the addition of context help IDs. These values can be set and retrieved programmatically with the GetMenuContextHelpId and SetMenuContextHelpId functions.

    The template then continues with a packed array of structures I will call MENUITEMEX16:

    struct MENUITEMEX16 {
     DWORD dwType;
     DWORD dwState;
     WORD  wID;
     BYTE  bFlags;
     CHAR  szText[]; // null terminated ANSI string
    };
    

    Whereas the members of the classic MENUITEM16 were designed to be passed to the function InsertMenu, the members of the extended MENUITEMEX16 were designed to be passed to the function InsertMenuItem. The dwType, dwState, and wID members correspond to the fType, fState, and wID members of the 16-bit MENUITEMINFO structure. Similarly, the szText goes into the dwItemData if the item requires a string. (If the item doesn't require a string, then the szText should be an empty string; i.e., should consist solely of the null terminator.)

    Notice that a new feature of extended menus is that pop-up menus can have IDs as well as normal menu items.

    The bFlags describes other information about the menu item, information that in the classic menu was hidden in spare bits in the wFlags. But here, the bFlags is where this information is kept. The following flags are currently defined:

    0x01This item is a pop-up submenu
    0x80This item is the last item in the menu

    If indeed the bottom bit is set, then after the MENUITEMEX16 comes a description of the submenu, recursively. (Note that the submenu does not have a MENUHEADER16.)

    As before, we'll illustrate this format with an example.

    1 MENUEX 1000
    BEGIN
      POPUP "&File", 200,,, 1001
      BEGIN
        MENUITEM "&Open\tCtrl+O", 100
        MENUITEM "", -1, MFT_SEPARATOR
        MENUITEM "&Exit\tAlt+X",  101
      END
      POPUP "&View", 201,,, 1002
      BEGIN
        MENUITEM "&Status Bar", 102,, MFS_CHECKED
      END
    END
    

    The resulting 16-bit extended menu template begins with the header:

    0000  01 00          // wVersion = 1
    0002  04 00          // cbHeaderSize = 4
    

    Since this is the start of a menu, we get a context help ID:

    0004  E8 03 00 00    // dwContextHelpID = 1000
    

    After the context help ID come the menu items. Our first is a pop-up submenu, so the bFlags indicates that a submenu is coming:

    0008  00 00 00 00    // dwType = MFT_STRING
    000C  00 00 00 00    // dwState = 0
    0010  C8 00          // wID = 200
    0012  01             // bFlags = "pop-up submenu"
    0013  26 46 69 6C 65 00 // "&File" + null terminator
    

    Since we have a pop-up submenu, we recursively include a template for that submenu directly after the menu item template. Consequently, we begin with the context help ID:

    0019  E9 03 00 00    // dwContextHelpID = 1001
    

    And then the contents of the submenu:

    001D  00 00 00 00    // dwType = MFT_STRING
    0021  00 00 00 00    // dwState = 0
    0025  64 00          // wID = 100
    0027  00             // bFlags = 0
    0028  26 4F 70 65 6E 09 43 74 72 6C 2B 4F 00
                         // "&Open\tCtrl+O" + null terminator
    
    0035  00 08 00 00     // dwType = MFT_SEPARATOR
    0039  00 00 00 00     // dwState = 0
    003D  FF FF           // wID = -1
    003F  00              // bFlags = 0
    0040  00              // ""
    
    0041  00 00 00 00     // dwType = MFT_STRING
    0045  00 00 00 00     // dwState = 0
    0049  65 00           // wID = 101
    004B  80              // bFlags = "this is the last menu item"
    004C  26 45 78 69 74 09 41 6C 74 2B 58 00
                         // "&Exit\tAlt+X" + null terminator
    

    When we reach the end of the pop-up submenu, we pop up a level. Therefore, the next entries describe more top-level menu items.

    0058  00 00 00 00     // dwType = MFT_STRING
    005C  00 00 00 00     // dwState = 0
    0060  C9 00           // wID = 201
    0062  81              // bFlags = "pop-up submenu" |
                          //          "this is the last menu item"
    0063  26 56 69 65 77 00 // "&View" + null terminator
    

    Ah, no sooner do we pop up than we push back down with another submenu. And the "last menu item" flag is set, which means that once the submenu is finished, we are done with the extended menu template.

    0069  EA 03 00 00    // dwContextHelpID = 1002
    
    006D  00 00 00 00    // dwType = MFT_STRING
    0071  08 00 00 00    // dwState = MFS_CHECKED
    0075  66 00          // wID = 102
    0077  80             // bFlags = "this is the last menu item"
    0078  26 53 74 61 74 75 73 20 42 61 72 00
                         // "&Status Bar" + null terminator
    

    After the context help ID, we have the sole menu item for this pop-up submenu, so the first item is also the last item.

    Next time, we'll wrap up by looking at the final menu template format, the 32-bit extended menu. I bet you all can't wait.

  • The Old New Thing

    When companies make it hard for you to pay money they are owed

    • 22 Comments

    Elissa Ely runs into a bureaucratic wall trying to pay her invalid mother's credit card bill. They won't tell her the outstanding balance on her mother's account because "it seemed to run the risk that a stranger might pay someone else's bill."

    My own mother ran into a similar problem. She wanted to find out the balance in an old elementary school bank account of mine that we both had long forgotten about. She went to the bank (this was pre-Internet) to obtain the balance, but the bank wouldn't tell her since she wasn't the account holder. My mother asked if she could make a deposit into the account, and they said that was allowed. She made a one-dollar deposit into the account, and at the bottom of the the receipt was the account balance.

  • The Old New Thing

    Why does the "Install Font" dialog look so old-school?

    • 51 Comments

    8 wonders why the "Install Font" dialog looks so old-school. (And Kevin Provance demonstrates poor reading skills by not only ignoring the paragraph that explains why the suggestion box is closed, but also asking a question that's a dup of one already in the suggestion box!)

    Because it's a really old dialog.

    That dialog has been around for probably two decades now. It works just fine, and since it's not really a high-traffic dialog, updating it takes lower priority than things that get used more often. Development and testing resources aren't infinite, after all. I'm sure that if you look harder, you can find other old dialog boxes. (My pet ugly old dialog is the Character Map program. It's hideous. and I say that even though my boss's boss wrote it.)

    Besides, people don't really add fonts that way much any more. When you install font packs, such as the Consolas Font Pack for Microsoft Visual Studio 2005, they just install the fonts as part of their setup process. It's all taken care of for you.

  • The Old New Thing

    Crazy ideas at the lunch table: The ice cream bicycle

    • 11 Comments

    At the lunch table some time ago, we considered the possibility of riding the STP atop a classic ice cream bicycle. You could play the music and cruise down the road, selling ice cream sandwiches out of your trunk.

    Naturally, you'd start really early in the day, because you can't go very fast on that bicycle, and besides, nobody is going to want ice cream early in the morning, so you may as well get a head start on them. Ideally, they would catch up to you around mid-afternoon, during the hot part of the day when they're looking for a break.

    No idea how practical this is, it was just a crazy idea.

    (Apparently we're not the first people to come up with this crazy idea.)

    The STP takes place this weekend. I wish all the riders a safe and enjoyable journey.

    Bonus shout-out: Among those riders is one of my lunchmates. Read his story here.

  • The Old New Thing

    The evolution of menu templates: 32-bit classic menus

    • 10 Comments

    Now that we've got a handle on 16-bit classic menu templates, we can move on to the next evolutionary step, namely 32-bit classic menu templates.

    The 32-bit classic menu template is in fact nearly identical to the 16-bit classic menu template. The only change is that the menu text is now a Unicode string instead of an ANSI string. Consequently, the discussion below will be rather brief when there is nothing new being introduced.

    The 32-bit classic menu template begins with the same header, and the fields have the same meaning.

    struct MENUHEADER32 {
     WORD wVersion;
     WORD cbHeaderSize;
     BYTE rgbExtra[cbHeaderSize];
    };
    

    Actually, there's a bonus wrinkle: Whereas the cbHeaderSize is always zero in practice for 16-bit menu templates, the 32-bit cbHeaderSize must be zero if you intend your menu template to be used on the Windows 95 series of Windows operating systems.

    Why must the value be zero on Windows 95? I just discovered this now doing the research for this series of articles. It's a bug in the code that converts between 32-bit and 16-bit resources! When converting from a 32-bit menu template to a 16-bit menu template, the conversion code dutifully copies the cbHeaderSize and uses it to skip ahead in the 32-bit menu template, but it neglects to skip ahead the same amount in the 16-bit menu template. Fortunately, nobody ever sets this value to anything other than zero, so the bug never manifests itself in practice. (I suspect I'm the first person ever to notice this bug. First, because nobody generates menu templates at runtime; everybody uses the resource compiler or some other tool, and those tools all set the field to zero. And second, because those extra bytes aren't used for anything, so there's no reason for the count to be nonzero.)

    Even if you don't care about Windows 95, the cbHeaderSize must still be an even number so that the menu item templates are suitably aligned.

    The rest of the 32-bit classic menu template is just stuff you've seen before. We have a packed array of menu item templates, either a POPUPMENUITEM32 if the menu item is a pop-up submenu, or a NORMALMENUITEM32 if not:

    struct NORMALMENUITEM32 {
     WORD wFlags;       // menu item flags (MFT_*, MFS_*)
     WORD wID;          // menu item ID
     WCHAR szText[];    // null terminated Unicode string
    };
    
    struct POPUPMENUITEM32 {
     WORD wFlags;       // menu item flags (MFT_*, MFS_*)
     WCHAR szText[];    // null terminated UNICODE string
    };
    

    Aside from changing CHAR to WCHAR, everything is exactly the same. Let's use that same resource script we used to illustrate the 16-bit classic menu template and convert it to a 32-bit classic menu template instead.

    1 MENU
    BEGIN
      POPUP "&File"
      BEGIN
        MENUITEM "&Open\tCtrl+O", 100
        MENUITEM SEPARATOR
        MENUITEM "&Exit\tAlt+X",  101
      END
      POPUP "&View"
      BEGIN
        MENUITEM "&Status Bar", 102, CHECKED
      END
    END
    

    Compiling this as a 32-bit classic menu template would result in something like this:

    // MENUHEADER32
    0000  00 00      // wVersion = 0
    0002  00 00      // cbHeaderSize = 0
    

    After the header come the top-level menu items:

    // POPUPMENUITEM32 for top-level menu
    0004  10 00     // wFlags = MF_POPUP
                    // no wID
    0006  26 00 46 00 69 00 6C 00 65 00 00 00
                    // "&File" + null terminator
    

    Since we have a pop-up submenu, the contents of the submenu come next, and we put the top-level menu items on hold.

    // NORMALMENUITEM32 for nested menu
    0012  00 00     // wFlags = MFT_STRING
    0014  64 00     // wID = 100
    0016  26 00 4F 00 70 00 65 00 6E 00 09 00
          43 00 74 00 72 00 6C 00 2B 00 4F 00 00 00
                    // "&Open\tCtrl+O" + null terminator
    

    For the separator, we can either do it the formally correct way:

    // NORMALMENUITEM32 for nested menu - separator
    0030  00 08     // wFlags = MFT_SEPARATOR
    0032  00 00     // wID = 0
    0034  00 00     // ""
    

    or we can use the alternate compatibility form:

    0030  00 00     // wFlags = 0
    0032  00 00     // wID = 0
    0034  00 00     // ""
    

    The last item on the submenu has the MF_END flag.

    // NORMALMENUITEM32 for nested menu - last item
    0036  80 00    // wFlags = MFT_STRING | MF_END
    0038  65 00    // wID = 101
    003A  26 00 45 00 78 00 69 00 74 00 09 00
          41 00 6C 00 74 00 2B 00 58 00 00 00
                   // "&Exit\tAlt+X" + null terminator
    

    We now pop back to the top-level menu, whos second item and final item is another pop-up submenu.

    // POPUPMENUITEM for top-level menu
    0052  90 00     // wFlags = MF_POPUP | MF_END
                    // no wID
    0054  26 00 56 00 69 00 65 00 77 00 00 00
                    // "&View" + null terminator
    

    And finally, the contents of that last pop-up submenu.

    0060  88 00    // wFlags = MFT_STRING | MFS_CHECKED | MF_END
    0062  65 00    // wID = 102
    0064  26 00 53 00 74 00 61 00 74 00 75 00
          73 00 20 00 42 00 61 00 72 00 00 00
                    // "&Status Bar" + null terminator
    

    And that's all there is to it. A pretty straightforward extension of the 16-bit classic menu template to a 32-bit Unicode version.

    After a short break, we'll look at the extended menu templates.

  • The Old New Thing

    News flash: Car salesmen are sneaky and underhanded

    • 9 Comments

    Steve Tripoli looks at deception in the car sales industry.

  • The Old New Thing

    What's the deal with that alternate form for menu item template separators?

    • 7 Comments

    We saw last time that you can specify a separator in a menu item template by specifying zero for everything, even though technically you're supposed to pass MFT_SEPARATOR for the flags. What's the deal with that alternate form for menu item template separators?

    This goes back to the early days of the InsertMenu function (and its friends like AppendMenu and ModifyMenu). In the Before Time, the way you specified a separator was to add a null pointer. Not a null string (a string with no characters, consisting only of the null terminator), but an actual null pointer.

    AppendMenu(hmenu, MF_STRING, 0, NULL);
    

    The recommended way of adding a separator is, of course, to use MF_SEPARATOR, but this old-fashioned method is still supported for backward compatibility. The alternate form of the menu item template for separators is just a throwback to the days when this "old style" of adding a separator was in fact the only style.

  • The Old New Thing

    Warning: This cereal may contain traces of spiders

    • 18 Comments

    In a Lisbon grocery store, I was looking at the ingredient panel of a box of cereal. It contained the information in multiple languages, and French came first. I was momentarily stunned by the following warning:

    Traces possibles d'arachides.

    I guess this falls in the category of "Raymond misreads French cereal box warnings."

  • The Old New Thing

    The evolution of menu templates: 16-bit classic menus

    • 6 Comments

    Menus aren't as complicated as dialogs. There are no fonts, no positioning, it's just a list of menu items and flags. Well, okay, there's the recursive part, when a menu has a submenu. But that's really the only wrinkle. Most of it is pretty boring.

    The 16-bit classic menu template begins with the following header:

    struct MENUHEADER16 {
     WORD wVersion;
     WORD cbHeaderSize;
     BYTE rgbExtra[cbHeaderSize];
    };
    

    The version is zero for 16-bit classic menu templates, and the cbHeaderSize is the number of extra bytes in the menu header that have to be skipped over to find the first actual menu item. In practice, cbHeaderSize is always zero. This header exists only on the top level menu; recursive submenus do not have a MENUHEADER16.

    After the header (and any extra bytes specified by cbHeaderSize) comes a packed array of menu item templates. There are two types of menu item templates, normal items and pop-up submenus. First, let's look at the normal items:

    struct NORMALMENUITEM16 {
     WORD wFlags;       // menu item flags (MFT_*, MFS_*)
     WORD wID;          // menu item ID
     CHAR szText[];     // null terminated ANSI string
    };
    

    Normal items represent menu items that are not pop-up submenus, and they take a pretty straightforward form. All you get are flags, the item ID, and the menu item text. The flags are values such as MFT_STRING, MFT_MENUBARBREAK, and MFS_DISABLED. Of course, the MF_POPUP flag is not allowed, since this is a normal item template. The flag MFS_HILITE is also not allowed, for reasons we will see later.

    The other type of menu item template is the pop-up submenu.

    struct POPUPMENUITEM16 {
     WORD wFlags;       // menu item flags (MFT_*, MFS_*)
     CHAR szText[];     // null terminated ANSI string
    };
    

    The pop-up item template doesn't have an ID, the MF_POPUP flag must be set in the flags (naturally), the MFS_HILITE flag must not be set, and it is immediately followed by... another menu resource, minus the resource header, which describes the pop-up submenu itself. (This is the recursive part.)

    The end of the list of menu item templates is reached when an item with the MF_END flag is set in its flags. And now you see why MFS_HILITE is disallowed:

    #define MF_END              0x00000080L
    #define MF_HILITE           0x00000080L
    #define MFS_HILITE          MF_HILITE
    

    If you set the MF_HILITE flag, it would be mistaken for the end of the menu template. Fortunately, there's no need to set the MFS_HILITE flag in the menu item template since highlighting happens at runtime based on the user's mouse and keyboard activity, not at menu creation time.

    To make all this discussion concrete, let's convert this rather uninteresting menu resource into a menu template:

    1 MENU
    BEGIN
      POPUP "&File"
      BEGIN
        MENUITEM "&Open\tCtrl+O", 100
        MENUITEM SEPARATOR
        MENUITEM "&Exit\tAlt+X",  101
      END
      POPUP "&View"
      BEGIN
        MENUITEM "&Status Bar", 102, CHECKED
      END
    END
    

    The menu template for this classic 16-bit menu would go something like this: We start with the header, which always looks the same.

    0000  00 00      // wVersion = 0
    0002  00 00      // cbHeaderSize = 0
    

    Next comes the list of menu items. Our first is a pop-up submenu, so the MF_POPUP flag is set, indicating that we have a POPUPMENUITEM16:

    0004  10 00     // wFlags = MF_POPUP
                    // no wID
    0006  26 46 69 6C 65 00 // "&File" + null terminator
    

    Since this is a pop-up menu, the contents of the pop-up menu follow. This is the recursive part of the menu template format: we have a menu template inside the outer one. The first item of the pop-up menu is a string and therefore takes the form of a NORMALMENUITEM16:

    000C  00 00     // wFlags = MFT_STRING
    000E  64 00     // wID = 100
    0010  26 4F 70 65 6E 09 43 74 72 6C 2B 4F 00
                    // "&Open\tCtrl+O" + null terminator
    

    The next item of the pop-up menu is a separator. If you have been following the rules strictly, you would generate the separator like this:

    001D  00 08     // wFlags = MFT_SEPARATOR
    001F  00 00     // wID = 0
    0021  00        // ""
    

    However, it turns out that there is an alternate form for separators, namely to pass all zeroes:

    001D  00 00     // wFlags = 0
    001F  00 00     // wID = 0
    0021  00        // ""
    

    The existence of this alternate form is actually an artifact of history, which we'll look at next time. But for now, just realize that you can express a separator in two different ways, either the official way with MFT_SEPARATOR or the alternate way with wFlags = 0. Either works just fine.

    Anyway, let's finish up that submenu with the final item, which is a string. We set the MF_END flag to indicate that this is the end of the (nested) menu.

    0022  80 00    // wFlags = MFT_STRING | MF_END
    0024  65 00    // wID = 101
    0026  26 45 78 69 74 09 41 6C 74 2B 58 00
                   // "&Exit\tAlt+X" + null terminator
    

    With the completion of the nested menu, we pop back to the top-level menu. Next comes the "View" submenu.

    0032  90 00     // wFlags = MF_POPUP | MF_END
                    // no wID
    0034  26 56 69 65 77 00 // "&View" + null terminator
    

    The MF_POPUP flag marks this as a POPUPMENUITEM16, which means that there is no wID. And look, the MF_END flag is set, which means that this is the last item on the top-level menu. But we're not finished yet, since we still have to read the nested submenu. (Notice that the "end of menu" marker is far away from the actual end of the menu!)

    003A  88 00    // wFlags = MFT_STRING | MFS_CHECKED | MF_END
    003C  66 00    // wID = 102
    003E  26 53 74 61 74 75 73 20 42 61 72 00
                    // "&Status Bar" + null terminator
    

    The submenu consists of a single item, so its first item is also its last (MF_END). Now that the submenu is complete, we pop back to the main menu again, but as we saw, the main menu is also complete, so that concludes the entire menu template.

    Next time, we'll look at that strange alternate form for separator items before returning to the history of menu templates.

Page 3 of 4 (40 items) 1234