引用地址:
http://blog.csdn.net/jinhill/article/details/8581555
http://blog.csdn.net/guoquan2003/article/details/4532055
一、先简单介绍一下ON_COMMAND_RANGE的使用
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分界符外面。
注:memberFxn 这个afx_msg函数带一个UINT uID参数,当然你也可以在后面追加些你其他想要的参数, 这个uID是在 id1,id2之间的,但是MFC提供的这个 id1,id2是有个范围的,最大为65535个,所以用户在创建动态菜单,BUTTON时,在资源文件中定义这些宏时请最好不要超过这些范围,如果硬是要超过65535,那麽在memberFxn(UINT uID)函数判断的时候 需要取余 uID%65535 处理。
资源发生了修改需要重新buildAll一下
二、ON_COMMAND_RANGE使用过程中出现的问题及解决办法
今天主要介绍一下ON_COMMAND_RANGE的用法第一次用这个方法还是刚毕业那会,那时写过一个控制程序,界面上有很多电器的控制按钮,这些按钮的响应函数基本一致,只是相应的ID值不一样,要是一一写响应函数那不累死人,于是就东找西找,找到ON_COMMAND_RANGE。
最近一个偶然机会也要用到它,三下五除二,CODE写完了,
1.在要添加的工程上添加函数afx_msg void OnButtonPort();
2.在
BEGIN_MESSAGE_MAP(CXXXDlg, CDialog)
//{{AFX_MSG_MAP(CXXXDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_TIMER()
//}}AFX_MSG_MAP
//这里的IDC_BUTTON_PORT_1和 IDC_BUTTON_START_ALL之间有很多个Button,并且ID连续
ON_COMMAND_RANGE(IDC_BUTTON_PORT_1, IDC_BUTTON_START_ALL, OnButtonPort)
ON_WM_DEVICECHANGE()
END_MESSAGE_MAP()
3.写你自己的响应函数 void CXXXDlg::OnButtonPort()
注:此代码DEBUG OK,Relase异常,不可直接参考,且听下面分解:
DEBUG通过,不料Release却直接崩溃,写了这么多年的CODE还真第一次遇到这种情况,为什么ON_COMMAND_RANGE Debug正常,Release不正常呢?
先MSDN:
Use this macro to map a contiguous range of command IDs to a single message handler function.
ON_COMMAND_RANGE(id1, id2, memberFxn )
Parameters
id1
Command ID at the beginning of a contiguous range of command IDs.
id2
Command ID at the end of a contiguous range of command IDs.
memberFxn
The name of the message-handler function to which the commands are mapped.
Remarks
The range of IDs starts with id1 and ends with id2.
Use ON_COMMAND_RANGE to map a range of command IDs to one member function. Use ON_COMMAND to map a single command to a member function. Only one message-map entry can match a given command ID. That is, you can't map a command to more than one handler. For more information on mapping message ranges, see Handlers for Message-Map Ranges.
There is no automatic support for message map ranges, so you must place the macro yourself.
MSDN也没有特别说明要注意什么的,我觉得我用的也很正常,于是在网上又搜了一大会,有一个网友非常专业的解释的原因,这里收藏了:http://yiyunscu.blog.163.com/blog/static/3626332020099802057982/
以下是转载内容:
该网友定义台下:
afx_msg void OnCommandMy(WPARAM wParam, LPARAM lParam );
申明只适用于ON_COMMAND消息的函数申明, 而ON_COMMAND_RANGE的函数申明在MSDN中建议写成这样:
OnCommandMy(UINT nID);
通过switch(nID) case **:进行针对不同菜单进行消息响应.
nID就是菜单传入消息的ID号, 奇怪的是, 在Debug版本下, 先前的申明方式运行完全正常, 查阅了MSDN, 找出了可能的原因:
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.
针对单个Command消息响应函数可以不带参数, 但是对于多个Command消息如ON_COMMAND_RANGE申明的消息响应需要将函数参数列表中的第一个参数定义为UINT nID, 指明command 的ID号, 按照MSDN的理解, ON_COMMAND_RANGE也可以像ON_COMMAND那样在消息响应函数中定义两个参数, 如afx_msg void OnCommandMy(WPARAM wParam, LPARAM lParam );在Debug和Release下, 编译不会出现问题, 在Debug下运行也不会出现问题, 但是在Release下面却出现内存错误, 所以可以带多个参数感觉只能在Debug下可以行的能, 在Release下就没失效了.
今天又花了整整一天上网查阅相关的资料并利用VC查看相应的汇编代码发现, 应该是函数调用和返回时栈操作不平衡导致Release版本下出现了内存错误的问题, ON_COMMAND_RANGE在MFC默认的消息响应函数中, 参数只有一个, 如:
#define ON_COMMAND_RANGE(id, idLast, memberFxn) \
{ WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)idLast, AfxSig_vw, \
(AFX_PMSG)(void (AFX_MSG_CALL CCmdTarget::*)(UINT))&memberFxn },
// ON_COMMAND_RANGE(id, idLast, OnFoo) is the same as
// ON_CONTROL_RANGE(0, id, idLast, OnFoo)
函数调用过程中, 会将传入的参数进行压栈操作, 因为MFC默认的传入参数只有一个, 因此调用OnCommandMy时会有系统传入的一个消息参数进行压栈操作. 在函数返回时, 应该进行出栈操作, 并且保证调用完成后栈维持平衡, 否则会出现可能的内存错误.
在DEBUG上没有出现内存错误在于在调用OnCommandMy函数返回时编译器在返回代码处添加了如下的汇编代码:
pop edi
pop esi
pop ebx
add esp, 48h
cmp ebp, esp
call __chkesp (0041e680)
mov esp, ebp
pop ebp
ret 8(两个参数出栈)
此汇编代码的作用就是在函数返回时检查调用中和调用返回时的栈是否一致, 如果不一致, 就强制平栈操作, 因为在这个调用过程中, 传入OnCommandMy的消息参数只有一个(只是申明成两个, 实际只有一个参数传入), 所以存在栈不一致的情况, 但是强制平栈可以避免由此引起的错误.
在Release版本下, 就没有了检测栈的操作,
只是简单的下面几句汇编代码完成出栈操作:
mov esp, ebp
pop ebp
ret 8两个参数出栈)
可以明显看到, Release下出现了栈操作不平衡的情况, 即入栈数小于出栈数, 从而导致栈区地址错误, 当其它函数两次对栈区进行地址访问时就极有可能出现内存错误的现象了.
所以, 平时写程序时在Debug下高度完成之后, 最好还在Release下看一下, 因为有些时候, Debug下对函数参数的检查不是那么严格, 并且在栈的操作上, Debug可以帮助我们解决很多隐藏的问题, 但是Release下就不会了. 另外在自定义的消息响应函数中, Debug和Release都不会对响应函数的参数列表与MFC默认参数列表进行一致性检测, 从而可能隐藏重大的内存出错的可能性, 导致最终软件在Release下运行可能发生崩溃.
终于明白了,原来是ON_COMMAND_RANGE只能带一个参数,带两个或不带都会异常
所以重新定义
afx_msg void OnButtonPort(UINT nID);
而且此nID就是你点击的按钮ID值,再也不用之前的麻烦代码了
CWnd *pWnd = GetFocus();
int nPortID = pWnd->GetDlgCtrlID() ;
问题解决!