Introduction
The CMenu
class is a great help when it comes to manipulating menus, but unfortunately it doesn't provide much help where serialization is concerned.
The WIN32 SDK, on the other hand, offers help in the form of the structures MENUITEMTEMPLATEHEADER
and MENUITEMTEMPLATE
, and the function LoadMenuIndirect()
. But these structures are fairly complex and are really cumbersome to use.
CSerializableMenu
is a subclass of CMenu
that provides serialization support, by encapsulating all the nasty details of the WIN32 implementation, in the usual MFC manner (i.e., directly by calling the overridden public
method Serialize()
or indirectly by calling the overloaded operators >>
and <<
.)
Usage
The CSerializableMenu
class can be used as a direct replacement for the CMenu
class in your code. To perform serialization, you may call the method Serialize()
directly or use the overloaded operators >>
and <<
.
If you do not wish to replace the CMenu
class in your code, you can still use the CSerializableMenu
class for its serialization support by doing the following:
CSerializableMenu smenu;
smenu.Attach(m_menu.GetSafeHmenu());
smenu.Serialize(m_archive);
smenu.Detach();
CSerializableMenu smenu;
smenu.Serialize(m_archive);
m_menu.Attach(smenu.Detach());
Class Innards Dissected
CSerializableMenu
implements serialization through the use of the member function LoadMenuIndirect()
, together with the WIN32 structures MENUITEMTEMPLATEHEADER
and MENUITEMTEMPLATE
. The two structures are defined as follows:
typedef struct {
WORD versionNumber;
WORD offset;
} MENUITEMTEMPLATEHEADER, *PMENUITEMTEMPLATEHEADER;
typedef struct {
WORD mtOption;
WORD mtID;
WCHAR mtString[1];
} MENUITEMTEMPLATE, *PMENUITEMTEMPLATE;
The MENUITEMTEMPLATEHEADER
structure defines the header for a menu template. A complete menu template consists of a header and one or more menu items (i.e., MENUITEMTEMPLATE
).

For example, the popup menu shown above will be serialized into the following structure (where MITH stands for MenuItemTemplateHeader
and MIT stands for MenuItemTemplate
. Also note that a separator takes up one MIT):
HEADER (MITH) Toolbars (MIT) Address (MIT) Links (MIT) Add Quick Search... (MIT) Desktop (MIT) Quick Launch (MIT) Search (MIT) -SEPARATOR- (MIT) New Toolbar... (MIT) -SEPARATOR- (MIT) Adjust Date/Time (MIT) Cascade Windows (MIT) Tile Windows Horizontally (MIT) Tile Windows Vertically (MIT) -SEPARATOR- (MIT) Minimize All Windows (MIT) -SEPARATOR- (MIT) Task Manager... (MIT) -SEPARATOR- (MIT) Properties (MIT)
Like other serializable classes in the MFC, CSerializableMenu
exposes the public method Serialize()
and the overloaded operators >>
and <<
. (Overloaded operators >>
and <<
are defined by the macros DECLARE_SERIAL
and IMPLEMENT_SERIAL
.)
Class Header File
class CSerializableMenu : public CMenu
{
public:
DECLARE_SERIAL(CSerializableMenu)
CSerializableMenu() {};
virtual ~CSerializableMenu() {};
public:
protected:
LPBYTE GetMenuTemplate(DWORD* dwLen);
void FillMenuTemplate(CMemFile* pFile, CMenu* pMenu);
public:
void Serialize(CArchive &ar);
protected:
};
Class Source File
#include "stdafx.h"
#include <afxcoll.h>
#include <afxpriv.h>
#include "SerializableMenu.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
IMPLEMENT_SERIAL( CSerializableMenu, CMenu, 1)
LPBYTE CSerializableMenu::GetMenuTemplate(DWORD* dwLen)
{
CMemFile memFile;
MENUITEMTEMPLATEHEADER mitHeader;
mitHeader.versionNumber = 0;
mitHeader.offset = 0;
memFile.Write(&mitHeader, sizeof(MENUITEMTEMPLATEHEADER));
FillMenuTemplate(&memFile, this);
*dwLen = memFile.GetLength();
return memFile.Detach();
}
void CSerializableMenu::FillMenuTemplate(CMemFile* pFile, CMenu* pMenu)
{
USES_CONVERSION;
_ASSERTE(pMenu != NULL);
CString tmpStr;
LPCWSTR wszTmp = NULL;
WORD mt;
int nSize = pMenu->GetMenuItemCount();
for (int i=0; i<nSize; i++)
{
mt = (WORD) pMenu->GetMenuState(i, MF_BYPOSITION);
if (mt & MF_POPUP)
mt &= ~0xFF00;
if (i == nSize-1)
mt |= MF_END;
pFile->Write(&mt, sizeof(WORD));
if (!(mt & MF_POPUP))
{
WORD cmdID = (WORD) pMenu->GetMenuItemID(i);
pFile->Write(&cmdID, sizeof(WORD));
}
pMenu->GetMenuString(i, tmpStr, MF_BYPOSITION);
wszTmp = T2CW(tmpStr);
pFile->Write(wszTmp, (tmpStr.GetLength()+1)*sizeof(WCHAR));
if (mt & MF_POPUP)
FillMenuTemplate(pFile, pMenu->GetSubMenu(i));
}
}
void CSerializableMenu::Serialize(CArchive &ar)
{
CMenu::Serialize(ar);
if (ar.IsLoading())
{
DestroyMenu();
DWORD dwSize;
ar.Read(&dwSize, sizeof(DWORD));
LPBYTE pBuf = new BYTE [dwSize];
ar.Read(pBuf, dwSize);
LoadMenuIndirect(pBuf);
delete [] pBuf;
}
else
{
DWORD dwSize;
LPBYTE pBuf = GetMenuTemplate(&dwSize);
ar.Write(&dwSize, sizeof(DWORD));
ar.Write(pBuf, dwSize);
free(pBuf);
}
}
License
This article has no explicit license attached to it, but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below.
A list of licenses authors might use can be found here.
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.