Undocumented
Windows ® 95
Shell File Menus
W hen you see something as often as you see the
Start Menu, you're inclined to take it for granted.
xxxxxxxxxxxxxxx
However, it has some rather interesting features that you
wouldn't expect on a regular menu. The most obvious addi-
tions are the colored icons and the neat border down the
left hand side. Slightly less obvious is the ability to create
menus that display portions of the file system (the
'Programs' submenu is a perfect example).
      While you may not consider all of these features parti-
cularly useful, they are all available for you to use. All that's
required is a few undocumented functions which we'll be
discussing in this article. The functions are all exported
from SHELL32.DLL and, as usual, they're all exported by
ordinal.
Creating a Menu
HMENU WINAPI FileMenu_Create(
  COLORREF  crBorderColor, 
  int       nBorderWidth, 
  HBITMAP   hBorderBmp, 
  int       nSelHeight, 
  UINT      uFlags);

void WINAPI FileMenu_Destroy(
  HMENU  hMenu);
Figure 1  Create and Destroy Functions
The first function we're going to look
at is FileMenu_Create (see Figure 1
for the function declaration). The
first few parameters deal with the
menu border - hBorderBmp specifies
the bitmap to use; nBorderWidth
gives the width of the bitmap; and
crBorderColor specifies the color
used to fill in the area above the bit-
map. If you don't want a border, you
can just set them all to NULL.
      The nSelHeight parameter specifies the height of the
menu selection bar. You can give any value you want but
you would typically just set it to one of the constants in
Figure 2FM_FULL_SELHEIGHT specifies that
the selection bar should cover the full height of
the menu item. FM_DEFAULT_SELHEIGHT is
the same as FM_FULL_SELHEIGHT when using
large icons, but with small icons you get a one
FM_FULL_SELHEIGHT -1
FM_DEFAULT_SELHEIGHT 0
Figure 2  nSelHeight Constants
pixel border on the top and bottom.
      The uFlags parameter enables you to choose whether
you want large or small icons, and whether the menu should
wrap onto multiple columns when it is too
big to fit on the screen. The latter feature
only applies when you're adding files to the
menu though. See Figure 3 for a list of the
possible flag values.
      Destroying a menu is fortunately a lot
easier. You just call FileMenu_Destroy (the
FMF_SMALL_ICONS 0x00
FMF_LARGE_ICONS 0x08
FMF_NO_COLUMN_BREAK 0x10
Figure 3  Create Flags
function declaration is also in Figure 1), passing it the handle
returned from FileMenu_Create. The ordinal value for
FileMenu_Destroy is 118. The ordinal for FileMenu_Create
is 114.
Adding Regular Menu Items
Now that you've created your menu, you'll probably want to
add some items with the FileMenu_AppendItem function
(see Figure 4). The first three
parameters are pretty obvious.
As for the others: iIcon specifies
an icon index from the System
Imagelist (see the Shell Icon
Cache article); hMenuPopup
specifies a submenu handle if
you want a submenu; nItem-
Height allows you to request a
specific height for each item.
BOOL WINAPI FileMenu_AppendItem(
  HMENU   hMenu, 
  LPCSTR  lpszText, 
  UINT    uID, 
  int     iIcon, 
  HMENU   hMenuPopup, 
  int     nItemHeight);
Figure 4  FileMenu_AppendItem
      You may also find some of the constants in Figure 5 to
be quite useful. If you want to add a menu separator, you
can just set the lpszText parameter to
FM_SEPARATOR. If you don't want an
item to have an icon, you can set the iIcon
parameter to FM_BLANK_ICON (there'll
still be a gap where the icon would've
been though). Finally, if you'd rather just
use the default height for an item you can
FM_SEPARATOR (LPCSTR)1
FM_BLANK_ICON -1
FM_DEFAULT_HEIGHT 0
Figure 5  AppendItem Constants
specify FM_DEFAULT_HEIGHT for the nItemHeight pa-
rameter.
      The function returns TRUE if it was successful, or
