如果是自已添加消息响应,CreateSolidCaret创建插入

2020-01-05 19:23 来源:未知

 

第5课
1.CWnd::CreateSolidCaret创建插入符,ShowCaret()显示插入符。GetTextMetrics(),获得当前字体的一些信息。CWnd::CreateCaret()创建图象插入符
bitmap.LoadBitmap(IDB_BITMAP1);//此处的bitmap为成员变量!!!
CreateCaret(&bitmap);
ShowCaret();
TEXTMETRIC tm;//字体结构体
dc.GetTextMetrics(&tm);//
m_ptOrigin.y+=tm.tmHeight;//获得字体高度。
2.VC中CString::LoadString(ID号),比较方便。
3.路径层的概念:有两种方法创建路径层:
    (1)
pDC->BeginPath();
pDC->Rectangle(50,50,50+sz.cx,50+sz.cy);
pDC->EndPath();
pDC->SelectClipPath(RGN_DIFF);
     (2)
          CSize sz=pDC->GetTextExtent(str);
          CRgn rn;
          rn.CreateRectRgn(0,50,sz.cx,sz.cy);
          pDC->SelectClipRgn(&rn,RGN_DIFF);
路径层有什么作用?可以保护我们先前的文本或者图像不被后来画的覆盖。
4.在View上输入文字的步骤。
CFont font;//创建字体对象
font.CreatePointFont(300,"华文行楷",NULL);//设置
CFont *pOldFont=dc.SelectObject(&font);//将字体选择到DC中
TEXTMETRIC tm;//创建字体信息对象
dc.GetTextMetrics(&tm);//获得当前字体信息
if(0x0d==nChar)//处理回车键
{
    m_strLine.Empty();
    m_ptOrigin.y+=tm.tmHeight;
}
else if(0x08==nChar)//处理退格键
{
    COLORREF clr=dc.SetTextColor(dc.GetBkColor());
    dc.TextOut(m_ptOrigin.x,m_ptOrigin.y,m_strLine);
    m_strLine=m_strLine.Left(m_strLine.GetLength()-1);
    dc.SetTextColor(clr);
}
else
{
    m_strLine+=nChar;
}
CSize sz=dc.GetTextExtent(m_strLine);

 ////////////////////////////////////////////////////////////////////////////////////
                     /********* 文章系列:MFC技术内幕系列***********/
                     /************MFC技术内幕系列之(四)***********/
                     /*****文章题目:MFC消息映射与消息传递内幕******/

说到消息,在MFC中,“最熟悉的神秘”可算是消息映射,那是我们刚开始接触MFC时就要面对的东西。有过SDK编程经验的朋友转到MFC编程的时候,一下子觉得什么都变了样。特别是窗口消息及对消息的处理跟以前相比,更是风马牛不相及的。如文档不是窗口,是怎样响应命令消息的呢?

一、Pop-up

CPoint pt;//处理光标的位置
pt.x=m_ptOrigin.x+sz.cx;
pt.y=m_ptOrigin.y;

                     /*                            All rights Reserved                        */
                     /   *********关键字:消息映射,消息传递************/

初次用MFC编程,我们只会用MFC ClassWizard为我们做大量的东西,最主要的是添加消息响应。记忆中,如果是自已添加消息响应,我们应何等的小心翼翼,对BEGIN_MESSAGE_MAP()……END_MESSAGE_MAP()更要奉若神灵。它就是一个魔盒子,把我们的咒语放入恰当的地方,就会发生神奇的力量,放错了,自己的程序就连“命”都没有。

弹出菜单复选框,当这个被选中的时候就会有弹出的菜单,同时取消了ID号和prompt的提示。

SetCaretPos(pt);
dc.TextOut(m_ptOrigin.x,m_ptOrigin.y,m_strLine);//输出字体

                     /*      注释:本文所涉及的程序源代码均在Microsoft   */
                     /           Visual Studio.net EntERPrise Architect Edition       /
                     /*                   开发工具包提供的源代码中                  */
                    
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
引言:
    Windows操作系统是以消息为基础,事件驱动的。作为程序员了解操作系统的消息传递机制是非常必要的。Microsoft的MFC又它自己的一套支持Windows操作系统消息机制的技术--消息映射(Message Mapping)和命令传递(Command Routing),在这篇文章中我就详细的挖掘一下MFC的消息映射技术以及命令传递技术。

据说,知道得太多未必是好事。我也曾经打算不去理解这神秘的区域,觉得编程的时候知道自己想做什么就行了。MFC外表上给我们提供了东西,直观地说,不但给了我个一个程序的外壳,更给我们许多方便。微软的出发点可能是希望达到“傻瓜编程”的结果,试想,谁不会用ClassWizard?大家知道,Windows是基于消息的,有了ClassWizard,你又会添加类,又会添加消息,那么你所学的东西似乎学到头了。于是许多程序员认为“我们没有必要走SDK的老路,直接用MFC编程,新的东西通常是简单、直观、易学……”

(&H)为快捷键提示显示为(H)

dc.SelectObject(pOldFont);//将原先的字体选择回去。
5.模拟卡啦OK变色的步骤。
     (1)设置定时器
     (2)在定时器中加入如下代码
//DEL    m_nWidth+=5;//此为view的成员变量,初始值为0
//DEL
//DEL
//DEL    CClientDC dc(this);
//DEL    TEXTMETRIC tm;
//DEL    dc.GetTextMetrics(&tm);
//DEL    CRect rect;
//DEL    rect.left=0;
//DEL    rect.top=200;
//DEL    rect.right=m_nWidth;
//DEL    rect.bottom=rect.top+tm.tmHeight;//此长方形的长度随着定时器的触发,逐渐增大
//DEL
//DEL    dc.SetTextColor(RGB(255,0,0));
//DEL    CString str;
//DEL    str.LoadString(IDS_WEIXIN);
//DEL    dc.DrawText(str,rect,DT_LEFT);此函数的作用是将字符串输出到长方形中,但如果字符串的长度超过长方形的长度,多余的字符将被截断
//DEL
//DEL    rect.top=150;
//DEL    rect.bottom=rect.top+tm.tmHeight;
//DEL    dc.DrawText(str,rect,DT_RIGHT);
//DEL
//DEL    CSize sz=dc.GetTextExtent(str);获得字符串的长度
//DEL    if(m_nWidth>sz.cx)当长方形的长度大于字符串的长度后,将其重新归0
//DEL    {
//DEL     m_n;
//DEL     dc.SetTextColor(RGB(0,255,0));
//DEL     dc.TextOut(0,200,str);
//DEL    }
//DEL
//DEL    CView::OnTimer(nIDEvent);
6.SetTimer也可以用回调函数来操作,但并不方便。以下是步骤
    (1) 在View的OnCreate消息响应函数中:SetTimer(1,1000,Timer2Proc);
    (2) 回调函数的实现:
