当了一份代码 忙活了近一天 新手伤不起啊 ON_COMMAND_RANGE

  程序主要是实现了一个计算器功能 代码部分上传到资源处了

首先要说的不是逻辑上多困难 因为是新手刚开始接触MFC编程 调试了一天的程序费好多劲 把收获总结一下吧

 1:函数PreTranslateMessage(MSG* pMsg)

 

 MSDN是这样解释的:

 

CWnd::PreTranslateMessage

virtual BOOL PreTranslateMessage( MSG* pMsg )

Return Value

Nonzero if the message was translated and should not be dispatched; 0 if the message was not translated and should be dispatched.

Parameters

pMsg

Points to a MSG structure that contains the message to process.

 查找了部分资料:

PreTranslateMessage是消息在送给TranslateMessage函数之前被调用的,绝大多数本窗口的消息都要通过这里,比较常用,当你需要在MFC之前处理某些消息时,常常要在这里添加代码. MFC消息控制流最具特色的地方是CWnd类的虚拟函数PreTranslateMessage(),通过重载这个函数,我们可以改变MFC的消息控制流程,甚至可以作一个全新的控制流出来。只有穿过消息队列的消息才受PreTranslateMessage()影响,采用SendMessage()或其他类似的方式向窗口直接发送的而不经过消息队列的消息根本不会理睬PreTranslateMessage()的存在。

  一、是否调用TranslateMessage()和DispatchMessage()是由一个名称为PreTranslateMessage()函数的返回值决定的,如果该函数返回TRUE,则不会把该消息分发给窗口函数处理。

  二、传给PreTranslateMessage()的消息是未经翻译过的消息,它没有经过TranslateMessage()处理。可以在该函数中使用(pMsg->wParam==VK_RETURN)来拦截回车键。

  三、在WindowProc里不能处理WM_Char消息。

  四、SetWindowText会发送WM_Char给窗口。

  五、PeekMessage和GetMessage的区别:

  GetMessage在没有消息的时候等待消息,cpu占用率当然低。

  PeekMessage没有消息的时候立刻返回,可以在没有消息的时候可以做其他处理,但cpu占用率一般较高。

  大多游戏都用PeekMessage();

PreTranslateMessage作用和使用方法
 PreTranslateMessage是消息在送给TranslateMessage函数之前被调用的,绝大多数本窗口的消息都要通过这里,比较常用,当需要在MFC之前处理某些消息时,常常要在这里添加代码.  

       MFC消息控制流最具特色的地方是CWnd类的虚拟函数PreTranslateMessage(),通过重载这个函数,可以改变MFC的消息控制流程,甚至可以作一个全新的控制流出来。只有穿过消息队列的消息才受PreTranslateMessage()影响,采用SendMessage()或其他类似的方式向窗口直接发送的而不经过消息队列的消息根本不会理睬PreTranslateMessage()的存在。 
 
       是否调用TranslateMessage()和DispatchMessage()是由一个名称为PreTranslateMessage()函数的返回值决定的,如果该函数返回TRUE,则不会把该消息分发给窗口函数处理。
    传给PreTranslateMessage()的消息是未经翻译过的消息,它没有经过TranslateMessage()处理。可以在该函数中使用(pMsg->wParam==VK_RETURN)来拦截回车键。
wParam中存放的是键盘上字符的虚拟码。

PeekMessage和GetMessage的区别:

GetMessage在没有消息的时候等待消息,cpu当然低

PeekMessage没有消息的时候立刻返回,所以cpu占用率高。