FALSE if there was an error of some sort. The ordinal value
is 115.
Adding Files and Folders
Adding files to a menu is slightly more complicated. Typi-
cally you would use the FileMenu_InsertUsingPidl function
(see Figure 6). The pidl pa-
rameter specifies the folder
whose files you want to
add. uEnumFlags can use
any of the SHCONTF flags.
It determines whether to
include files or folders and
whether hidden files should
be shown. The uID pa-
rameter specifies the ID
int WINAPI FileMenu_InsertUsingPidl(
  HMENU           hMenu, 
  UINT            uID, 
  LPCITEMIDLIST   pidl, 
  UINT            uFlags, 
  UINT            uEnumFlags, 
  LPFNFMCALLBACK  lpfnCallback);
Figure 6  FileMenu_InsertUsingPidl
that will be assigned to each item (all file items on a menu
share the same ID).
      The uFlags parameter gives you control of a few other
features (see Figure 7 for a list of possible values). If you
specifiy the FMF_NO_EMPTY_ITEM flag,
you won't get the default 'Empty' item
that is normally added when a folder is
empty (this doesn't apply to submenus
though). If you want to prevent the user's
FMF_NO_EMPTY_ITEM 0x01
FMF_NO_PROGRAM_GROUPS 0x04
Figure 7  Insert Flags
Program Groups from appearing on your menu, you can
specify FMF_NO_PROGRAM_GROUPS (I'm not sure why
you would want to do that though).
      The lpfnCallback parameter allows you to specify a
callback function that will be called for each item that is
added to the menu
(see Figure 8). The
first parameter gives
the pidl of the folder
from which the file
was retrieved. The
typedef void (CALLBACK *LPFNFMCALLBACK)(
  LPCITEMIDLIST pidlFolder, 
  LPCITEMIDLIST pidlFile);
Figure 8  Insert Callback Function
second parameter gives the simple pidl of the file itself. To
get the full pidl for the file you obviously just concatenate
the two together. The function is also called when the menu
is destroyed or all items are deleted, in which case the
second parameter will be NULL.
      The return value for FileMenu_InsertUsingPidl gives
the number of items that were added to the menu. This
does not include items on submenus (i.e. in subfolders),
since those files are only added when the menu is actually
displayed. The ordinal value for the function is 110.
Other File Functions
One problem with FileMenu_InsertUsingPidl is that you
can only call it for an 'empty' menu (otherwise you may get
rather unexpected re-
sults). As an alternative,
you may want to use File-
Menu_ReplaceUsingPidl
(see Figure 9) which auto-
matically removes all
items from the menu be-
fore adding any files.
However, for some rea-
int WINAPI FileMenu_ReplaceUsingPidl(
  HMENU           hMenu, 
  UINT            uID,
  LPCITEMIDLIST   pidl, 
  UINT            uEnumFlags, 
  LPFNFMCALLBACK  lpfnCallback);
Figure 9  FileMenu_ReplaceUsingPidl
son you don't get to specify the flags parameter, so you
may be better off just removing the items yourself if you
need to. 
      Another function that might be useful is FileMenu_
Invalidate (see Figure 10). It effecitvely deletes all items
from the menu, but
the menu remains as-
sociated with the last
folder that was used
for adding files. The
next time the menu is
displayed, it will auto-
matically be refreshed
with the current set of
void WINAPI FileMenu_Invalidate(
  HMENU  hMenu);

HMENU WINAPI FileMenu_FindSubMenuByPidl(
  HMENU          hMenu,
  LPCITEMIDLIST  pidl);
Figure 10  Invalidating and Finding Submenus
files. If you only want to invalidate a particular subfolder on
your menu, you can use FileMenu_FindSubMenuByPidl
(also in Figure 10) to get a handle to the submenu first.
      On Windows NT, there are another two file related
functions. FileMenu_AppendFilesForPidl (see Figure 11)
allows you to add a se-
cond set of files onto a
menu that already has
a folder associated
with it. This is how NT
manages to display the
common program
int WINAPI FileMenu_AppendFilesForPidl(
  HMENU          hMenu, 
  LPCITEMIDLIST  pidl, 
  BOOL           bAddSeparator);