void CALLBACK EXPORT Timer2Proc(
     HWND hWnd,        // handle of CWnd that called SetTimer
     UINT nMsg,        // WM_TIMER
     UINT nIDEvent,     // timer identification
     DWORD dwTime      // system time
)
{
//    MessageBox((((CMainFrame *)AfxGetMainWnd())->m_hWnd),"ddfaf","weixin",0);
;
CMainFrame *pMain=(CMainFrame *)AfxGetApp()->m_pMainWnd;//获得MainFrame的指针
CTextView *pView=(CTextView *)pMain->GetActiveView();//获得view的指针
CClientDC dc(pView);//构造DC
    dc.TextOut(333,222,"hello world");

    正文:
                       ///////////////////////////////////////////////
                       /*     1.Windows消息概览      */
                       //////////////////////////////////////////////
    对于消息,程序员应该不陌生。WM_CREATE,WM_PAINT等等都是Windows程序设计中必不可缺少的组成部分。大多有关MFC Win32编程的书籍都将Windows消息分为三大类即:
    * 标准消息:   任何以WM_开头的消息(WM_COMMAND除外);如:WM_QUIT,WM_CREATE;
    * 命令消息:   WM_COMMAND;
    * 子窗口通知: 由子窗口(大多为控件)产生并发送到该控件所属的父窗口的消息。(注意:此类消息也                    以WM_COMMAND形式出现)
    消息类型我们已经了解了,下面我们就来看看消息映射是如何工作的:
                       //////////////////////////////////////////////////////
                       /*  2.MFC消息映射网的组成元素 */
                       //////////////////////////////////////////////////////   
   我的前几篇文章中涉及到了MFC内部建立的一些“网”技术,比如“执行期类型识别网”等,这回我们将建立一个消息映射网,这个网的建立与前面相同的是它也利用了一些神秘的宏。下面我们就来掀开它们的神秘面纱。
   我们先简单地看看这些宏在程序源文件中的什么地方?
   //in xx.h
   class theClass
  {
      ...//
     DECLARE_MESSAGE_MAP
   };
   //in xx.cpp
   BEGIN_MESSAGE_MAP(theClass, baseClass)
 ON_COMMAND( ID_MYCMD, OnMyCommand )
        ON_WM_CREATE
   END_MESSAGE_MAP
   ...//
  
   这些宏的定义如下:
   //in Afxwin.h
   #define DECLARE_MESSAGE_MAP /
   private: /
 static const AFX_MSGMAP_ENTRY _messageEntries; /
   protected: /
 static const AFX_MSGMAP messageMap; /
 static const AFX_MSGMAP* PASCAL GetThisMessageMap; /
 virtual const AFX_MSGMAP* GetMessageMap const; /

到你真正想用MFC编程的时候,你会发觉光会ClassWizard的你是多么的愚蠢。MFC不是一个普通的类库,普通的类库我们完全可以不理解里面的细节,只要知道这些类库能干什么,接口参数如何就万事大吉。如string类,操作顺序是定义一个string对象,然后修改属性,调用方法。

作为弹出菜单(Pop-up)不能完成命令响应的。

}//我们可以看出,使用回调函数时要获得窗口或者APP的指针,给我们的操作带来麻烦。并不方便。

   #define BEGIN_MESSAGE_MAP(theClass, baseClass) /
 const AFX_MSGMAP* PASCAL theClass::GetThisMessageMap /
  { return &theClass::messageMap; } /
 const AFX_MSGMAP* theClass::GetMessageMap const /
  { return &theClass::messageMap; } /
 AFX_COMDAT const AFX_MSGMAP theClass::messageMap = /
 { &baseClass::GetThisMessageMap, &theClass::_messageEntries[0] }; /
 AFX_COMDAT const AFX_MSGMAP_ENTRY theClass::_messageEntries = /
 { /

但对于MFC,你并不是在你的程序中写上一句“#i nclude MFC.h”,然后就在你的程序中用MFC类库。

二、ID标识:

第6课
1.当对某菜单添加消息响应函数时,4个类的消息响应优先次序分别是:1.View;2.CDOC;3.CMainFrame.4.CWinAPP.为什么?请参阅《深入浅出》
2.消息分类:a;标准消息(以WM_开头的消息,但不包括ON_COMMAND);b;命令消息 ON_COMMAND(IDM_PHONE1, OnPhone1),菜单和工具栏的消息。c.通告消息:按钮,列表框发出的消息。
CCmdTarget只能接受命令消息。而从CCmdTarget派生的CWnd可以接收命令消息,也可以接受标准消息。
3.确定菜单的索引号,注意从0开始,分隔符也算数。什么叫弹出菜单(Popup Menu)?一个子菜单只能有一个缺省菜单。 //GetMenu()->GetSubMenu(0)->SetDefaultItem(5,TRUE);
str.Format("x=%d,y=%d",GetSystemMetrics(SM_CXMENUCHECK),
     GetSystemMetrics(SM_CYMENUCHECK));//获得系统的菜单的位图的大小。
/* SetMenu(NULL);//移除菜单

   #define END_MESSAGE_MAP /
  {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } /
 }; /
   DECLARE_MESSAGE_MAP宏为每个类添加了四个东东,包括那个重要的消息映射表messageMap和消息入口结构数组AFX_MSGMAP_ENTRY _messageEntries;BEGIN_MESSAGE_MAP(theClass, baseClass)和END_MESSAGE_MAP宏则初始化了它们,随后我将带领大家看看这个初始化过程。

MFC是一块包着糖衣的牛骨头。你很轻松地写出一个单文档窗口,在窗口中间打印一句“I love MFC!”,然后,恶梦开始了……想逃避,打算永远不去理解MFC内幕?门都没有!在MFC这个黑暗神秘的洞中,即使你打算摸着石头前行,也注定找不到出口。对着MFC这块牛骨头,微软温和、民主地告诉你“你当然可以选择不啃掉它,咳咳……但你必然会因此而饿死!”

ID标识要用大写字母。MFC对资源进行标识的时候都是用大写进行标识,我们跟MFC保持相同的习惯也用最好是大写的标识。

CMenu menu;
menu.LoadMenu(IDR_MAINFRAME);
SetMenu(&menu);
menu.Detach();*/增加菜单,此处detach(),如果是局部变量。

                       ///////////////////////////////////////////////
                       /*      3.MFC消息映射表       */
                       //////////////////////////////// //////////////
   下面我们看看消息映射表messageMap和消息入口结构AFX_MSGMAP_ENTRY的定义:
 //in Afxwin.h
 struct AFX_MSGMAP_ENTRY
 {
 UINT nMessage;   // windows message
 UINT nCode;      // control code or WM_NOTIFY code
 UINT nID;        // control ID (or 0 for windows messages)
 UINT nLastID;    // used for entries specifying a range of control id's
 UINT_PTR nSig;   // signature type (action) or pointer to message #
 AFX_PMSG pfn;    // routine to call (or special value)
 };

消息映射与命令传递体现了MFC与SDK的不同。在SDK编程中,没有消息映射的概念,它有明确的回调函数中,通过一个switch语句去判断收到了何种消息,然后对这个消息进行处理。所以,在SDK编程中,会发送消息和在回调函数中处理消息就差不多可以写SDK程序了。

给ID去名字时在ID后加一个字母标识他是那一种标识。如:”IDM_”为菜单的ID

  1. void CMainFrame::OnUpdateEditCut(CCmdUI* pCmdUI)
    {
    if(2==pCmdUI->m_nIndex)
        pCmdUI->Enable();//当此菜单显示时,设为可用。
    }
    5.右键弹出菜单功能的实现方法有两个:
        a.Project->Add to Project->component and controls->文件夹VC components->Popup Menu OK
        b.用TrackPopupMenu()实现。
    CMenu menu;
    menu.LoadMenu(IDR_MENU1);
    CMenu *pPopup=menu.GetSubMenu(0);
    ClientToScreen(&point);
    pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y,
         GetParent());
    6.动态创建菜单的方法:

 struct AFX_MSGMAP
 {
 #ifdef _AFXDLL
 const AFX_MSGMAP* (PASCAL* pfnGetBaseMap);//基类的映射表指针,本程序将使用
 #else
 const AFX_MSGMAP* PBaseMap;
 #endif
 const AFX_MSGMAP_ENTRY* lpEntries;
 };
 
  其中AFX_MSGMAP结构中包含一个基类的映射表指针和一个指向消息入口结构AFX_MSGMAP_ENTRY的指针。

