ON_COMMAND_RANGE的用法

2024-09-06 13:08
文章标签 range 用法 command

本文主要是介绍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() ;

问题解决!大笑

这篇关于ON_COMMAND_RANGE的用法的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1142089

相关文章

JDK21对虚拟线程的几种用法实践指南

《JDK21对虚拟线程的几种用法实践指南》虚拟线程是Java中的一种轻量级线程,由JVM管理,特别适合于I/O密集型任务,:本文主要介绍JDK21对虚拟线程的几种用法,文中通过代码介绍的非常详细,... 目录一、参考官方文档二、什么是虚拟线程三、几种用法1、Thread.ofVirtual().start(

Java8 Collectors.toMap() 的两种用法

《Java8Collectors.toMap()的两种用法》Collectors.toMap():JDK8中提供,用于将Stream流转换为Map,本文给大家介绍Java8Collector... 目录一、简单介绍用法1:根据某一属性,对对象的实例或属性做映射用法2:根据某一属性,对对象集合进行去重二、Du

Python中isinstance()函数原理解释及详细用法示例

《Python中isinstance()函数原理解释及详细用法示例》isinstance()是Python内置的一个非常有用的函数,用于检查一个对象是否属于指定的类型或类型元组中的某一个类型,它是Py... 目录python中isinstance()函数原理解释及详细用法指南一、isinstance()函数

Python中的sort方法、sorted函数与lambda表达式及用法详解

《Python中的sort方法、sorted函数与lambda表达式及用法详解》文章对比了Python中list.sort()与sorted()函数的区别,指出sort()原地排序返回None,sor... 目录1. sort()方法1.1 sort()方法1.2 基本语法和参数A. reverse参数B.

vue监听属性watch的用法及使用场景详解

《vue监听属性watch的用法及使用场景详解》watch是vue中常用的监听器,它主要用于侦听数据的变化,在数据发生变化的时候执行一些操作,:本文主要介绍vue监听属性watch的用法及使用场景... 目录1. 监听属性 watch2. 常规用法3. 监听对象和route变化4. 使用场景附Watch 的

Java Instrumentation从概念到基本用法详解

《JavaInstrumentation从概念到基本用法详解》JavaInstrumentation是java.lang.instrument包提供的API,允许开发者在类被JVM加载时对其进行修改... 目录一、什么是 Java Instrumentation主要用途二、核心概念1. Java Agent

Java 中 Optional 的用法及最佳实践

《Java中Optional的用法及最佳实践》在Java开发中,空指针异常(NullPointerException)是开发者最常遇到的问题之一,本篇文章将详细讲解Optional的用法、常用方... 目录前言1. 什么是 Optional?主要特性:2. Optional 的基本用法2.1 创建 Opti

Python函数的基本用法、返回值特性、全局变量修改及异常处理技巧

《Python函数的基本用法、返回值特性、全局变量修改及异常处理技巧》本文将通过实际代码示例,深入讲解Python函数的基本用法、返回值特性、全局变量修改以及异常处理技巧,感兴趣的朋友跟随小编一起看看... 目录一、python函数定义与调用1.1 基本函数定义1.2 函数调用二、函数返回值详解2.1 有返

Python中logging模块用法示例总结

《Python中logging模块用法示例总结》在Python中logging模块是一个强大的日志记录工具,它允许用户将程序运行期间产生的日志信息输出到控制台或者写入到文件中,:本文主要介绍Pyt... 目录前言一. 基本使用1. 五种日志等级2.  设置报告等级3. 自定义格式4. C语言风格的格式化方法

SpringBoot 获取请求参数的常用注解及用法

《SpringBoot获取请求参数的常用注解及用法》SpringBoot通过@RequestParam、@PathVariable等注解支持从HTTP请求中获取参数,涵盖查询、路径、请求体、头、C... 目录SpringBoot 提供了多种注解来方便地从 HTTP 请求中获取参数以下是主要的注解及其用法:1