为CMenu添加序列化支持






4.69/5 (6投票s)
2001年12月5日
2分钟阅读

73716

939
CMenu 类在操作菜单时非常有用,但不幸的是它没有实现序列化。CSerializableMenu 是 CMenu 的一个子类,它提供了序列化支持。
引言
`CMenu` 类在操作菜单时非常有用,但不幸的是它在序列化方面并没有提供太多帮助。
另一方面,WIN32 SDK 以结构体 `MENUITEMTEMPLATEHEADER` 和 `MENUITEMTEMPLATE`,以及函数 `LoadMenuIndirect()` 的形式提供了帮助。但是这些结构体相当复杂,使用起来非常麻烦。
`CSerializableMenu` 是 `CMenu` 的一个子类,它提供了序列化支持,通过以通常的 MFC 方式封装 WIN32 实现的所有麻烦细节(即,直接通过调用重写的 `public` 方法 `Serialize()` 或间接通过调用重载的运算符 `>>` 和 `<<`)。
用法
`CSerializableMenu` 类可以用作代码中 `CMenu` 类的直接替代品。要执行序列化,您可以直接调用方法 `Serialize()` 或使用重载的运算符 `>>` 和 `<<`。
如果您不想替换代码中的 `CMenu` 类,您仍然可以使用 `CSerializableMenu` 类来实现其序列化支持,方法如下
//assuming that you already have a CMenu object called m_menu //(or even a raw handle to a HMENU called m_hMenu) //and a CArchive object called m_archive //to store the menu CSerializableMenu smenu; smenu.Attach(m_menu.GetSafeHmenu()); //or smenu.Attach(m_hMenu); smenu.Serialize(m_archive); // or m_archive << smenu; smenu.Detach(); //to restore the menu CSerializableMenu smenu; smenu.Serialize(m_archive); // or m_archive >> smenu; m_menu.Attach(smenu.Detach()); //or m_hMenu = smenu.Detach();
类内部剖析
`CSerializableMenu` 通过使用成员函数 `LoadMenuIndirect()` 以及 WIN32 结构体 `MENUITEMTEMPLATEHEADER` 和 `MENUITEMTEMPLATE` 来实现序列化。这两个结构体的定义如下
typedef struct { WORD versionNumber; //must be 0 WORD offset; //byte offset of the first MENUITEMTEMPLATE after this structure } MENUITEMTEMPLATEHEADER, *PMENUITEMTEMPLATEHEADER; typedef struct { WORD mtOption; // OR flags controlling the appearance of the menu item WORD mtID; //menu item identifier of a command item WCHAR mtString[1]; //null-terminated string for the menu item } MENUITEMTEMPLATE, *PMENUITEMTEMPLATE;
`MENUITEMTEMPLATEHEADER` 结构体定义了菜单模板的标头。一个完整的菜单模板由一个标头和一个或多个菜单项(即,`MENUITEMTEMPLATE`)组成。
例如,上面显示的弹出菜单将被序列化为以下结构(其中 MITH 代表 `MenuItemTemplateHeader`,MIT 代表 `MenuItemTemplate`。另请注意,分隔符占用一个 MIT)
HEADER (MITH) 工具栏 (MIT) 地址 (MIT) 链接 (MIT) 添加快速搜索... (MIT) 桌面 (MIT) 快速启动 (MIT) 搜索 (MIT) -分隔符- (MIT) 新工具栏... (MIT) -分隔符- (MIT) 调整日期/时间 (MIT) 级联窗口 (MIT) 水平平铺窗口 (MIT) 垂直平铺窗口 (MIT) -分隔符- (MIT) 最小化所有窗口 (MIT) -分隔符- (MIT) 任务管理器... (MIT) -分隔符- (MIT) 属性 (MIT)
与 MFC 中的其他可序列化类一样,`CSerializableMenu` 公开了公共方法 `Serialize()` 和重载的运算符 `>>` 和 `<<`。(重载运算符 `>>` 和 `<<` 由宏 `DECLARE_SERIAL` 和 `IMPLEMENT_SERIAL` 定义。)
类头文件
class CSerializableMenu : public CMenu { public: DECLARE_SERIAL(CSerializableMenu) CSerializableMenu() {}; virtual ~CSerializableMenu() {}; //operations public: //internal implementations protected: //helper functions for serialization support LPBYTE GetMenuTemplate(DWORD* dwLen); void FillMenuTemplate(CMemFile* pFile, CMenu* pMenu); //overrides public: void Serialize(CArchive &ar); //attributes protected: };
类源文件
#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) ////////////////////////////////////////////////////////////////////// // Internal helper mtds ////////////////////////////////////////////////////////////////////// /** * This method will create the memory structure that represents * the menu wrapped by this class. * @param dwLen [in/out] pointer to a DWORD that will receive the size * of the structure allocated. * @return a BYTE array containing the created structure. **/ LPBYTE CSerializableMenu::GetMenuTemplate(DWORD* dwLen) { //since we have no idea whats the total size of //the structure will be, lets use a memory file instead CMemFile memFile; //create and initialize the header structure MENUITEMTEMPLATEHEADER mitHeader; mitHeader.versionNumber = 0; //as required by SDK mitHeader.offset = 0; //implies that the menu items come next //write it to our memory file memFile.Write(&mitHeader, sizeof(MENUITEMTEMPLATEHEADER)); //lets get a helper function to fill up the menu items FillMenuTemplate(&memFile, this); //update the length variable *dwLen = memFile.GetLength(); //and return the BYTE array return memFile.Detach(); } /** * This is a recursive method that will populate the given memory file * with the menu items (including all submenus). * @param pFile [in] pointer to the memory file * @param pMenu [in] pointer to the menu **/ void CSerializableMenu::FillMenuTemplate(CMemFile* pFile, CMenu* pMenu) { USES_CONVERSION; //need this for the conversion macro to work _ASSERTE(pMenu != NULL); CString tmpStr; LPCWSTR wszTmp = NULL; WORD mt; int nSize = pMenu->GetMenuItemCount(); //loop thru all the menu items in this level for (int i=0; i<nSize; i++) { //first, get the menu state and store it mt = (WORD) pMenu->GetMenuState(i, MF_BYPOSITION); if (mt & MF_POPUP) //need to mask out the high order byte if its a popup //cuz its contains the number of items in the popup //which we are not interested in mt &= ~0xFF00; if (i == nSize-1) //is last item, so add the flag MF_END mt |= MF_END; pFile->Write(&mt, sizeof(WORD)); //if its NOT a popup, we should store the command ID as well if (!(mt & MF_POPUP)) { WORD cmdID = (WORD) pMenu->GetMenuItemID(i); pFile->Write(&cmdID, sizeof(WORD)); } //now, lets get the menu string and store it pMenu->GetMenuString(i, tmpStr, MF_BYPOSITION); wszTmp = T2CW(tmpStr); //+1 to include the null terminator pFile->Write(wszTmp, (tmpStr.GetLength()+1)*sizeof(WCHAR)); if (mt & MF_POPUP) //is a popup, so add in the submenus FillMenuTemplate(pFile, pMenu->GetSubMenu(i)); } } ////////////////////////////////////////////////////////////////////// // overrides ////////////////////////////////////////////////////////////////////// /** * This is THE method that does serialization. * @param ar [in/out] a reference to the CArchive object that will * store/load the menu **/ void CSerializableMenu::Serialize(CArchive &ar) { //get the base class to do its thing first CMenu::Serialize(ar); if (ar.IsLoading()) //loading the menu from storage { //destroy (any) old menu first DestroyMenu(); DWORD dwSize; //first, lets read the size of the structure ar.Read(&dwSize, sizeof(DWORD)); //next, we allocate a space in memory that is large enough //to hold the structure LPBYTE pBuf = new BYTE [dwSize]; //lets read the structure proper ar.Read(pBuf, dwSize); //get the member function to create/load the menu LoadMenuIndirect(pBuf); //cleanup delete [] pBuf; } else //storing the menu into storage { DWORD dwSize; //lets get our helper function to create the BYTE array for us LPBYTE pBuf = GetMenuTemplate(&dwSize); //first, write the size of the buffer into the archive ar.Write(&dwSize, sizeof(DWORD)); //next, write the structure into the archive ar.Write(pBuf, dwSize); //cleanup free(pBuf); //CMemFile uses malloc so we have to use free here } }
许可证
本文未附加明确的许可证,但可能在文章文本或下载文件本身中包含使用条款。如有疑问,请通过下面的讨论区联系作者。
作者可能使用的许可证列表可以在此处找到。