在MFC中,看上去发送消息和处理消息比SDK更简单、直接,但可惜不直观。举个简单的例子,如果我们想自定义一个消息,SDK是非常简单直观的,用一条语句:SendMessage(hwnd,message,wparam,lparam),之后就可以在回调函数中处理了。但MFC就不同了,因为你通常不直接去改写窗口的回调函数,所以只能亦步亦趋对照原来的MFC代码,把消息放到恰当的地方。这确实是一样很痛苦的劳动。

三、所见即所得。

CMenu menu;
menu.CreatePopupMenu();
// GetMenu()->AppendMenu(MF_POPUP,(UINT)menu.m_hMenu,"WinSun");
GetMenu()->InsertMenu(2,MF_BYPOSITION | MF_POPUP,(UINT)menu.m_hMenu,"WinSun");
menu.AppendMenu(MF_STRING,IDM_HELLO,"Hello");
menu.AppendMenu(MF_STRING,112,"Weixin");
menu.AppendMenu(MF_STRING,113,"Mybole");
menu.Detach();
GetMenu()->GetSubMenu(0)->AppendMenu(MF_STRING,114,"Welcome");
GetMenu()->GetSubMenu(0)->InsertMenu(ID_FILE_OPEN,
     MF_BYCOMMAND | MF_STRING,115,"维新");
// GetMenu()->DeleteMenu(1,MF_BYPOSITION);
// GetMenu()->GetSubMenu(0)->DeleteMenu(2,MF_BYPOSITION);
7.为动态创建的菜单增加消息响应的步骤
    a.在resource.h中增加#define IDM_HELLO 123
    b.在MainFrm.h中加入afx_msg void OnHello();
    c.MainFrm.cpp中加入ON_COMMAND(IDM_HELLO,OnHello)
    d.最后加入
void CMainFrame::OnHello()
{
MessageBox("Hello!");
}
8.动态增加电话号码本步骤
    a.处理WM_Char消息。如果回车,则清空字符串,窗口重绘invalidate,将人名加入到菜单中,将字符串保存集合类CStringArray中,用的是成员函数Add方法。
    b.取出动态创建的菜单的数据的方法。
      1)创建一个弹出菜单,弹出菜单下面有4个子菜单。将子菜单的ID号连续。
      2)在resource.h中添加#define IDM_PHONE1 123....
      3)添加其消息响应函数。注意注释中的文字
BEGIN_MESSAGE_MAP(CMenu2View, CView)
//{{AFX_MSG_MAP(CMenu2View)
ON_WM_CHAR()
ON_COMMAND(ID_EDIT_COPY, OnEditCopy)//下面的4句代码原来在此处。
//}}AFX_MSG_MAP
// Standard printing commands
ON_COMMAND(IDM_PHONE1, OnPhone1)//一定要这4句代码移到此处。
ON_COMMAND(IDM_PHONE2, OnPhone2)
ON_COMMAND(IDM_PHONE3, OnPhone3)
ON_COMMAND(IDM_PHONE4, OnPhone4)
ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
END_MESSAGE_MAP()
      4)填写代码
9.如何在MainFrame中拦截OnCommand消息?答,在它增加OnCommand的消息处理函数即可。
10.错误调试方法:Missing ";" before "*"
CMenu2Doc* GetDocument();//因为CMenu2Doc是个不认识的变量,将其头文件包含进即可。

                       /////////////////////////////////////////////////
                       /*    4.MFC消息映射宏展开     */
                       /////////////////////////////////////////////////

要了解MFC消息映射原理并不是一件轻松的事情。我们可以逆向思维,想象一下消息映射为我们做了什么工作。MFC在自动化给我们提供了很大的方便,比如,所有的MFC窗口都使用同一窗口过程,即所有的MFC窗口都有一个默认的窗口过程。不象在SDK编程中,要为每个窗口类写一个窗口过程。

四、菜单响应顺序、消息的分类:

第7课
1.如果在SDI中要调用对话框
    a.先插入一个对话框资源;
    b.然后在ClassWizards中为其创建一个类。其目的是比较方便为添加按纽和消息响应函数。
    c.然后实例化它。在实例化时,必须将其头文件包含进去。
2.创建非模态对话框,注意它不能是局部变量。当 点击非模态对话框的OnOK按纽时,它并没有关闭,而是隐藏了。需要调用destroyWindow().
3.一个对象只能一个按纽。为什么?因为在Wincore.cpp的628行有代码 ASSERT(pWnd->m_hWnd == NULL);     // only do once而创建后它的m_hWnd就不为0了。此处ASSERT的用法是如果括号里面不为真,则程序崩溃。
4.如何为静态文本框增加消息响应?首先将IDC_STATIC改名。同时还需要将Notify特性复选中。
5.完成加法功能。
    a.GetDlgItem();
    b.GetDlgItemText();
    c.GetDlgItemInt();
    d.将IDC_EDIT1关联CEDIT类型变量
    e.将IDC_EDIT1关联int型变量。注意调用 UpdateData();
    f. //::SendMessage(GetDlgItem(IDC_EDIT1)->m_hWnd,WM_GETTEXT,10,(LPARAM)ch1);