因为游戏不能靠windows消息驱动,所以要用PeekMessage();

     PretranslateMessage的实现,不得不谈到MFC消息循环的实现。MFC通过CWinApp类中的Pumpmessage函数实现消息循环,但是实际的消息循环代码位于CWinThread中,CWinApp只是从CWinThread继承过来。其简化后的代码大概如下:
  BOOL CWinThread::PumpMessage()
  {
  _AFX_THREAD_STATE *pState = AfxGetThreadState();
  
  ::GetMessage(&(pState->m_msgCur), NULL, NULL, NULL))
  
  if (!AfxPreTranslateMessage(&(pState->m_msgCur)))
  {
  ::TranslateMessage(&(pState->m_msgCur));
  ::DispatchMessage(&(pState->m_msgCur));
  }
  return TRUE;
  }
  可以看到,PumpMessage在实际的TranslateMessage和DispatchMessage发生之前会调用AfxPreTranslateMessage,AfxPreTranslateMessage又会调用CWnd::WalkPreTranslateTree(虽然也会调用其他函数,但是这个最为关键),其代码如下:
  BOOL PASCAL CWnd::WalkPreTranslateTree(HWND hWndStop, MSG* pMsg)
  {
  ASSERT(hWndStop == NULL || ::IsWindow(hWndStop));
  ASSERT(pMsg != NULL);
  
  // walk from the target window up to the hWndStop window checking
  // if any window wants to translate this message
  
  for (HWND hWnd = pMsg->hwnd; hWnd != NULL; hWnd = ::GetParent(hWnd))
  {
  CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);
  if (pWnd != NULL)
  {
  // target window is a C window
  if (pWnd->PreTranslateMessage(pMsg))
  return TRUE; // trapped by target window (eg: accelerators)
  }
  
  // got to hWndStop window without interest
  if (hWnd == hWndStop)
  break;
  }
  return FALSE; // no special processing
  }
  
  可以看到,代码还是很直接的。从接受到消息的窗口层层往上遍历,并调用PretranslateMessage看是否返回TRUE,是则结束,否则继续。
  这里有一个地方非常关键:CWnd *pWnd = CWnd::FromHandlePermanent(hWnd) 这一句代码从当前AfxModuleThreadState拿到Permanent句柄表,从而找到hWnd对应的CWnd


MFC中PreTranslateMessage是GetMessage(...)函数的下一级操作,即GetMessage(...)从消息队列中获取消息后,交由PreTranslateMessage()处理,若其返回FALSE则再交给TranslateMessage和DispatchMessage处理(进入WindowProc);  
如果用SendMessage,   则消息直接交到WindowProc处理,所以GetMessage不会取得SendMessage的消息,当然PreTranslateMessage也就不会被调用。   [Page]
如果用PostMessage,则消息进入消息队列,由GetMessage取得,PreTranslateMessage就有机会进行处理。
 
 
windows消息处理机制是这样的 :  
    
 首先系统(也就是windows)把来自硬件(鼠标,键盘等消息)和来自应用程序的消息 放到一个系统消息队列中去而应用程序需要有自己的消息队列,也就是线程消息队列,每一个线程有自己的消息队列,对于多线程的应用程序就有和线程数目相等的线程消息队列
.  
  windows
