You can add dynamic menus to your application using DLLs. This is helpful if you want to support add-ons to your base application. These add-ons may be provided by you or any third party (of course, not free of charge!). This is an old technique but I still find it useful. (For the sake of this article, I assume that the readers are familiar with writing DLLs.)
Using the code
The idea is to specify a directory in which your application looks for the add-on DLLs. The application then uses the
LoadLibrary function to load DLLs. The sample application provided with this article looks in the current directory. It uses
CFileFind to look for any DLL in the current directory. Once it finds a DLL, it checks whether the DLL has the “specific format” of add-on DLLs. The format of add-on DLLs should be such that they provide a set of predefined functions (which are known to the application and the application uses the
GetProcAddress API to get the address of these functions). I have defined the following functions for my program:
bool MenuTitle(CString& str);
bool MenuText(int n, CString& str);
bool MenuFunction(int n);
While defining the functions in the DLL, please note that the functions should be properly exported. Specifying
DllExport alone is not enough. The
DllExport attribute tells the linker to generate an export table entry for the specified function. This export entry is decorated. This is necessary to support
DllExport-ing of overloaded functions. But it also means that the string you pass to
GetProcAddress needs to be decorated. The decoration scheme varies from architecture to architecture and from calling convention to calling convention. So, for example, the function “
NumberOfMenus” is exported from a DLL which is compiled with VC++6 , its decorated name is "
?NumberOfMenus@@YAHXZ", so you have to do
GerProcAddress(hinst, ?NumberOfMenus@@YAHXZ) instead of
To get around with this problem, the function must be exported by its undecorated name. The easiest way to do this is to define the name of the functions in the “DEF” file of the DLL. (The DEF file of the sample DLL is shown below: TestDll.def: it declares the module parameters for the DLL.)
DESCRIPTION 'TestDll Windows Dynamic Link Library'
; Explicit exports can go here
OK, so the function definition is like this:
_declspec(dllexport) int NumberOfMenus(void);
_declspec(dllexport) bool MenuText(int n, CString& str);
_declspec(dllexport) bool MenuFunction(int n);
_declspec(dllexport) bool MenuTitle(CString& str);
But when I run the program, it generates a Windows error box saying the following:
The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention.
(press Retry to debug the application)
When I looked for help in MSDN, I found that the above condition applies when one of the following conditions are true:
- You call a function that does not use the
- The function contains parameters that are larger than 64 kilobytes (KB).
- You compile your application in Debug mode and with the /RTCs compiler option enabled.
Therefore, change the function declaration to:
_declspec(dllexport) int __cdecl NumberOfMenus(void);
_declspec(dllexport) bool __cdecl MenuText(int n, CString& str);
_declspec(dllexport) bool __cdecl MenuFunction(int n);
_declspec(dllexport) bool __cdecl MenuTitle(CString& str);
Before I proceed any further, let me explain the purpose of each of the above functions.
MenuTitle returns the title of the popup menu provided by this DLL. The application adds a popup-menu with this name. The function
NumberOfMenus defines the number of menu items provided by this add-on and
MenuText gives the text string for each menu item. The function
MenuFunction provides the handler for each sub-menu (provided by this add-on DLL).
typedef int (__cdecl *NUMMENUPTR)(void);
typedef bool (__cdecl *MENUTEXTPTR)(int ,CString&);
typedef bool (__cdecl *MENUTITLEPTR)(CString&);
typedef bool (__cdecl *MENUFUNCTIONPTR)(int);
For the sake of simplicity, the application class is an SDI application, and all the menus are added to the main frame class. The mainframe class maintains an array of the above structure (one per add-on DLL).
if (!ff.FindFile("*.dll")) return;
CString strFileName=ff.GetFileName( );
if (!hInst) continue;
if (!pfn) continue;
if (!pfn2) continue;
if (!pfn3) continue;
if (!pfn4) continue;
if (!(*pfn3)(strMainMenu)) continue;
CMenu *pDllMenu= new CMenu;
if ((*pfn2)(i, strMenu))
BOOL CMainFrame::AddPopUp(CString str,CMenu* pMenu)
if (!pMainMenu || !pMenu) return NULL;
return pMainMenu->AppendMenu(MF_STRING|MF_POPUP ,(int)pMenu->m_hMenu,str);
AddonMenus() is called from
The application program assigns 100 consecutive command IDs for these add-on DLLs. This is done by defining
ID_COMMAND_END in the resource.h and changing the
#define ID_COMMAND_START 32771
#define ID_COMMAND_END 32870
#define _APS_NEXT_COMMAND_VALUE 32871
The main assumption here is that the total number of menus in the add-on DLLs does not exceed 100 (a crude assumption indeed!!).
ON_COMMAND_RANGE(ID_COMMAND_START,ID_COMMAND_END,DllFunctions) is used to call the menu handlers (from the DLLs).
The menu handler is defined as follows:
void CMainFrame::DllFunctions(UINT nID)
if (nID >= m_Dlls[i].m_uStartID && nID
Finally use "
FreeLibrary" to unmap your DLLs from memory when the application terminates.