Figure 11  FileMenu_AppendFilesForPidl
groups on the same menu as the user's private program
groups. The other function,
FileMenu_AddFilesForPidl,
is not nearly as useful (see
Figure 12). It's basically
identical to the FileMenu_
InsertUsingPidl function,
except for the uReserved
parameter which is never
used.
      As usual, if you want
to use the NT functions
int WINAPI FileMenu_AddFilesForPidl(
  HMENU           hMenu, 
  UINT            uReserved, 
  UINT            uID, 
  LPCITEMIDLIST   pidl, 
  UINT            uFlags, 
  UINT            uEnumFlags, 
  LPFNFMCALLBACK  lpfnCallback);
Figure 12  FileMenu_AddFilesForPidl
and you still want your app to run on Windows 95, you're
going to have to link to them with GetProcAddress. The
ordinals for FileMenu_AppendFilesForPidl and FileMenu_
AddFilesForPidl are 124 and 125 respectively. The ordinal
for FileMenu_ReplaceUsingPidl is 113, the ordinal for
FileMenu_Invalidate is 111, and the ordinal for FileMenu_
FindSubMenuByPidl is 106.
Displaying the Menu
Now that we've created our menu and added a few items,
the next logical step would be to display the menu. If you
want to display it as a popup menu, the function to use is
FileMenu_TrackPopupMenuEx (see Figure 13). The pa-
rameters are identical to
the TrackPopupMenuEx
function. 
      If you want to use it
as a submenu on the
main menu bar of your
window, you can attach
it to the menu with the
AppendMenu function.
However, you can't rely
BOOL WINAPI FileMenu_TrackPopupMenuEx(
  HMENU        hMenu, 
  UINT         uFlags,
  int          x, 
  int          y, 
  HWND         hWnd, 
  LPTPMPARAMS  lptpm);
Figure 13  FileMenu_TrackPopupMenuEx
on the system to automatically free the menu resources
from a File Menu when the window closes. You must
detach the menu with RemoveMenu and then destroy it
yourself with FileMenu_Destroy.
      As with regular menus, your application will receive a
WM_COMMAND message when a menu item is selected.
However, if you've added files to your menu, you'll recall
that all file
items on a
menu share
the same
menu ID so
there's no
way to tell
BOOL WINAPI FileMenu_GetLastSelectedItemPidls(
  UINT         uReserved,
  LPITEMIDLIST *ppidlFolder, 
  LPITEMIDLIST *ppidlItem);
Figure 14  FileMenu_GetLastSelectedItemPidls
which particular file was selected. That's where the
function FileMenu_GetLastSelectedItemPidls comes in
(see Figure 14). It returns the pidl of the folder from which
the file was retrieved, and the simple pidl of the file itself. 
      The ordinal value for FileMenu_TrackPopupMenuEx is
116. FileMenu_GetLastSelectedItemPidls has an ordinal
value of 107.
Responding to Messages
If you think you should have a working menu by now I'm
afraid you're wrong. File Menus are actually implemented
as owner-drawn menus,
which means that some-
body has to respond to the
WM_MEASUREITEM and
WM_DRAWITEM mes-
sages or your menu is
going to be practically invi-
sible. Fortunately you don't
have to do any of the ac-
tual drawing - you just call
LRESULT WINAPI FileMenu_MeasureItem(
  HWND  hWnd, 
  LPMEASUREITEMSTRUCT  lpmis);

LRESULT WINAPI FileMenu_DrawItem(
  HWND  hWnd, 
  LPDRAWITEMSTRUCT  lpdis);
Figure 15  Message Handlers
FileMenu_MeasureItem and FileMenu_DrawItem (see
Figure 15). The ordinal values for these functions are 112
and 105 respectively.
      If you've added files to the menu, you should also be
responding to the WM_INITMENUPOPUP message by cal-
ling FileMenu_InitMenuPopup (see Figure 16). This takes
care of refreshing any
folders that have been
invalidated or subfol-
ders that haven't been
created yet. In addition,
you should respond to
the WM_MENUCHAR
function by calling File-
Menu_HandleMenu-
BOOL WINAPI FileMenu_InitMenuPopup(
  HMENU  hMenu);

