学习win设一学期有余,无奈学业繁重,资质鲁钝,对许多问题只是一知半解!现考试临近,特将心中不解贴出,希望各位高手不吝出手,予我等菜鸟一臂之力,小弟拜谢了!
第四问: 在MFC的消息映射中,将消息分成哪几类?
ps 如是粘贴他人或书本上的回答,请注明出处。问题搞清楚,立即结贴,多谢了!

解决方案 »

  1.   

    命令訊息(WM_COMMAND):命令訊息意味著「使用者命令程式做某些動作」。
    凡由UI 物件產生的訊息都是這種命令訊息,可能來自選單或加速鍵或工具列
    按鈕,並且都以WM_COMMAND 呈現。如何分辨來自各處的命令訊息?SDK
    程式主要靠訊息的wParam 辨識之,MFC 程式則主要靠選單項目的識別碼
    (menu ID)辨識之-- 兩者其實是相同的。
    什麼樣的類別有資格接受命令訊息?凡衍生自CCmdTarget 之類別,皆有資格。從
    command target 的字面意義可知,這是命令訊息的目的㆞。也就是說,凡衍生自
    CCmdTarget 者,它的骨子裡就有了㆒種特殊的機制。把整張MFC 類別階層圖攤開
    來看,幾乎建構應用程式的最重要的幾個類別都衍生自CCmdTarget,剩㆘的不能接
    收訊息的,是像CFile、CArchive、CPoint、CDao(資料庫)、Collection Classes(純
    粹資料處理)、GDI 等等「非主流」類別。
      標準訊息- 除WM_COMMAND 之外,任何以WM_ 開頭的都算是這㆒類。任何
    衍生自CWnd 之類別,均可接收此訊息。
      Control Notification - 這種訊息由控制元件產生,為的是向其父視窗(通常是對
    話盒)通知某種情況。例如當你在ListBox ㆖選擇其㆗㆒個項目,ListBox 就
    會產生LBN_SELCHANGE 傳送給父視窗。這類訊息也是以WM_COMMAND 形
    式呈現。
      

  2.   

    我记得是两类吧,WM_COMMAND和非WM_COMMAND这两类。
      

  3.   

    消息映射的实现 
    Windows消息概述 Windows 应用程序的输入由Windows系统以消息的形式发送给应用程序的窗口。这些窗口通过窗口过程来接收和处理消息,然后把控制返还给Windows。 消息的分类 
    队列消息和非队列消息 从消息的发送途径上看,消息分两种:队列消息和非队列消息。队列消息送到系统消息队列,然后到线程消息队列;非队列消息直接送给目的窗口过程。这里,对消息队列阐述如下:Windows 维护一个系统消息队列(System message queue),每个GUI线程有一个线程消息队列(Thread message queue)。 
    鼠 标、键盘事件由鼠标或键盘驱动程序转换成输入消息并把消息放进系统消息队列,例如WM_MOUSEMOVE、WM_LBUTTONUP、WM_KEYDOWN、WM_CHAR等等。Windows每次从系统消息队列移走一个消息,确定它是送给哪个窗口的和这个窗口是由哪个线程创建的,然后,把它放进窗口创建线程的线程消息队列。线程消息队列接收送给该线程所创建窗口的消息。线程从消息队列取出消息,通过Windows把它送给适当的窗口过程来处理。 
    除了键盘、鼠标消息以外,队列消息还有 WM_PAINT、WM_TIMER和WM_QUIT。 
    这些队列消息以外的绝大多数消息是非队列消息。
    系统消息和应用程序消息 从消息的来源来看,可以分为:系统定义的消息和应用程序定义的消息。系统消息ID的范围是从0到WM_USER-1,或0X80000到0XBFFFF;应用程序消息从WM_USER(0X0400)到0X7FFF,或0XC000到0XFFFF;WM_USER到0X7FFF范围的消息由应用程序自己使用;0XC000到0XFFFF范围的消息用来和其他应用程序通信,为了ID的唯一性,使用::RegisterWindowMessage来得到该范围的消息ID。
    消息结构和消息处理 
    消息的结构 为了从消息队列获取消息信息,需要使用MSG结构。例如,::GetMessage函数(从消息队列得到消息并从队列中移走)和::PeekMessage函数(从消息队列得到消息但是可以不移走)都使用了该结构来保存获得的消息信息。MSG 结构的定义如下: 
    typedef struct tagMSG { // msg HWND hwnd; 
    UINT message; WPARAM wParam; LPARAM lParam; DWORD time; POINT pt; } MSG; 
    该结构包括了六个成员,用来描述消息的有关属性:接收消息的窗口句柄、消息标识( ID)、第一个消息参数、第二个消息参数、消息产生的时间、消息产生时鼠标的位置。 应用程序通过窗口过程来处理消息 如前所述,每个“窗口类”都要登记一个如下形式的窗口过程:LRESULT CALLBACK MainWndProc (HWND hwnd,// 窗口句柄 
    UINT msg,// 消息标识 
    WPARAM wParam,// 消息参数1 
    LPARAM lParam// 消息参数2 
    )应用程序通过窗口过程来处理消息:非队列消息由 Windows直接送给目的窗口的窗口过程,队列消息由::DispatchMessage等派发给目的窗口的窗口过程。窗口过程被调用时,接受四个参数: 
    a window handle (窗口句柄); 
    a message identifier (消息标识); 
    two 32-bit values called message parameters (两个32位的消息参数); 
    需要的话,窗口过程用 ::GetMessageTime获取消息产生的时间,用::GetMessagePos获取消息产生时鼠标光标所在的位置。 
    在窗口过程里,用 switch/case分支处理语句来识别和处理消息。 应用程序通过消息循环来获得对消息的处理 每个GDI应用程序在主窗口创建之后,都会进入消息循环,接受用户输入、解释和处理消息。消息循环的结构如下:while (GetMessage(&msg, (HWND) NULL, 0, 0)) {// 从消息队列得到消息 
    if (hwndDlgModeless == (HWND) NULL || !IsDialogMessage(hwndDlgModeless, &msg) && !TranslateAccelerator(hwndMain, haccel, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); // 发送消息 

    }消息循环从消息队列中得到消息,如果不是快捷键消息或者对话框消息,就进行消息转换和派发,让目的窗口的窗口过程来处理。当得到消息 WM_QUIT,或者::GetMessage出错时,退出消息循环。 MFC消息处理 使用MFC框架编程时,消息发送和处理的本质也如上所述。但是,有一点需要强调的是,所有的MFC窗口都使用同一窗口过程,程序员不必去设计和实现自己的窗口过程,而是通过MFC提供的一套消息映射机制来处理消息。因此,MFC简化了程序员编程时处理消息的复杂性。所谓消息映射,简单地讲,就是让程序员指定要某个MFC类(有消息处理能力的类)处理某个消息。MFC提供了工具ClassWizard来帮助实现消息映射,在处理消息的类中添加一些有关消息映射的内容和处理消息的成员函数。程序员将完成消息处理函数,实现所希望的消息处理能力。如果派生类要覆盖基类的消息处理函数,就用ClassWizard在派生类中添加一个消息映射条目,用同样的原型定义一个函数,然后实现该函数。这个函数覆盖派生类的任何基类的同名处理函数。
    下面几节将分析MFC的消息机制的实现原理和消息处理的过程。为此,首先要分析ClassWizard实现消息映射的内幕,然后讨论MFC的窗口过程,分析MFC窗口过程是如何实现消息处理的。
      

  4.   

    消息映射的定义和实现 
    MFC处理的三类消息 根据处理函数和处理过程的不同, MFC主要处理三类消息: Windows 消息,前缀以“WM_”打头,WM_COMMAND例外。Windows消息直接送给MFC窗口过程处理,窗口过程调用对应的消息处理函数。一般,由窗口对象来处理这类消息,也就是说,这类消息处理函数一般是MFC窗口类的成员函数。 
    控制通知消息,是控制子窗口送给父窗口的 WM_COMMAND通知消息。窗口过程调用对应的消息处理函数。一般,由窗口对象来处理这类消息,也就是说,这类消息处理函数一般是MFC窗口类的成员函数。 需要指出的是,Win32使用新的WM_NOFITY来处理复杂的通知消息。WM_COMMAND类型的通知消息仅仅能传递一个控制窗口句柄(lparam)、控制窗ID和通知代码(wparam)。WM_NOTIFY能传递任意复杂的信息。
    命令消息,这是来自菜单、工具条按钮、加速键等用户接口对象的WM_COMMAND通知消息,属于应用程序自己定义的消息。通过消息映射机制,MFC框架把命令按一定的路径分发给多种类型的对象(具备消息处理能力)处理,如文档、窗口、应用程序、文档模板等对象。能处理消息映射的类必须从CCmdTarget类派生。 在讨论了消息的分类之后,应该是讨论各类消息如何处理的时候了。但是,要知道怎么处理消息,首先要知道如何映射消息。
    MFC消息映射的实现方法 MFC 使用ClassWizard帮助实现消息映射,它在源码中添加一些消息映射的内容,并声明和实现消息处理函数。现在来分析这些被添加的内容。 
    在类的定义(头文件)里,它增加了消息处理函数声明,并添加一行声明消息映射的宏 DECLARE_MESSAGE_MAP。 
    在类的实现(实现文件)里,实现消息处理函数,并使用 IMPLEMENT_MESSAGE_MAP宏实现消息映射。一般情况下,这些声明和实现是由MFC的ClassWizard自动来维护的。看一个例子: 
    在 AppWizard产生的应用程序类的源码中,应用程序类的定义(头文件)包含了类似如下的代码: 
    //{{AFX_MSG(CTttApp)afx_msg void OnAppAbout();//}}AFX_MSGDECLARE_MESSAGE_MAP()
    应用程序类的实现文件中包含了类似如下的代码:BEGIN_MESSAGE_MAP(CTApp, CWinApp)//{{AFX_MSG_MAP(CTttApp)ON_COMMAND(ID_APP_ABOUT, OnAppAbout)//}}AFX_MSG_MAPEND_MESSAGE_MAP()
    头文件里是消息映射和消息处理函数的声明,实现文件里是消息映射的实现和消息处理函数的实现。它表示让应用程序对象处理命令消息 ID_APP_ABOUT,消息处理函数是OnAppAbout。 
    为什么这样做之后就完成了一个消息映射?这些声明和实现到底作了些什么呢?接着,将讨论这些问题。
    在声明与实现的内部 
    DECLARE_MESSAGE_MAP宏: 首先,看DECLARE_MESSAGE_MAP宏的内容:#ifdef _AFXDLL#define DECLARE_MESSAGE_MAP() \private: \static const AFX_MSGMAP_ENTRY _messageEntries[]; \protected: \static AFX_DATA const AFX_MSGMAP messageMap; \static const AFX_MSGMAP* PASCAL _GetBaseMessageMap(); \virtual const AFX_MSGMAP* GetMessageMap() const; \
    #else#define DECLARE_MESSAGE_MAP() \private: \static const AFX_MSGMAP_ENTRY _messageEntries[]; \protected: \static AFX_DATA const AFX_MSGMAP messageMap; \virtual const AFX_MSGMAP* GetMessageMap() const; \
    #endifDECLARE_MESSAGE_MAP 定义了两个版本,分别用于静态或者动态链接到MFC DLL的情形。 BEGIN_MESSAE_MAP宏 然后,看BEGIN_MESSAE_MAP宏的内容:#ifdef _AFXDLL#define BEGIN_MESSAGE_MAP(theClass, baseClass) \const AFX_MSGMAP* PASCAL theClass::_GetBaseMessageMap() \{ return &baseClass::messageMap; } \const AFX_MSGMAP* theClass::GetMessageMap() const \{ return &theClass::messageMap; } \AFX_DATADEF const AFX_MSGMAP theClass::messageMap = \{ &theClass::_GetBaseMessageMap, &theClass::_messageEntries[0] }; \const AFX_MSGMAP_ENTRY theClass::_messageEntries[] = \{ \
    #else#define BEGIN_MESSAGE_MAP(theClass, baseClass) \const AFX_MSGMAP* theClass::GetMessageMap() const \{ return &theClass::messageMap; } \AFX_DATADEF const AFX_MSGMAP theClass::messageMap = \{ &baseClass::messageMap, &theClass::_messageEntries[0] }; \const AFX_MSGMAP_ENTRY theClass::_messageEntries[] = \{ \
    #endif
    #define END_MESSAGE_MAP() \{0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \}; \对应地, BEGIN_MESSAGE_MAP定义了两个版本,分别用于静态或者动态链接到MFC DLL的情形。END_MESSAGE_MAP相对简单,就只有一种定义。 ON_COMMAND宏 最后,看ON_COMMAND宏的内容:#define ON_COMMAND(id, memberFxn) \{\WM_COMMAND,\CN_COMMAND,\(WORD)id,\(WORD)id,\AfxSig_vv,\(AFX_PMSG)memberFxn\};
      

  5.   

    消息映射声明的解释 在清楚了有关宏的定义之后,现在来分析它们的作用和功能。消息映射声明的实质是给所在类添加几个静态成员变量和静态或虚拟函数,当然它们是与消息映射相关的变量和函数。
    成员变量 有两个成员变量被添加,第一个是 _messageEntries,第二个是messageMap。 第一个成员变量的声明: AFX_MSGMAP_ENTRY _messageEntries[]这是一个 AFX_MSGMAP_ENTRY 类型的数组变量,是一个静态成员变量,用来容纳类的消息映射条目。一个消息映射条目可以用AFX_MSGMAP_ENTRY结构来描述。 
    AFX_MSGMAP_ENTRY 结构的定义如下: 
    struct AFX_MSGMAP_ENTRY{//Windows 消息ID 
    UINT nMessage;// 控制消息的通知码 
    UINT nCode;//Windows Control 的ID 
    UINT nID;// 如果是一定范围的消息被映射,则nLastID指定其范围 
    UINT nLastID;
    UINT nSig;// 消息的动作标识 
    // 响应消息时应执行的函数(routine to call (or special value)) 
    AFX_PMSG pfn; };从上述结构可以看出,每条映射有两部分的内容:第一部分是关于消息 ID的,包括前四个域;第二部分是关于消息对应的执行函数,包括后两个域。 
    在上述结构的六个域中,pfn是一个指向CCmdTarger成员函数的指针。函数指针的类型定义如下:typedef void (AFX_MSG_CALL CCmdTarget::*AFX_PMSG)(void);当使用一条或者多条消息映射条目初始化消息映射数组时,各种不同类型的消息函数都被转换成这样的类型:不接收参数,也不返回参数的类型。因为所有可以有消息映射的类都是从 CCmdTarge派生的,所以可以实现这样的转换。 
    nSig 是一个标识变量,用来标识不同原型的消息处理函数,每一个不同原型的消息处理函数对应一个不同的nSig。在消息分发时,MFC内部根据nSig把消息派发给对应的成员函数处理,实际上,就是根据nSig的值把pfn还原成相应类型的消息处理函数并执行它。 
    第二个成员变量的声明 AFX_MSGMAP messageMap;这是一个 AFX_MSGMAP类型的静态成员变量,从其类型名称和变量名称可以猜出,它是一个包含了消息映射信息的变量。的确,它把消息映射的信息(消息映射数组)和相关函数打包在一起,也就是说,得到了一个消息处理类的该变量,就得到了它全部的消息映射数据和功能。AFX_MSGMAP结构的定义如下: 
    struct AFX_MSGMAP{// 得到基类的消息映射入口地址的数据或者函数 
    #ifdef _AFXDLL//pfnGetBaseMap 指向_GetBaseMessageMap函数 
    const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)();#else//pBaseMap 保存基类消息映射入口_messageEntries的地址 
    const AFX_MSGMAP* pBaseMap;#endif//lpEntries 保存消息映射入口_messageEntries的地址 
    const AFX_MSGMAP_ENTRY* lpEntries;};从上面的定义可以看出,通过 messageMap可以得到类的消息映射数组_messageEntries和函数_GetBaseMessageMap的地址(不使用MFC DLL时,是基类消息映射数组的地址)。 
    成员函数 
    _GetBaseMessageMap() 用来得到基类消息映射的函数。GetMessageMap() 用来得到自身消息映射的函数。
    消息映射实现的解释 消息映射实现的实质是初始化声明中定义的静态成员函数 _messageEntries和messageMap,实现所声明的静态或虚拟函数GetMessageMap、_GetBaseMessageMap。 
    这样,在进入 WinMain函数之前,每个可以响应消息的MFC类都生成了一个消息映射表,程序运行时通过查询该表判断是否需要响应某条消息。 对消息映射入口表 (消息映射数组)的初始化 如前所述,消息映射数组的元素是消息映射条目,条目的格式符合结构 AFX_MESSAGE_ENTRY的描述。所以,要初始化消息映射数组,就必须使用符合该格式的数据来填充:如果指定当前类处理某个消息,则把和该消息有关的信息(四个)和消息处理函数的地址及原型组合成为一个消息映射条目,加入到消息映射数组中。 
    显然,这是一个繁琐的工作。为了简化操作, MFC根据消息的不同和消息处理方式的不同,把消息映射划分成若干类别,每一类的消息映射至少有一个共性:消息处理函数的原型相同。对每一类消息映射,MFC定义了一个宏来简化初始化消息数组的工作。例如,前文提到的ON_COMMAND宏用来映射命令消息,只要指定命令ID和消息处理函数即可,因为对这类命令消息映射条目,其他四个属性都是固定的。ON_COMMAND宏的初始化内容如下: 
    { WM_COMMAND, 
    CN_COMMAND,(WORD)ID_APP_ABOUT,(WORD)ID_APP_ABOUT,AfxSig_vv,(AFX_PMSG)OnAppAbout}这个消息映射条目的含义是:消息 ID是ID_APP_ABOUT,OnAppAbout被转换成AFX_PMSG指针类型,AfxSig_vv是MFC预定义的枚举变量,用来标识OnAppAbout的函数类型为参数空(Void)、返回空(Void)。 
    在消息映射数组的最后,是宏 END_MESSAGE_MAP的内容,它标识消息处理类的消息映射条目的终止。 对 messageMap的初始化 如前所述, messageMap的类型是AFX_MESSMAP。 
    经过初始化,域 lpEntries保存了消息映射数组_messageEntries的地址;如果动态链接到MFC DLL,则pfnGetBaseMap保存了_GetBaseMessageMap成员函数的地址;否则pBaseMap保存了基类的消息映射数组的地址。 
      

  6.   

    若觉得jackwuwei的比较长,看看我这个简易的: 
      分三类:
      1 命令消息:一般是用户发出的一个请求或指令引起的, 以ON_COMMAND()的方式进行消息映射.lParam=0;HIGHWORD(wParam)=0;LOWWORD(wParam)=资源ID.
      2 窗口消息:一般与窗口的创建,销毁,改变大小,移动,绘制等窗口的动作相关,只能由窗口本身处理.
    常用的有 WM_CREATE,WM_DESTROY,WM_PAINT,WM_SIZE,WM_LBUTTONDOWN.wParam,lParam与具体消息有关.
      3 通知消息: 与控件窗口的某个事件的发生相关,为了向父窗口通知某种情况.
    比如: 当你在ListBox上选择一个项目时,ListBox就会产生消息LBN_SELCHANGE传给父窗口.