//::SendMessage(m_edit1.m_hWnd,WM_GETTEXT,10,(LPARAM)ch1);
//GetDlgItem(IDC_EDIT1)->SendMessage(WM_GETTEXT,10,(LPARAM)ch1);
m_edit1.SendMessage(WM_GETTEXT,10,(LPARAM)ch1);
    g. SendDlgItemMessage(IDC_EDIT1,WM_GETTEXT,10,(LPARAM)ch1);
SendDlgItemMessage(IDC_EDIT2,WM_GETTEXT,10,(LPARAM)ch2);
6.点击按纽改变窗口尺寸
     if(GetDlgItemText(IDC_BUTTON2,str),str=="收缩<<")
{
    SetDlgItemText(IDC_BUTTON2,"扩展>>");
static CRect rectLarge;
static CRect rectSmall;

  上面的宏展开后代码如下:(以CMaimFrame为例)
   //in MaimFrm.h
   class CMaimFrame : public CFrameWnd
  {
    ...//
    private:
        static const AFX_MSGMAP_ENTRY _messageEntries;
    protected:
 static const AFX_MSGMAP messageMap;
 static const AFX_MSGMAP* PASCAL GetThisMessageMap;
 virtual const AFX_MSGMAP* GetMessageMap const;
  };
  //in MaimFrm.cpp
    const AFX_MSGMAP* PASCAL CMaimFrame::GetThisMessageMap
  { return &CMaimFrame::messageMap; }
    const AFX_MSGMAP* CMaimFrame::GetMessageMap const
  { return &CMaimFrame::messageMap; }
    AFX_COMDAT const AFX_MSGMAP theClass::messageMap =
 { &CFrameWnd::GetThisMessageMap, &CMaimFrame::_messageEntries[0] };

对于消息映射,最直截了当地猜想是:消息映射就是用一个数据结构把“消息”与“响应消息函数名”串联起来。这样,当窗口感知消息发生时,就对结构查找,找到相应的消息响应函数执行。其实这个想法也不能简单地实现:我们每个不同的MFC窗口类,对同一种消息,有不同的响应方式。即是说,对同一种消息,不同的MFC窗口会有不同的消息响应函数。

1、我们通过”View”的”ClassWizard”来给自定义的资源进行响应的编辑。

if(rectLarge.IsRectNull())
{
    CRect rectSeparator;
    GetWindowRect(&rectLarge);
    GetDlgItem(IDC_SEPARATOR)->GetWindowRect(&rectSeparator);

    AFX_COMDAT const AFX_MSGMAP_ENTRY CMaimFrame::_messageEntries

        {
          {         ...//                      }
                    ...
          {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 }
 };
   相信大家看了后大多源代码都能够理解,但是AFX_MSGMAP_ENTRY结构还是能够引起我们的兴趣的。下面让我们看看_messageEntries是如何被初始化的:
   我们还是举例来说明吧!(还是CMainFrame为例吧)
   BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
 ON_WM_CREATE
        ON_COMMAND( ID_MYCMD, OnMyCommand )
   END_MESSAGE_MAP
  
   大家看到了夹在BEGIN_MESSAGE_MAP和END_MESSAGE_MAP之间的宏,这些宏可分为基类,一类是Windows预定义消息宏(比如:ON_WM_CREATE,ON_WM_DESTROY等定义在afxmsg_.h中的Message map tables for Windows messages),一类是自定义的ON_COMMAND宏以及类似的如ON_UPDATE_COMMAND_UI等宏 。
   //in afxmsg_.h
   // Message map tables for Windows messages
   #define ON_WM_CREATE /
 { WM_CREATE, 0, 0, 0, AfxSig_is, /
  (AFX_PMSG) (AFX_PMSGW) /
  (static_cast< int (AFX_MSG_CALL CWnd::*)(LPCREATESTRUCT) > (OnCreate)) },
 
   #define ON_COMMAND(id, memberFxn) /
 { WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSigCmd_v, /
  static_cast (memberFxn) },

   AFX_MSGMAP_ENTRY结构初始化过程:
   AFX_COMDAT const AFX_MSGMAP_ENTRY CMaimFrame::_messageEntries =
        {
          { WM_CREATE, 0, 0, 0, AfxSig_is,
  (AFX_PMSG) (AFX_PMSGW)
              (static_cast< int (AFX_MSG_CALL CWnd::*)(LPCREATESTRUCT) > (OnCreate)) },
          { WM_COMMAND, CN_COMMAND, (WORD)ID_MYCMD, (WORD)ID_MYCMD, AfxSigCmd_v, /
  static_cast ( OnMyCommand) },       
          {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 }
 };
   现在一切都清楚了吧!

                       //////////////////////////////////////////////////
                       /*    5.MFC消息映射网的连接   */
                       //////////////////////////////////////////////////
   MFC消息映射网的连接也是在初始化过程中完成的,其建立过程很简单。主要有关成员有:
   private:
        static const AFX_MSGMAP_ENTRY _messageEntries;
   protected:
 static const AFX_MSGMAP messageMap;
   和BEGIN_MESSAGE_MAP宏开后的
   AFX_COMDAT const AFX_MSGMAP theClass::messageMap =
 { &baseClass::GetThisMessageMap, &theClass::_messageEntries[0] };
   该宏将pfnGetBaseMap赋值为其基类的messageMap地址;将AFX_MSGMAP_ENTRY* lpEntries赋值为该类的
_messageEntries[0];
   这样一个类不仅拥有本类的messageMap,而且还拥有其基类的messageMap,依此类推MFC消息映射网的连接
就建立了,最终的基类都是CCmdTarget

                       //////////////////////////////////////////////////
                       /*    6.MFC命令传递机制概述   */
                       //////////////////////////////////////////////////
   有了MFC消息映射网就为命令传递打下了坚实的基础。Win32API程序员都熟悉传统的API编程都有一个
WndProc回调函数来集中处理各种的Windows消息,然而在MFC中我们却怎么也看不见WndProc回调函数的踪影了。而MFC命令传递机制恰是为了将各种消息“拐弯抹角”地送到各个对应的"WndProc"函数的一种技术。MFC是如何将各种Windows消息准确的送到期该区的地方呢? MFC使用了钩子函数等技术来保证其准确性和全面性。
   不知大家是否还记得MFC在注册窗口类时作了什么?
   BOOL AFXAPI AfxEndDeferReGISterClass(LONG fToRegister)//部分源代码
  {
        ...//
        // common initialization
 WNDCLASS wndcls;
 memset(&wndcls, 0, sizeof(WNDCLASS));   // start with NULL defaults
 wndcls.lpfnWndProc = DefWindowProc;
 ...//
   }
  可以看到MFC注册时将wndcls.lpfnWndProc赋值为DefWindowProc函数,那么实际上是否消息都是由它处理的呢?显然不可能,MFC又不知道我们要处理什么消息。那么还有什么函数能帮我们处理消息呢?这就是我要讲的;  在MFC技术内幕系列之(二)----《 MFC文档视图结构内幕》中曾提到“CWnd::CreateEx函数调用了AfxHookWindowCreate(this);后者是干什么的呢?其实它与消息映射和命令传递有关。”
   实际上MFC命令传递机制就是从这里AfxHookWindowCreate(this)开始的。还是老办法看看代码吧:
 //in wincore.cpp
 void AFXAPI AfxHookWindowCreate(CWnd* pWnd)
 {
 ...//
 if (pThreadState->m_hHookOldCbtFilter == NULL)
 {
  pThreadState->m_hHookOldCbtFilter = ::SetWindowsHookEx(WH_CBT,
   _AfxCbtFilterHook, NULL, ::GetCurrentThreadId);
  if (pThreadState->m_hHookOldCbtFilter == NULL)
   AfxThrowMemoryException;
 }
 ...//
 }
  该函数设置了消息钩子,其钩子处理函数为_AfxCbtFilterHook;这里简介一下钩子函数:
  用我的理解,钩子就是能给你一个在某个消息到达其默认的处理函数之前处理该消息机会的工具。
与钩子有关的函数主要有三个:
HHOOK SetWindowsHookEx(int idHook, HOOKPROC lpfn, HINSTANCE hMod, DWORD dwThreadId );
LRESULT CallNextHookEx(HHOOK hhk,  int nCode,    WPARAM wParam,   LPARAM lParam  );
BOOL UnhookWindowsHookEx( HHOOK hhk   // handle to hook procedure);
关于这三个函数我也不想多解释,大家看看MFC有关文档吧!这里主要讲的是在AfxHookWindowCreate(CWnd* pWnd)中注册的钩子函数的类型(hook type)--WH_CBT;
有关WH_CBT,MFC文档时如是说的:
  Installs a hook procedure that receives notifications useful to a computer-based training (CBT) application. The system calls this function(这里指的是_AfxCbtFilterHook) before activating, creating, destroying, minimizing, maximizing, moving, or sizing a window; before completing a system command; before removing a mouse or keyboard event from the system message queue; before setting the keyboard focus; or before synchronizing with the system message queue. A computer-based training (CBT) application uses this hook procedure to receive useful notifications from the system.
 
                       /////////////////////////////////////////////
                       /*    7.偷换“窗口函数”      */
                       /////////////////////////////////////////////

   这会知道了吧,当发生窗口(包括子窗口)发生被激活,创建,撤销,最小化等时候,应用程序将调用
_AfxCbtFilterHook函数;下面就让我们看看_AfxCbtFilterHook函数做了个啥:
 //in wincore.cpp
// Window creation hooks
 LRESULT CALLBACK _AfxCbtFilterHook(int code, WPARAM wParam, LPARAM lParam)
 {
 _AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData;
 if (code != HCBT_CREATEWND)
 {
  // wait for HCBT_CREATEWND just pass others on...
  return CallNextHookEx(pThreadState->m_hHookOldCbtFilter, code,
   wParam, lParam);
 }

 ...//   CWnd* pWndInit = pThreadState->m_pWndInit;
  HWND hWnd = (HWND)wParam;
  WNDPROC oldWndProc;
  if (pWndInit != NULL)
  {
                 #ifdef _AFXDLL
   AFX_MANAGE_STATE(pWndInit->m_pModuleState);
                 #endif

   // the window should not be in the permanent map at this time
   ASSERT(CWnd::FromHandlePermanent(hWnd) == NULL);

   // connect the HWND to pWndInit...
   pWndInit->Attach(hWnd);
   // allow other subclassing to occur first
   pWndInit->PreSubclassWindow;//***

   WNDPROC *pOldWndProc = pWndInit->GetSuperWndProcAddr;
   ASSERT(pOldWndProc != NULL);

   // subclass the window with standard AfxWndProc
   WNDPROC afxWndProc = AfxGetAfxWndProc;//***
   oldWndProc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC,
    (DWORD_PTR)afxWndProc);//***
   ASSERT(oldWndProc != NULL);
   if (oldWndProc != afxWndProc)
    *pOldWndProc = oldWndProc;

   pThreadState->m_pWndInit = NULL;
  }
 ...//
 lCallNextHook:
 LRESULT lResult = CallNextHookEx(pThreadState->m_hHookOldCbtFilter, code,
  wParam, lParam);

 #ifndef _AFXDLL
 if (bContextIsDLL)
 {
  ::UnhookWindowsHookEx(pThreadState->m_hHookOldCbtFilter);
  pThreadState->m_hHookOldCbtFilter = NULL;
 }
 #endif
 return lResult;
 }

 void CWnd::PreSubclassWindow
{
 // no default processing
 }
 
 // always indirectly Accessed via AfxGetAfxWndProc
 WNDPROC AFXAPI AfxGetAfxWndProc
 {
 #ifdef _AFXDLL
 return AfxGetModuleState->m_pfnAfxWndProc;
 #else
 return &AfxWndProc;
 #endif
 }

  原来_AfxCbtFilterHook函数偷换了窗口函数,将原来的DefWndProc换成AfxWndProc函数.
                       ////////////////////////////////////////////////////
                       /*   8.MFC的“WndProc”函数   */
                       ////////////////////////////////////////////////////
   AfxWndProc函数就可以说是MFC的“WndProc”函数,它也是MFC中消息传递的开始,其代码如下:
//in wincore.cpp
// The WndProc for all CWnd's and derived classes
LRESULT CALLBACK AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
 // special message which identifies the window as using AfxWndProc
 if (nMsg == WM_QUERYAFXWNDPROC)
  return 1;

 // all other messages route through message map
 CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);
 ASSERT(pWnd != NULL);
 ASSERT(pWnd->m_hWnd == hWnd);
 return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam);
}