消息队列把得到的消息发送到线程消息队列,线程消息队列每次取出一条消息发送到指定窗口,不断循环直到程序退出.这个循环就是靠消息环(while(GetMessage()) TranslateMessage();DispatchMessage();实现的.GetMessage()只是从线程消息中取出一条消息,TranslateMessage()把virtue key消息转化成character消息,如VK_F1会转化成WM_HELP,而DispatchMessage  则把取出的消息发送到目的窗口.如果收到WM_CLOSE消息则结束循环,发送postqiutmessage(0),处理WM_DESTROY销毁窗口!
 
 while (GetMessage(&msg, NULL, 0, 0))          //C++ code
 {  
        TranslateMessage(&msg);
        DispatchMessage(&msg);
 }

 

 

 

 

同时也算是知道了一个获取键盘输入的方法吧

 if(pMsg->message==WM_CHAR)//WM_CHAR应该是说键盘被点击获得的char
 {     AfxMessageBox("这是键盘点击的");
}  


 

 if (pMsg->wParam>='0'&&pMsg->wParam<='9')//对键盘获取的消息判定是不是 0-9的数字 因为是char类型
        {
            On0_9((pMsg->wParam-'0')+IDC_0);
		
        } 

    详细信息可以看代码
2 另外一点收获 代码之所以调试这么长时间就是这个原因了.上代码

BEGIN_MESSAGE_MAP(CMyDlg, CDialog)
	//{{AFX_MSG_MAP(CMyDlg)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_POINT, OnPoint)
	ON_BN_CLICKED(IDC_ADD, OnAdd)
	ON_BN_CLICKED(IDC_BUTTON14, OnJian)
	ON_BN_CLICKED(IDC_BUTTON16, OnCheng)
	ON_BN_CLICKED(IDC_BUTTON13, OnChu)
	ON_BN_CLICKED(IDC_BUTTON12, OnNum)
	ON_BN_CLICKED(IDOK2, OnClear)
	//}}AFX_MSG_MAP
	ON_COMMAND_RANGE(IDC_0,IDC_9,On0_9)
END_MESSAGE_MAP()

这一段是在类 CMyDlg的DoDataExchange(CDataExchange* pDX)方法后边的 查找了部分资料:

 

BEGIN_MESSAGE_MAP(CpassApp, CWinApp) 

  ON_COMMAND(ID_HELP, CWinApp::OnHelp) 

  END_MESSAGE_MAP() 

  这些都是宏定义,不是函数。 

  在BEGIN_MESSAGE_MAP()和END_MESSAGE_MAP()之间添加你的消息响应函数,为每个消息处理函数加入一个入口 

  BEGIN_MESSAGE_MAP( theClass, baseClass )//

  
  
theClass指定消息映射所属的类的名字。
baseClass指定theClass的基类的名字。

使用BEGIN_MESSAGE_MAP宏开始你的消息映射的定义。

  在你的类的成员函数的实现文件(.CPP)中,使用BEGIN_MESSAGE_MAP宏开始消息映射,然后为每个消息处理函数加入一个入口,最后用END_MESSAGE_MAP宏结束消息映射。

  每个消息映射入口的格式如下:

  ON_Notification(id, memberFxn)

  其中id指定了发送通知的控件的子窗口的ID,而memberFxn指定了处理该通知的父对象中的成员函数名。

  父对象的函数原型格式如下:

  afx_msg void memberFxn( );

  可能的消息映射入口如下:

  

映射入口何时向父对象发送消息
ON_BN_CLICKED用户单击按钮时
ON_BN_DOUBLECLICKED用户双击按钮时

举例:

  1 BEGIN_MESSAGE_MAP(CpassDlg, CDialog)

  2 ON_WM_SYSCOMMAND()

  3 ON_WM_PAINT()

  4 ON_WM_QUERYDRAGICON()

  5 //}}AFX_MSG_MAP

  6 ON_BN_CLICKED(IDOK, OnOK)

  7 ON_BN_CLICKED(IDCANCEL, OnExit)

  8 END_MESSAGE_MAP()

  void CTestDlg::OnSysCommand(UINT nID, LPARAM lParam)

  这个函数响应系统控制菜单的命令.(即左上角图标处)。

  OnSysCommand:The framework calls this member function when the user selects a command from the Control menu, or when the user selects the Maximize or the Minimize button.

另外:

  DoDataExchange:当UpdateData时候

  OnInitDialog:对话框类已经构造,但是对话框还没有显示出来的时候

  OnQueryDragIcon:The framework calls this member function by a minimized (iconic) window that does not have an icon defined for its class. The system makes this call to obtain the cursor to display while the user drags the minimized window.

上传的计算器代码中多出了一行代码 因为在计算机代码中 数字键是没有显式的通过ClassWizard添加事件 但是在运行的时候后台却可以获取点击的按钮的值 这有点不解 原来原因在这

	ON_COMMAND_RANGE(IDC_0,IDC_9,On0_9)

这一句表示 id号从IDC_0到IDC_9的10个数字键在点击的时候调用函数 ON0_9(...)

先说一下它的用法 上代码

BEGIN_MESSAGE_MAP(CIOStatue, CDialog)
//{{AFX_MSG_MAP(CIOStatue)
//}}AFX_MSG_MAP
ON_COMMAND_RANGE(IDC_STATIC_OUT1,IDC_STATIC_OUT16,OnOutPutStatusButtonUp)
END_MESSAGE_MAP()
//注意IDC_STATIC_OUT1,IDC_STATIC_OUT16之间是连续的


void CIOStatue::OnOutPutStatusButtonUp(WPARAM wParam, LPARAM lParam)
{
  switch(wParam)
  {
    case IDC_STATIC_OUT1:
    //代码1
    break;
    case IDC_STATIC_OUT2:
   //代码2
   break;
   
  case IDC_STATIC_OUT3:
 //
  break;
  //等
  }
  
}


注释:
当按下IDC_STATIC_OUT1按钮,执行 代码1的程序。
当按下IDC_STATIC_OUT2按钮,执行 代码2的程序。

等等

ON_COMMAND_RANGE

ON_COMMAND_RANGE( id1, id2, memberFxn )

参数:

id1一个连续范围的命令ID的起始值。
id2一个连续范围的命令ID的结束值。
memberFxn该命令被映射到的消息处理函数的名字。

说明:
使用这个宏把一个连续范围的命令ID映射到单个命令处理函数。ID的范围从id1开始,到id2结束。
用ON_COMMAND_RANGE把一个范围的命令ID映射到一个成员函数。用ON_COMMAND把单个命令ID映射到成员函数。每个给定的命令ID只能有一个消息映射入口。这就是说,不能把命令映射到多于一个的处理函数。关于映射消息范围的更多信息参见“Visual C++程序员指南”中的“消息映射范围的处理函数”。
ClassWizard不支持消息映射范围,所以你必须自己写入这个宏。确保你把它写在了消息映射的//{{AFX_MSG_MAP分界符外面。

请参阅:
ON_UPDATE_COMMAND_UI_RANGE, ON_CONTROL_RANGE, ON_COMMAND

但是在那一句宏定义的代码里只有一个函数名 甚至于连参数都省略了 我又查了一下资料,顺便又扩展了一下视野

VC++中的ON_COMMAND_RANGE宏和ON_COMMAND等宏一样,是用来声明消息处理函数的,与
ON_COMMAND不同的是,此宏可用来定义一组消息的处理函数。

两个宏的用法是:
ON_COMMAND(id,memberFxn)
ON_COMMAND_RANGE(
id1,id2,memberFxn)

看起来其中memberFxn似乎没有什么差别,但是在学习使用中,发现ON_COMMAND_RANGE宏中的memberFxn常常会被定义成带参数的处理函数,而ON_COMMAND则是不带参数的处理函数。这就让我奇怪了,于是查阅MSDN的解释,循着提供的线索一直查到了《Visual C++ Programmer’s Guide》中的《Handlers for Message-Map Ranges》一章,于是在《Declaring the Handler Function》一节中找到了答案,引用原文如下:

Handler functions for single commands normally take no parameters. With the exception of update handler functions, handler functions for message-map ranges require an extra parameter, nID, of type UINT. This parameter is the first parameter. The extra parameter accommodates the extra command ID needed to specify which command the user actually chose. 

为了帮助和我一样不太懂英文的朋友,顶着头皮翻译了下:
单个命令(消息)的处理函数通常不带参数。但是更新处理函数、针对消息映射范围的处理函数需要一个额外的参数,一个UINT类型的nID。此参数是第一个参数,这个额外的参数收集了用来指定用户真正选择命令的命令ID。

因此完全可以一个带参数的消息处理函数来接收指定的命令ID,不过这里要提醒朋友一点的是,并不是非得只有一个参数,可以使用多个参数的消息处理函数,如 function(WPARAM wParam, LPARAM lParam),但是真正能够使用的参数是第一个参数 wParam,它是用户选择的命令ID。

对于更新处理函数,也存在相对应于ON_COMMAND_RANGE的 ON_UPDATE_COMMAND_UI_RANGE宏,它的作用也是处理连续的范围(contiguous range),但是它与ON_COMMAND_RANGE有一点点区别。通过上面可知,一般范围处理函数(ON_COMMAND_RANGE)是通过在处理函数后面加一个参数,当消息进行处理时,将会把该命令ID传至该参数。而ON_UPDATE_COMMAND_UI_RANGE则使用的是 pCmdUI,一个 CCmdUI的指针类型,它内部就包含了一个数据成员(m_nID),用来指向命令ID,具体请参考MSDN中的内容。上述内容在MSDN中的《 Visual C++ Programmer’s Guide》中的《Handlers for Message-Map Ranges》一章《Example for a Range of Command IDs》一节中可以找到原文:

Update handler functions for single commands normally take a single parameter, pCmdUI, of type CCmdUI*. Unlike handler functions, update handler functions for message-map ranges do not require an extra parameter, nID, of type UINT. The command ID, which is needed to specify which command the user actually chose, is found in the CCmdUI object.

意思大概也是:
单个命令的更新处理函数通常只带一个参数,即一个CCmdUI指针类型的pCmdUI。不像普通的处理函数一样,针对消息映射范围的更新处理函数不需要一个额外的UINT参数nID,这个用来指定用户真正选择的命令ID,能够在CCmdUI对象中找到。
 
 
资料好多 扩展起来没完 以后遇到再记吧

    ON_COMMAND 以及 ON_COMMAND_RANGE 是用于处理菜单项或工具栏按钮的命令消息的对于复选框这样的控件,请使用ON_CONTROL_RANGE1. 打开 resource.h,将你的复选框控件ID进行连续编号,例如1001,1002...2. 在对话框类的头文件中添加公有成员函数:afx_msg void OnCheckBox(UINT uID) ;3. 在对话框类的cpp文件中BEGIN_MESSAGE_MAP(CTestDlg, CDialog) 的 //}}AFX_MSG_MAP下添加代码 ON_CONTROL_RANGE(BN_CLICKED,1001,1003,OnCheckBox)4. 然后在cpp文件中添加函数void CTestDlg::OnCheckBox(UINT uID) {}5. 在函数内判断 uID,来确定用户单击了哪项复选框,并作相应处理。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值