LRESULT WINAPI FileMenu_HandleMenuChar(
  HMENU   hMenu, 
  WPARAM  wParam);
Figure 16  More Message Handlers
Char (also in Figure 16). This allows the menu to emulate
keyboard accelerators for the file items. The ordinal values
for these two functions are 109 and 108 respectively.
Other Functions
BOOL WINAPI FileMenu_DeleteAllItems(
  HMENU  hMenu);

BOOL WINAPI FileMenu_DeleteItemByCmd(
  HMENU  hMenu, 
  UINT   uID);

BOOL WINAPI FileMenu_DeleteItemByIndex(
  HMENU  hMenu, 
  UINT   uPos);
Figure 17  Delete Functions
You've probably got
enough information to
get your menu working
now, but there are still
a few other functions
that you may want to
use. The functions for
deleting items, shown
in Figure 17, are pretty
self-explanatory. The
functions in Figure 18
are not quite as obvious. FileMenu_DeleteMenuItemBy-
FirstID, deletes a submenu given the ID of its first menu
item. FileMenu_DeleteSeparator actually deletes the first
menu item with
a zero ID (you
are obviously
expected to
use a zero ID
for all of your
separators).
      To disable
or enable a
BOOL WINAPI FileMenu_DeleteMenuItemByFirstID(
  HMENU  hMenu, 
  UINT   uID);

BOOL WINAPI FileMenu_DeleteSeparator(
  HMENU  hMenu);
Figure 18  More Delete Functions
menu item, you can use the FileMenu_EnableItemByCmd
function (see Figure 19). To determine the dimensions of a
particular menu item you can use FileMenu_GetItemExtent
(also in Figure 19). The
height is in the high-order
word of the return value
and the width is in the
low-order word. Finally,
FileMenu_AbortInitMenu
(also in Figure 19), allows
you to abort the initializa-
tion of a subfolder menu if
it appears to be taking too
long. This is not as easy
as it seems, though, since
you would probably need
BOOL WINAPI FileMenu_EnableItemByCmd(
  HMENU  hMenu, 
  UINT   uID, 
  BOOL   bEnable);

DWORD WINAPI FileMenu_GetItemExtent(
  HMENU  hMenu, 
  UINT   uPos);

void WINAPI FileMenu_AbortInitMenu();
Figure 19  Other Functions
a second thread to keep track of the time spent in the WM_
INITMENUPOPUP
 message handler.
      The ordinal values for the delete functions in the order
they appear in the Figures 17 & 18 are as follows: 104, 117,
140, 141 and 142 . The ordinal values for the last three
functions (in Figure 19) are 143, 144, and 120.
Operating System Differences
By now, you should be well aware that undocumented
functions on Windows NT almost always require unicode
strings where the Windows 95 equivalent would use ansi.
Fortunately there's only one function in this section that has
this problem - FileMenu_AppendItem expects an LPCWSTR
for the lpszText parameter on NT.
      Also, on NT there are certain internal variables (mostly
to do with icon sizes) that the shell uses when calculating
the layout of File Menus. These variables are usually setup
when calling a function that initializes the System Imagelist,
such as FileIconInit or Shell_GetImageLists. If you aren't
already calling one of these functions, you must do so be-
fore using any File Menu functions.
      However, the real problems arise when you start trying
to support Windows 98 or Windows NT 5. The FileMenu_
InsertUsingPidl and FileMenu_ReplaceUsingPidl functions
have been replaced by a single function with their combined
functionality. That means that you'll have to write a lot OS
specific code if you need to use those functions and still
have your code work on Windows 98.
      What's worse is that you'll have the same problems on
Windows 95 if you've installed Internet Explorer 4 with the
Active Desktop. Since Internet Explorer actually updates
the operating system, what you're really getting is a pre-
release version of the Windows 98 shell. Unfortunately I'm
not prepared to document any of this new shell functionality
at this stage. If you need your code to work with the Active
Desktop it's probably best that you stick to the basics.
BACK TO HOME
Copyright © 1998-1999 James Holderness. All Rights Reserved
Page last modified: December 14th, 1998

1