// Official way to send message to a CWnd
LRESULT AFXAPI AfxCallWndProc(CWnd* pWnd, HWND hWnd, UINT nMsg,
 WPARAM wParam = 0, LPARAM lParam = 0)
{
 _AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData;
 MSG oldState = pThreadState->m_lastSentMsg;   // save for nesting
 pThreadState->m_lastSentMsg.hwnd = hWnd;
 pThreadState->m_lastSentMsg.message = nMsg;
 pThreadState->m_lastSentMsg.wParam = wParam;
 pThreadState->m_lastSentMsg.lParam = lParam;
         ...//
       // in debug builds and warn the user.
 LRESULT lResult;
 TRY
 {
#ifndef _AFX_NO_OCC_SUPPORT
  // special case for WM_DESTROY
  if ((nMsg == WM_DESTROY) && (pWnd->m_pCtrlCont != NULL))
   pWnd->m_pCtrlCont->OnUIActivate(NULL);
#endif
               // special case for WM_INITDIALOG
  CRect rectOld;
  DWORD dwStyle = 0;
  if (nMsg == WM_INITDIALOG)
   _AfxPreInitDialog(pWnd, &rectOld, &dwStyle);

  // delegate to object's WindowProc
  lResult = pWnd->WindowProc(nMsg, wParam, lParam);//***

  // more special case for WM_INITDIALOG
  if (nMsg == WM_INITDIALOG)
   _AfxPostInitDialog(pWnd, rectOld, dwStyle);
 }
 CATCH_ALL(e)
 {
  ...//
 }
 END_CATCH_ALL

 pThreadState->m_lastSentMsg = oldState;
 return lResult;
}

