ON_COMMAND_RANGE多个按钮响应一个函数的解决方法

本文描述了ON_COMMAND_RANGE多个按钮响应一个函数的解决方法。

开发人员需要注意在自定义消息响应函数的声明过程中,一定要注意参数的形式,稍微一疏忽就会导致莫须有的错误,具体以ON_COMMAND_RANGE为例说下。

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() ;

问题解决!

附加:

1、ON_COMMAND(ID_VIEW_CUSTOMIZE, OnViewCustomize)==>void CMainFrame::OnViewCustomize();或void CMainFrame::OnViewCustomize(WPARAM wParam, LPARAM lParam);

2、ON_REGISTERED_MESSAGE(AFX_WM_RESETTOOLBAR, OnToolbarReset)==>afx_msg LRESULT CMainFrame::OnToolbarReset(WPARAM /*wp*/,LPARAM);

3、ON_COMMAND_RANGE(ID_SHORTCUT_1, ID_SHORTCUT_5, OnOutlookBarShortcut)==>void CMainFrame::OnOutlookBarShortcut(UINT id);

4、ON_UPDATE_COMMAND_UI(ID_VIEW_CAPTIONBAR, OnUpdateViewCaptionBar)==>void CMainFrame::OnUpdateViewCaptionBar(CCmdUI* pCmdUI);

(0)

相关推荐

  • FloatingActionButton增强版一个按钮跳出多个按钮第三方开源之FloatingActionButton

    FloatingActionButton项目在github上的主页:https://github.com/futuresimple/android-floating-action-button FloatingActionButton使用简单,而且可以自定义颜色.大小.背景图片 项目构造: 下面是demo的代码(主要见sample): 布局: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android&

  • from表单多个按钮提交用onclick跳转不同action

    复制代码 代码如下: <form name="form1" method="post" action="ad/BusinessAction!list.action"> //下面input框提交的action不同,用onclick跳转,写一个js方法 <input type="button" class="mulButton" name="Ids" id="al

  • ON_COMMAND_RANGE多个按钮响应一个函数的解决方法

    本文描述了ON_COMMAND_RANGE多个按钮响应一个函数的解决方法. 开发人员需要注意在自定义消息响应函数的声明过程中,一定要注意参数的形式,稍微一疏忽就会导致莫须有的错误,具体以ON_COMMAND_RANGE为例说下. 1.声明消息响应函数:在要添加的工程上添加函数afx_msg void OnButtonPort(); 2.消息映射: BEGIN_MESSAGE_MAP(CXXXDlg, CDialog) //{{AFX_MSG_MAP(CXXXDlg) ON_WM_SYSCOMMA

  • 关于python下cv.waitKey无响应的原因及解决方法

    按下键的时候,焦点要落在窗口上,不能落在cmd窗口上. 另外,一般在imshow()后要使用waitKey(),给图像绘制留下时间,不然窗口会出现无响应情况,并且图像无法显示出来. int waitKey(int delay=0) - 延时delay = 0 函数则延时无限长,必须有键按下才继续执行. - 延时delay > 0 函数返回值为按下的键的ASCII码值,超时则返回-1. OpenCV: waitKey waitKey Waits for a pressed key. C++: in

  • VIVO手机上del键无效OnKeyListener不响应的原因及解决方法

    在修改交易密码界面用到一个自定义输入框 类似这种(原谅丑了点~) 结果昨天有用户反馈在VIVO手机上回出现,Del键无效的问题~~~ 最后发现是:EdiText的OnKeyListener没有响应. 最后搜搜了一下,找到了解决方案:重写EdiTextView的InputConnectionWrapper的方法后解决了问题: 代码如下: public class ZanyEditText extends EditText { private OnDelKeyEventListener delKey

  • Python实现动态添加类的属性或成员函数的解决方法

    某些时候我们需要让类动态的添加属性或方法,比如我们在做插件时就可以采用这种方法.用一个配置文件指定需要加载的模块,可以根据业务扩展任意加入需要的模块. 本文就此简述了Python实现动态添加类的属性或成员函数的解决方法,具体方法如下: 首先我们可以参考ulipad的实现:mixin. 这里做的比较简单,只是声明一个类,类初始化的时候读取配置文件,根据配置列表加载特定目录下的模块下的函数,函数和模块同名,将此函数动态加载为类的成员函数. 代码如下所示: class WinBAS(Bas): def

  • easyui datagrid 表格中操作栏 按钮图标不显示的解决方法

    jQuery EasyUI动态添加控件或者ajax加载页面后不能自动渲染 解决办法: 使用解析器 Parser(解析器) $.parser.parse(); // 解析所有页面 $.parser.parse('#cc'); // 解析指定节点 以上这篇easyui datagrid 表格中操作栏 按钮图标不显示的解决方法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们.

  • Android Studio中Run按钮是灰色的快速解决方法

    首先是,在不同的AS中,gradle版本不同,下载的sdk版本不同,这些,都在gradle(Project.Models)相关代码里调过来就好.之前的文章里有说过. 经过调好gradle这些文件,AS已经可以built 成功后. 下一步,Run the application. 这时候,遇到问题:Run按钮灰色,失效. 点击Run旁边 Select Run/Debug Configuration按钮 选择 Edit Configuration,于是: 在model下拉框中选择app.如果下拉框中

  • Layui多选只有最后一个值的解决方法

    在使用Layui的时候,获取checkbox的表单数据,会出现只会获取到最后一个选中的值,不管是将checkbox的name设置成 name[]或name[1] ,name[2]均无效. form.on('submit(add)',function(data){ console.log(data.field); }); 此时你会发现,输入的checkbox中只有最后一个值.没有办法,只能自己想办法处理了. var industryObj = $('input[name="industry&quo

  • 关于Centos7中Vscode无响应的问题及解决方法

    版本信息 虚拟机版本:VMware® Workstation 16 Pro 16.1.2 build-17966106 Linux系统:CentOS7 问题描述 在CentOS7中使用命令行直接安装Vscode时,打开Vscode出现界面卡死.无响应情况 问题原因 在安装Vscode时,系统默认安装和官网下载的Vscode版本,都是最新版本的Vscode,图为笔者安装时的最新版本:1.58,由于CentOS7与Vscode的新版本不兼容,导致Vscode无法使用. 解决方案 步骤一:卸载新版本的

  • 定时器(setTimeout/setInterval)调用带参函数失效解决方法

    首先来看下定时器的用法 1. setInterval(code,millisec[,"lang"]) setInterval() 方法可按照指定的周期(以毫秒计)来调用函数或计算表达式. 参数 描述 code 必需,要调用的函数或要执行的代码串. millisec 必需,周期性执行或调用 code 之间的时间间隔,以毫秒计.2.setTimeout(code,millisec) setTimeout() 方法用于在指定的毫秒数后调用函数或计算表达式. 参数 描述 code 必需,要调用

  • asp.net中提示该行已属于另一个表的解决方法

    复制代码 代码如下: DataTable dt = new DataTable();dt = ds.Tables["All"].Clone();//克隆All的结构传递给dtDataRow[] dr=this.dataSet31.Tables["Product"].Select("bc=1"); //通过条件得到符合条件的行for(int i=0;i<dr.Length;i++){//将数组元素加入表...dt.Rows.Add(dr[i]

随机推荐