标题：CMenu
定义
对于系统菜单，创建起来比较简单，直接使用资源编辑器就能生成菜单，再通过ClassWizard创建菜单命令函数。在我的资源中上传了一个工程，实现了一个右键弹出贴图菜单用资源编辑器创建菜单(5张)。结合这个工程，介绍动态创建菜单、创建弹出式菜单和重绘菜单。
首先介绍基础知识：
CMenu类的成员函数：
1. CreateMenu()和CreatePopupMenu(),这两个函数用来创建一个菜单实例，CreateMenu()创建的是普通的菜单实例，如果想创建弹出式菜单，就要用CreatePopupMenu()函数。
2. AppendMenu()向菜单中添加一个子项，这个函数有两个主要的参数。第一个UINT nFlags，这个参数表明了该子项的属性特征，可以这样说，这个参数规定了菜单的样式和功能。后面会详细讲这个参数所能使用的值。第二个参数UINT_PTR nIDNewItem，根据nFlags使用不同的设置，该参数将标明菜单的资源ID或在这个菜单中的索引号。第三个参数可以省略，如果不省略，可以传入一个字符串，这个字符串将显示在菜单中（因为我准备用突破表示菜单项，所以我的工程中省略了这个参数）。
3. DrawItem()，这是一个虚函数，如果菜单设置成可以自绘类型，则这个函数将在生成菜单、弹出菜单、选中菜单、点击菜单等时由系统框架调用。因此，这个函数是一个很有用的函数，它可以帮你绘制出各种样式的菜单。
4. MeasureItem()也是一个虚函数，当菜单被创建的时候由系统框架调用。这个函数用来设置菜单的大小。
nFlags说明：
只有当nFlags设置成MF_OWNERDRAW的时候，系统框架才会重绘菜单。
MF_CHECKED：命令旁显示默认复选标志
MF_UNCHECKED：清除命令旁的复选标志
MF_DISABLED：禁止此菜单命令，但是不变灰显示
MF_ENABLED：允许此菜单命令，恢复到正常状态
MF_GRAYED：禁止此菜单命令，变灰显示
MF_MENUBARBREAK：对于静态菜单，放到新行；对于弹出菜单，放到新栏 中，栏间有分隔线
MF_MENUBREAK：对于静态菜单，放到新行；对于弹出菜单，放到新栏，栏间无分隔线
MF_OWNERDRAW：指定该命令是自画式菜单命令
MF_POPUP：指定该菜单命令有一个关联的弹出式菜单
MF_SEPARATOR：画一条水平分隔线，只用于弹出式菜单
MF_STRING：指定此菜单命令是一个字符串
CMenu类从CObject类派生而来。为什么要使用CMenu类呢？AppWzard不是把菜 单做好了吗？在资源编辑器上修改菜单不是很方便吗？
感觉学vc++稍微深入一点好，至少要能搞清楚AppWizard在背后都干了些什么东西。
事实上mfc就是用CMenu类来生成菜单的。让我们就从CMenu开始吧。
CMenu生成的菜单有两种：Popup类型和非Popup类型。这两种方法里又可以分
成使用资源编辑器生成的菜单资源和不使用这个资源。对于非Popup类型的菜单，
必须在创建出来后把它张贴到某个窗口上，它才会显示出来，从而才有用处。Po
pup的菜单却不能张贴到窗口上。
说明之前，先定义几个常量：
#define IDM_MENU0 0
#define IDM_MENU1 1
#define IDM_MENU2 2
#define IDM_MENU3 3
#define IDM_ITEM0 10
#define IDM_ITEM1 11
#define IDM_ITEM2 12
#define IDM_ITEM3 13
#define IDM_ITEM4 14
#define IDM_ITEM5 15
#define IDM_ITEM6 16
一。创建非Popup类型菜单，不使用资源。
(一)创建非下拉菜单。
1。在窗口类的OnCreate函数里创建CMenu对象。如果是创建运用程序主框架窗口
的话，也可以在InitInstance()函数里。
2。声明一个CMenu对象:CMenu MyMenu;
3。调用MyMenu.CreateMenu()或MyMenu.LoadMenu()
4。调用若干次MyMenu.AppendMenu()或MyMenu.InsertMenu()，每调用一次创建一
个菜单项。
5。调用MyMneu.SetMenu()将菜单Attach到窗口上。
6。调用MyMenu.Detach()。
例子：
int CMyWnd::OnCreate( LPCREATESTRUCT lpCreateStruct )
{
{
CMenu MyMenu;
MyMenu.CreateMenu();
MyMenu.AppendMenu(MF_STRING,IDM_MENU0,"文件");
MyMenu.AppendMenu(MF_STRING,IDM_MENU1,"编辑");
MyMenu.AppendMenu(MF_STRING,IDM_MENU2,"查看");
MyMenu.AppendMenu(MF_STRING,IDM_MENU3,"帮助");
MyMenu.InsertMenu(IDM_MENU2,MF_BYCOMMAND,IDM_ITEM0,"有关");
this->SetMenu(&MyMenu);
MyMenu.Detach();
return 0;
}//各个函数的细节就不讲解了，看联机帮助是最好的。
这个方法是先把菜单创建好后再贴到窗口上去，然后用Detach()使菜单和My
Menu对象脱离关系，因为MyMenu对象马上就要超出作用域了，这一步是必须的。
(二)创建下拉菜单，不使用资源。
这种菜单当鼠标移动到菜单条目上面点击时不是去执行某段程序，而是弹出
一个下拉菜单。这需要用前面的方法创建两个菜单。第一个是鼠标未点击时看到
的那个菜单，另一个就是扮演下拉菜单的菜单。例子：
int CMyWnd::OnCreate( LPCREATESTRUCT lpCreateStruct )
{
CMenu MyMenu0,MyMenu1;
//下面这几条创建下拉菜单
MyMenu1.CreateMenu();
MyMenu1.AppendMenu(MF_STRING,IDM_ITEM0,"拷贝");
MyMenu1.AppendMenu(MF_STRING,IDM_ITEM1,"剪切");
MyMenu1.AppendMenu(MF_STRING,IDM_ITEM2,"粘贴");
MyMenu1.AppendMenu(MF_SEPARATOR,IDM_ITEM3,"");
MyMenu1.AppendMenu(MF_STRING,IDM_ITEM4,"全选");
MyMenu1.AppendMenu(MF_SEPARATOR,IDM_ITEM5,"");
MyMenu1.AppendMenu(MF_STRING,IDM_ITEM6,"删除");
//下面这两条创建鼠标未点击时看到的那个菜单
//其中第二句将下拉菜单张贴到第一个菜单上。
MyMenu0.CreateMenu();
MyMenu0.AppendMenu(MF_POPUP,(UINT)MyMenu1.m_hMenu,"编辑");
this->SetMenu(&MyMenu0);//将菜单张贴到窗口上
MyMenu0.Detach();//必须有
MyMenu1.Detach();//必须有
return 0;
}
二。创建Popup类型的菜单，也不用资源。
很多程序里，只要用鼠标右键点一下窗口客户区，就会在鼠标的位置弹出一
个菜单，这叫右键菜单。我们可以用CMenu类来制作。
制作这种菜单比制作第一类菜单稍微复杂点。首先要在窗口类里加个成员变
量：CMenu *MyMenu2;
然后在窗口类的构造函数里(或OnCreate()函数里)加上创建菜单的语句，再
在析构函数里加上销毁菜单的语句，最后在OnRButtonDown()函数里加上显示菜单
的语句。
创建菜单时，CMenu类对象应该用new来分配。
例子：
CMyWnd::CMyWnd()
{
//CMyWnd是从CWnd派生来的。
//先把菜单创建起来。
MyMenu2=new CMenu;
MyMenu2->CreatePopupMenu();
MyMenu2->AppendMenu(MF_STRING,IDM_ITEM0,"拷贝");
MyMenu2->AppendMenu(MF_STRING,IDM_ITEM1,"剪切");
MyMenu2->AppendMenu(MF_STRING,IDM_ITEM2,"粘贴");
MyMenu2->AppendMenu(MF_SEPARATOR,IDM_ITEM3,"");
MyMenu2->AppendMenu(MF_STRING,IDM_ITEM4,"全选");
MyMenu2->AppendMenu(MF_SEPARATOR,IDM_ITEM3,"");
MyMenu2->AppendMenu(MF_STRING,IDM_ITEM5,"删除");
}
CMyWnd::~CMyWnd()
{
MyMenu2->DestroyMenu();//销毁菜单所占用的系统资源
delete MyMenu2;//销毁菜单类对象
}
void CMyWnd::OnRButtonDown(UINT nFlags, CPoint point)
{
RECT rect;
GetWindowRect(&rect);
//显示菜单
MyMenu2->TrackPopupMenu(TPM_RIGHTALIGN,point.x+rect.left,point.y+
rect.top,this,NULL);
}三。如果使用资源的话，创建菜单确实非常简单了，只须在窗口类的OnCreate()
函数里加几句话就行了：
int CMyWnd::OnCreate( LPCREATESTRUCT lpCreateStruct )
{
CMenu MyMenu3;
MyMenu3.LoadMenu(IDR_MENU1);//IDR_MENU1是你的菜单的资源ID。
this->SetMenu(&MyMenu3);
MyMenu3.Detach();
return 0;
}
CMenu类还有很多成员函数，使你可以在运行中对菜单进行裁剪，比如加上几
项或减去几项等等，使用非常方便。大家可以去看msdn。
如果要实验以上的菜单创建方法的话，可以用一个非常简单的mfc程序来搞：
//这是一个非常简单的mfc程序，必要的函数自己去加吧。
#include
class CMyApp : public CWinApp
{
public:
virtual BOOL InitInstance();
};
};
class CMyWnd : public CWnd
{
public:
DECLARE_MESSAGE_MAP()
};
CMyApp MyApp;
BEGIN_MESSAGE_MAP(CMyWnd,CWnd)
END_MESSAGE_MAP()
BOOL CMyApp::InitInstance()
{
RECT rect={30,30,400,300};
CMyWnd* pCWindow=new CMyWnd;
pCWindow->CreateEx
(
NULL,
AfxRegisterWndClass(NULL,0,(HBRUSH)::GetStockObject(WHITE_BRU
SH),0),
"实验程序",
WS_OVERLAPPEDWINDOW,
rect,NULL,NULL,NULL
);
m_pMainWnd = pCWindow;
pCWindow->ShowWindow(m_nCmdShow);
pCWindow->UpdateWindow();
return TRUE; 
CMenu类为Windows HMENU的封装类。它提供了成员函数以用于创建、追踪、更新及销毁菜单。
在本地的堆栈框架中创建一个CMenu对象，然后调用CMenu的成员函数来操纵所需的新菜单。接着，调用CWnd::SetMenu函数为窗口设置菜单。然后立即调用CMenu对象的Detach成员函数。CWnd::SetMenu成员函数将窗口的菜单设置为新菜单，这将导致在窗口刷新后将影响菜单的改变，同时也将菜单的拥有者传递给窗口。调用Detach函数将把HMENU从CMenu对象中分离出来，以便当本地的CMenu变量超出范围后，CMenu对象的构造函数将不会销毁不再拥有的菜单。当窗口销毁后，菜单自动销毁。
可以调用LoadMenuIndirect成员函数在内存中创建来自模板的菜单，不过通过调用LoadMenu创建的菜单更容易维护。并且这种菜单资源本身也可以由菜单编辑器创建或修改。
#include <afxwin.h>
CMenu类的成员
数据成员
m_hMenu
指定附加给CMenu对象的窗口菜单的句柄
构造函数
CMenu
构造一个CMenu对象
初始化
Attach
附加一个Windows菜单句柄给CMenu对象
Detach
从CMenu对象中分离Windows菜单的句柄，并返回该句柄
FromHandle
返回一个指向给定Windows菜单句柄的CMenu对象的指针
GetSafeHmenu
返回由CMenu对象包含的m_hMenu值
DeleteTempMap
删除由FromHandle成员函数创建的所有临时CMenu对象
CreateMenu
创建一个空菜单，并将其附加给CMenu对象
CreatePopupMenu
创建一个空的弹出菜单，并将其附加给CMenu对象
LoadMenu
从可执行文件中装载菜单资源，并将其附加给CMenu对象LoadMenuIndirect从内存的菜单模板中装载菜单，并将其附加给CMenu对象
DestroyMenu
销毁附加给CMenu对象的菜单，并释放菜单占用的内存
菜单操作
DeleteMenu
从菜单中删除指定的项。如果菜单项与弹出菜单相关联，那么将销毁弹出菜单的句柄，并释放它占用的内存
TrackPopupMenu
在指定的位置显示浮动菜单，并跟踪弹出菜单的选择项
菜单项操作
AppendMenu
在该菜单末尾添加新的菜单项
CheckMenuItem
在弹出菜单的菜单项中放置或删除检测标记
CheckMenuRadioItem
将单选钮放置在菜单项之前，或从组中所有的其它菜单项中删除单选钮
SetDefaultItem
为指定的菜单设置缺省的菜单项GetDefaultItem获取指定的菜单缺省的菜单项
EnableMenuItem
使菜单项有效、无效或变灰GetMenuItemCount决定弹出菜单或顶层菜单的项数
GetMenuItemID
获取位于指定位置菜单项的菜单项标识
GetMenuState
返回指定菜单项的状态或弹出菜单的项数
GetMenuString
获取指定菜单项的标签GetMenuItemInfo获取有关菜单项的信息
GetSubMenu
获取指向弹出菜单的指针
InsertMenu
在指定位置插入新菜单项，并顺次下移其它菜单项
ModifyMenu
改变指定位置的已存在的菜单项
RemoveMenu
从指定的菜单中删除与弹出菜单相关联的菜单项
SetMenuItemBitmaps
将指定检测标记的位图与菜单项关联
GetMenuCountextHelpID
获取与菜单关联的帮助文本的ID号
SetMenuCountextHelpID
设置与菜单关联的帮助文本的ID号
可覆盖的函数
DrawItem
通过框架来调用，其发生于拥有者菜单的可视部分有所改变
MeasureItem
通过框架来调用，用于决定当创建了拥有者菜单时的菜单维数