// main WindowProc implementation
LRESULT CWnd::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
 // OnWndMsg does most of the work, except for DefWindowProc call
 LRESULT lResult = 0;
 if (!OnWndMsg(message, wParam, lParam, &lResult))
  lResult = DefWindowProc(message, wParam, lParam);
 return lResult;
}

    BOOL CWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
 LRESULT lResult = 0;

 mmf.pfn = 0;
        // special case for commands
 if (message == WM_COMMAND)
 {
  if (OnCommand(wParam, lParam))
  {
   lResult = 1;
   goto LReturnTrue;
  }
  return FALSE;
 }
        // special case for notifies
 if (message == WM_NOTIFY)
 {
  NMHDR* pNMHDR = (NMHDR*)lParam;
  if (pNMHDR->hwndFrom != NULL && OnNotify(wParam, lParam, &lResult))
   goto LReturnTrue;
  return FALSE;
 }
        ...//
        const AFX_MSGMAP* pMessageMap; pMessageMap = GetMessageMap;
 UINT iHash; iHash = (LOWORD((DWORD_PTR)pMessageMap) ^ message) & (iHashMax-1);
 AfxLockGlobals(CRIT_WINMSGCACHE);
 AFX_MSG_CACHE* pMsgCache; pMsgCache = &_afxMsgCache[iHash];
 const AFX_MSGMAP_ENTRY* lpEntry;
 if (message == pMsgCache->nMsg && pMessageMap == pMsgCache->pMessageMap)
 {
  // cache hit
  lpEntry = pMsgCache->lpEntry;
  AfxUnlockGlobals(CRIT_WINMSGCACHE);
  if (lpEntry == NULL)
   return FALSE;

  // cache hit, and it needs to be handled
  if (message < 0xC000)
   goto LDispatch;
  else
   goto LDispatchRegistered;
 }
 else
 {
  // not in cache, look for it
  pMsgCache->nMsg = message;
  pMsgCache->pMessageMap = pMessageMap;

  #ifdef _AFXDLL
  for (/* pMessageMap already init'ed */; pMessageMap->pfnGetBaseMap != NULL;
   pMessageMap = (*pMessageMap->pfnGetBaseMap))
  #else
  for (/* pMessageMap already init'ed */; pMessageMap != NULL;
   pMessageMap = pMessageMap->pBaseMap)
  #endif
  {
   // Note: catch not so common but fatal mistake!!
   //      BEGIN_MESSAGE_MAP(CMyWnd, CMyWnd)
  #ifdef _AFXDLL
   ASSERT(pMessageMap != (*pMessageMap->pfnGetBaseMap));
  #else
   ASSERT(pMessageMap != pMessageMap->pBaseMap);
  #endif

   if (message < 0xC000)
   {
    // constant window message
    if ((lpEntry = AfxFindMessageEntry(pMessageMap->lpEntries,
     message, 0, 0)) != NULL)
    {
     pMsgCache->lpEntry = lpEntry;
     AfxUnlockGlobals(CRIT_WINMSGCACHE);
     goto LDispatch;
    }
   }
   else
   {
     // registered windows message
           lpEntry = pMessageMap->lpEntries;
   while ((lpEntry = AfxFindMessageEntry(lpEntry, 0xC000, 0, 0)) != NULL)
    {
     UINT* pnID = (UINT*)(lpEntry->nSig);
     ASSERT(*pnID >= 0xC000 || *pnID == 0);
      // must be successfully registered
     if (*pnID == message)
     {
      pMsgCache->lpEntry = lpEntry;
      AfxUnlockGlobals(CRIT_WINMSGCACHE);
      goto LDispatchRegistered;
     }
     lpEntry++;      // keep looking past this one
    }
   }
  }

  pMsgCache->lpEntry = NULL;
  AfxUnlockGlobals(CRIT_WINMSGCACHE);
  return FALSE;
 }
   LDispatch:
 ASSERT(message < 0xC000);

 mmf.pfn = lpEntry->pfn;

 switch (lpEntry->nSig)
 {
 default:
  ASSERT(FALSE);
  break;

 case AfxSig_b_D_v:
  lResult = (this->*mmf.pfn_b_D)(CDC::FromHandle(reinterpret_cast(wParam)));
  break;
        ...//
  LDispatchRegistered:    // for registered windows messages
 ASSERT(message >= 0xC000);
 ASSERT(sizeof(mmf) == sizeof(mmf.pfn));
永利平台娱乐, mmf.pfn = lpEntry->pfn;
 lResult = (this->*mmf.pfn_l_w_l)(wParam, lParam);

  LReturnTrue:
 if (pResult != NULL)
  *pResult = lResult;
 return TRUE;
 }
   该源代码有整整700行,不知道是不是MFC源码中最长的一个函数,在这里我只列出部分有代表性的源码。从源代码中大家也可以看得出该函数主要用于分辨消息并将消息交于其处理函数。MFC为了加快消息得分检速度在AfxFindMessageEntry函数中甚至使用了汇编代码。
在CWnd::OnWndMsg中得不到处理的消息则交给CWnd::DefWindowProc(相当于MFC的默认DefWindowProc函数)处理,其代码为:
 // Default CWnd implementation
 LRESULT CWnd::DefWindowProc(UINT nMsg, WPARAM wParam, LPARAM lParam)
 {
 if (m_pfnSuper != NULL)
  return ::CallWindowProc(m_pfnSuper, m_hWnd, nMsg, wParam, lParam);

 WNDPROC pfnWndProc;
 if ((pfnWndProc = *GetSuperWndProcAddr) == NULL)
  return ::DefWindowProc(m_hWnd, nMsg, wParam, lParam);
 else
  return ::CallWindowProc(pfnWndProc, m_hWnd, nMsg, wParam, lParam);
 }
  CWnd::DefWindowProc这调用了传统win32程序员熟悉的::DefWindowProc(m_hWnd, nMsg, wParam, lParam);
