The CMenuSite class and the window it creates act as an intermediary between a BaseBar and a MenuBand, forwarding the events and messages either to the child band, or to the parent bar, as necessary. It handles the sizing of the child band, to adapt it to changes in the available space. It also provides certain services related to the positioning and sizing of the child bar, which it handles by forwarding some requests to the child, and the rest to the parent.
This implementation was done within rshell, a DLL created by Giannis that is used to provide alternative implementations of the shell32 classes, which can be referenced instead of the real ones while constructing the Start Menu. With rshell, the code can be tested directly from within Visual Studio, using all of the debugging features of the IDE.
Gigaherz continued by writing a wrapper for CMenuBand that logs the calls, with parameters and return values, done to a real CMenuBand from Windows. This allowed for seeing the essential features needed to implement the basic displaying of content in a CMenuBand, in the way expected by Windows' Start Menu and Menu Site.
The rough, partial implementation of CMenuBand was ported from ReactOS' shell32 into rshell. Allow the start menu to use this CMenuBand required implementing support for QueryService and Exec methods. Although the behavior of the Exec system is still largely unknown, there's enough information from the call logs and stack traces to realize that command id 16 is used to set "large icons" mode, and id 19 is used in the Popup function, and requires returning S_FALSE.
The band currently assumes that a null mask means "give me everything", and it requests the optimal size from the toolbar to provide a value different than 0. This makes the CMenuBand at least partially functional.
Analyzing CMenuBand allowed for adding rudimentary support for a custom CMenuBand which shows both a shell folder and a static menu. The callbacks are also used to obtain the icons for the static menu items.
Gigaherz continued by writing a wrapper for CMenuDeskBar, in the same style used for CMenuBand. The partial implementation CMenuDeskBar was ported from shell32 to rshell. Merging the code from CBaseBar into it simplified the implementation. The combined log from the wrappers for CMenuDeskBar and CMenuBand allowed seeing that the SetIconSize from CMenuDeskBar is the one that is supposed to notify the CMenuBand of the icon size choice. To achieve this, it calls the Exec method with cmd=16 and opts=2. It appears that opts=2 means “big icons”, and calling this method with opts=0 keeps the icons small.
The Popup system is supposed to calculate the optimal size of the start menu, and notify the different objects that it are about to be displayed.
Clicking on Menu Items
Clicking on menu items involves sending a callback notification for the currently selected item, asking the callback to execute the appropriate action of the item.
Some of the items are not meant to be clicked on and are supposed to show a sub menu instead. Implementing these required subclassing the toolbar windows to add a timer on hover and handle the opening of the submenu in the resulting WM_TIMER event, which would be received by the toolbar, but needed to be handled by the code.
The OnSelect behavior seems to be something close to this:
- The DeskBar’s SubMenu parent is the IMenuPopup interface of the DeskBar’s “owner” Site, except for the top-level start menu, which does not have a submenu parent.
- The DeskBar’s SubMenu child is the contained Band obtained through the contained MenuSite by using the SID_SMenuPopup service.
- The Band’s SubMenu parent is the DeskBar obtained through the parent MenuSite which is itself obtained through the SID_SMenuBandChild.
- When a user clicks a toolbar item and the item executes an action, the Band sends an OnSelect(Execute) notification to its SubMenu parent.
- When a user presses the alt key, the Band sends an OnSelect(FullCancel) notification to its parent.
- When a different window activates, the DeskBar sends an OnSelect(FullCancel) to itself.
- When the DeskBar receives an OnSelect notification that results in closing of a submenu, it notifies the child using OnSelect(CancelLevel).
- When the DeskBar receives a notification other than CancelLevel, it notifies its parent menu. This is not mutually exclusive with the previous “rule”: Some notifications are forwarded to the child and parent.
- When the Band receives a cancellation event it cancels its current child, if any.
- When the Band receives a keyboard navigation event it either notifies the parent or, in the case of SelectRight, it opens the submenu of the currently selected item.
- SelectLeft is equivalent to CancelLevel except that it is notified to the parent also so it can select the appropriate item.
This implementation requires a focus manager class, which takes care of hooking the application’s message flow to intercept keyboard and mouse events and do the appropriate actions in each case.
Since the focus manager is unable to process the WM_ACTIVATION messages. a handler was added to the DeskBar class, which will send OnSelect notifications when a window gets deactivated in favour of a foreign one. To be able to prevent the menu from closing if the window being shown is a submenu, the GetSite method of the contained Band was repurposed to query the SubMenu child of the band. This is probably not the way Windows does it, but it serves its purpose until a better method is found.