在挖掘上面源代码的同时你也看到了消息的传递路线。在MFC中CWnd以及派生于CWnd的类都拥有虚函数
CWnd::WndProc(...)。
 
                       /////////////////////////////////////////////////////
                       /* 9.MFC各类消息的"行走路径 " */
                       /////////////////////////////////////////////////////
   在篇头我就将消息分了类而且到目前我们已经了解了消息的传递机制了,下面我就具体的某类消息来看看其传递路径。
   无论什么消息都有AfxWndProc进入,到达CWnd::OnWndMsg函数分检消息;
   对于标准消息:
       标准消息一般都沿其消息映射表从本类到父类逐层查找其处理函数,若没查到着交给::DefWindowProc        处理。
   对于命令消息:
       命令消息除了能像标准消息一样从本类到父类逐层查找其处理函数外,有时他们可能还要拐弯。
       再回头看看CWnd::OnWndMsg源码是如何处理WM_COMMAND消息的:
        if (message == WM_COMMAND)
 {
  if (OnCommand(wParam, lParam))
  {
   lResult = 1;
   goto LReturnTrue;
  }
  return FALSE;
 }
        原来交给了CWnd::OnCommand(wParam, lParam)
        下面看看一个SDI程序中命令消息在Frame,View,Document以及CWinApp对象之间的传递路线。
        //in winfrm.cpp
        BOOL CFrameWnd::OnCommand(WPARAM wParam, LPARAM lParam)
 // return TRUE if command invocation was attempted
       {
 HWND hWndCtrl = (HWND)lParam;
 UINT nID = LOWORD(wParam);

 CFrameWnd* pFrameWnd = GetTopLevelFrame;
 ASSERT_VALID(pFrameWnd);
 if (pFrameWnd->m_bHelpMode && hWndCtrl == NULL &&
  nID != ID_HELP && nID != ID_DEFAULT_HELP && nID != ID_CONTEXT_HELP)
 {
  // route as help
  if (!SendMessage(WM_COMMANDHELP, 0, HID_BASE_COMMAND+nID))
   SendMessage(WM_COMMAND, ID_DEFAULT_HELP);
  return TRUE;
 }

 // route as normal command
 return CWnd::OnCommand(wParam, lParam);
        }
       
        //in wincore.cpp
        // CWnd command handling
        BOOL CWnd::OnCommand(WPARAM wParam, LPARAM lParam)
 // return TRUE if command invocation was attempted
       { ...//
         return OnCmdMsg(nID, nCode, NULL, NULL);//CFrameWnd::OnCmdMsg
       }
      
      // CFrameWnd command/message routing
       BOOL CFrameWnd::OnCmdMsg(UINT nID, int nCode, void* pExtra,
 AFX_CMDHANDLERINFO* pHandlerInfo)
      {
 CPUshRoutingFrame push(this);

 // pump through current view FIRST
 CView* pView = GetActiveView;
 if (pView != NULL && pView->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
  return TRUE;

 // then pump through frame
 if (CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
  return TRUE;

 // last but not least, pump through app
 CWinApp* pApp = AfxGetApp;
 if (pApp != NULL && pApp->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
  return TRUE;

 return FALSE;
       }
       Frame的COMMAND传递顺序是View--->Frame本身-->CWinApp对象。
       
       //in viewcore.cpp
       BOOL CView::OnCmdMsg(UINT nID, int nCode, void* pExtra,
 AFX_CMDHANDLERINFO* pHandlerInfo)
      {
 // first pump through pane
 if (CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
  return TRUE;

 // then pump through document
 if (m_pDocument != NULL)
 {
  // special state for saving view before routing to document
  CPushRoutingView push(this);
  return m_pDocument->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
 }

 return FALSE;
      }
      View的COMMAND传递顺序是View本身--->Document

     //in doccore.cpp
     BOOL CDocument::OnCmdMsg(UINT nID, int nCode, void* pExtra,
 AFX_CMDHANDLERINFO* pHandlerInfo)
     {
 if (CCmdTarget::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
  return TRUE;

 // otherwise check template
 if (m_pDocTemplate != NULL &&
   m_pDocTemplate->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
  return TRUE;

 return FALSE;
     }
     Document的COMMAND传递顺序是Document本身--->Document Template
     由这个例子我们可以清楚地看到WM_COMMAND的传递路径了。
 
  对于子窗口通知:由于子窗口通知通常以WM_COMMAND形式出现,所以它的传递路径也大致与WM_COMMAND相同,这里就不详述了。  

                       ///////////////////////////////////////////
                       /*       10.收尾工作          */
                       /////////////////////////////////////////
  至此,MFC消息映射与消息传递的内幕已基本被揭开,若想更深刻的理解,你就得在平时的程序开发中夺观察多思考了。

                       /////////////////////////////////////////
                       /*       11.下期预告          */
                       /////////////////////////////////////////

    MFC技术内幕系列之(五)-------《MFC文档序列化内幕》

这时,大家又想了一个可行的方法。我们设计窗口基类(CWnd)时,我们让它对每种不同的消息都来一个消息响应,并把这个消息响应函数定义为空虚函数。这样,从CWnd派生的窗口类对所有消息都有了一个空响应,我们要响应一个特定的消息就重载这个消息响应函数就可以了。但这样做的结果,一个几乎什么也不做的CWnd类要有几百个“多余”的函数,那怕这些消息响应函数都为纯虚函数,每个CWnd对象也要背负着一个巨大的虚拟表,这也是得不偿失的。

在frameviewdocapp四个类中Ontest()的响应顺序:

    rectSmall.left=rectLarge.left;
    rectSmall.top=rectLarge.top;
    rectSmall.right=rectLarge.right;
    rectSmall.bottom=rectSeparator.bottom;
}
if(str=="收缩<<")
{
    SetWindowPos(NULL,0,0,rectSmall.Width(),rectSmall.Height(),
     SWP_NOMOVE | SWP_NOZORDER);
}
else
{
    SetWindowPos(NULL,0,0,rectLarge.Width(),rectLarge.Height(),
     SWP_NOMOVE | SWP_NOZORDER);
}
7.回车时将输入焦点移动到下一个控件
SetWindowLong()改变窗口的属性。
方法1:
改变控件的回调函数,注意IDC_EDIT1的MultiLine要复选上。
WNDPROC prevProc;
LRESULT CALLBACK WinSunProc(
    HWND hwnd,        // handle to window
    UINT uMsg,        // message identifier
    WPARAM wParam,    // first message parameter
    LPARAM lParam     // second message parameter
)
{
if(uMsg==WM_CHAR && wParam==0x0d)
{
    //::SetFocus(::GetNextWindow(hwnd,GW_HWNDNEXT));
    //SetFocus(::GetWindow(hwnd,GW_HWNDNEXT));
    CString str;
    str.Format("%d",hwnd);
    AfxMessageBox(str);//, UINT nType = MB_OK, UINT nIDHelp = 0 );
//    AfxGetApp()->
    SetFocus(::GetNextDlgTabItem(::GetParent(hwnd),hwnd,FALSE));
    return 1;
}
else
{
    return prevProc(hwnd,uMsg,wParam,lParam);
}
}
BOOL CTestDlg::OnInitDialog()
{
CDialog::OnInitDialog();

许多朋友在学习消息映射时苦无突破,其原因是一开始就认为MFC的消息映射的目的是为了替代SDK窗口过程的编写——这本来没有理解错。但他们还有多一层的理解,认为既然是替代“旧”的东西,那么MFC消息映身应该是更高层次的抽象、更简单、更容易认识。但结果是,如果我们不通过ClassWizard工具,手动添加消息是相当迷茫的一件事。

View(视图程序类)-Doc(文档程序类)-Frame(应用程序类)-App(应用程序类)

// TOD Add extra initialization here
prevProc=(WNDPROC)SetWindowLong(GetDlgItem(IDC_EDIT1)->m_hWnd,GWL_WNDPROC,
    (LONG)WinSunProc);//设置回调函数
return TRUE;    // return TRUE unless you set the focus to a control
                 // EXCEPTION: OCX Property Pages should return FALSE
}
方法2:
在OnOK响应函数中加入代码
//GetDlgItem(IDC_EDIT1)->GetNextWindow()->SetFocus();
//GetFocus()->GetNextWindow()->SetFocus();
//GetFocus()->GetWindow(GW_HWNDNEXT)->SetFocus();
GetNextDlgTabItem(GetFocus())->SetFocus();

所以,我们在学习MFC消息映射时,首先要弄清楚:消息映射的目的,不是为是更加快捷地向窗口过程添加代码,而是一种机制的改变。如果不想改变窗口过程函数,那么应该在哪里进行消息响应呢?许多朋友一知半解地认为:我们可以用HOOK技术,抢在消息队列前把消息抓取,把消息响应提到窗口过程的外面。再者,不同的窗口,会有不同的感兴趣的消息,所以每个MFC窗口都应该有一个表把感兴趣的消息和相应消息响应函数连系起来。然后得出——消息映射机制执行步骤是:当消息发生,我们用HOOK技术把本发送到窗口过程的消息抓获,然后对照一下MFC窗口的消息映射表,如果是表里面有的消息,就执行其对应的函数。

2、消息分类:

当然,用HOOK技术,我们理论上可以在不改变窗口过程函数的情况下,可以完成消息响应。MFC确实是这样做,但实际操作起来可能跟你的想象差别很大。

n        标准消息

现在我们来编写消息映射表,我们先定义一个结构,这个结构至少有两个项:一是消息ID,二是响应该消息的函数。如下:

      除WM_COMMAND之外,所有以WM_开头的消息。

struct AFX_MSGMAP_ENTRY

      从CWnd派生的类,都可以接收到这类消息。如:WM_CHAR,WM_CREATE,WM_PAIN

{

       CCmdTarget为CWnd的父类。凡是从CWnd派生出来的类即可以接收标准消息也可以接收命令消息。

UINT nMessage; //感兴趣的消息

n        命令消息

AFX_PMSG pfn; //响应以上消息的函数指针

      来自菜单、加速键或工具栏按钮的消息。这类消息都以WM_COMMAND呈现。在MFC中,通过菜单项的标识(ID)来区分不同的命令消息;在SDK中,通过消息的wParam参数识别。

}

       从CCmdTarget派生的类,都可以接收到这类消息。 

当然,只有两个成员的结构连接起来的消息映射表是不成熟的。Windows消息分为标准消息、控件消息和命令消息,每类型的消息包含数百不同ID、不同意义、不同参数的消息。我们要准确地判别发生了何种消息,必须再增加几个成员。还有,对于AFX_PMSG pfn,实际上等于作以下声明:

       CDocument和CWinApp都是从CCndTarget中派生出来因此不能接受标准消息,只能接收命令消息。

void (CCmdTarget::*pfn)();

n        通告消息

(提示:AFX_PMSG为类型标识,具体声明是:typedef void (AFX_MSG_CALL CCmdTarget::*AFX_PMSG)(void);)

      由控件产生的消息,例如,按钮的单击,列表框的选择等均产生此类消息,为的是向其父窗口(通常是对话框)通知事件的发生。这类消息也是以WM_COMMAND形式呈现。

pfn是不一不带参数和返回值的CCmdTarget类型函数指针,只能指向CCmdTarget类中不带参数和返回值的成员函数,这样pfn更为通用,但我们响应消息的函数许多需要传入参数的。为了解决这个矛盾,我们还要增加一个表示参数类型的成员。当然,还有其它……

       从CCmdTarget派生的类,都可以接收到这类消息。

最后,MFC我们消息映射表成员结构如下定义:

3、接收一个命令消息时:

struct AFX_MSGMAP_ENTRY

1

{

2

UINT nMessage; //Windows 消息ID

3

UINT nCode; // 控制消息的通知码

4

UINT nID; //命令消息ID范围的起始值

5

UINT nLastID; //命令消息ID范围的终点

6

UINT nSig; // 消息的动作标识

protected:

AFX_PMSG pfn;

       //{{AFX_MSG(CMenu3View)

};

       afx_msg void OnPhno();

有了以上消息映射表成员结构,我们就可以定义一个AFX_MSGMAP_ENTRY类型的数组,用来容纳消息映射项。定义如下:

       afx_msg void OnTest();

AFX_MSGMAP_ENTRY _messageEntries[];

       //}}AFX_MSG

但这样还不够,每个AFX_MSGMAP_ENTRY数组,只能保存着当前类感兴趣的消息,而这仅仅是我们想处理的消息中的一部分。对于一个MFC程序,一般有多个窗口类,里面都应该有一个AFX_MSGMAP_ENTRY数组。我们知道,MFC还有一个消息传递机制,可以把自己不处理的消息传送给别的类进行处理。为了能查找各下MFC对象的消息映射表,我们还要增加一个结构,把所有的AFX_MSGMAP_ENTRY数组串联起来。

       DECLARE_MESSAGE_MAP()

于是,我们定义了一个新结构体:

在消息映射中添加命令消息函数原型如3、4行代码。

struct AFX_MSGMAP

TAG标签:
版权声明:本文由永利平台娱乐发布于关于我们,转载请注明出处:如果是自已添加消息响应,CreateSolidCaret创建插入