消息申明:
ON_COMMAND_RANGE(ID_CLOUD, ID_CLOUD + 4, On
消息响应函数申明:
afx_msg void On
通过wParam参数的低字节判断菜单的ID, 进而来实现不同菜单的消息响应.
因为以前写过类似的程序, 就直接Copy了过来, 在Debug下调试好了, 想应该OK了, 结果在Release版本
下只要对托盘右键菜单进行操作就发生内存错误, 搞得自己很是郁闷, 弄了很久, 没有办法, 就在Release版本下进行了调试, 结果发现是消息响应中出现了问题, 网上也找了很久, 没有找到解决的办法, 最后也只好老实看MSDN, 结果发现我的消息响应函数中的申明可能存在问题:
afx_msg void On
On
通过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 On
今天又花了整整一天上网查阅相关的资料并利用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, On
// ON_CONTROL_RANGE(0, id, idLast, On
函数调用过程中, 会将传入的参数进行压栈操作, 因为MFC默认的传入参数只有一个, 因此调用On
在DEBUG上没有出现内存错误在于在调用On
pop edi
pop esi
pop ebx
add esp, 48h
cmp ebp, esp
call __chkesp (0041e680)
mov esp, ebp
pop ebp
ret 8(两个参数出栈)
此汇编代码的作用就是在函数返回时检查调用中和调用返回时的栈是否一致, 如果不一致, 就强制平栈操作, 因为在这个调用过程中, 传入On
在Release版本下, 就没有了检测栈的操作,
只是简单的下面几句汇编代码完成出栈操作:
mov esp, ebp
pop ebp
ret 8两个参数出栈)
可以明显看到, Release下出现了栈操作不平衡的情况, 即入栈数小于出栈数, 从而导致栈区地址错误, 当其它函数两次对栈区进行地址访问时就极有可能出现内存错误的现象了.
所以, 平时写程序时在Debug下高度完成之后, 最好还在Release下看一下, 因为有些时候, Debug下对函数参数的检查不是那么严格, 并且在栈的操作上, Debug可以帮助我们解决很多隐藏的问题, 但是Release下就不会了. 另外在自定义的消息响应函数中, Debug和Release都不会对响应函数的参数列表与MFC默认参数列表进行一致性检测, 从而可能隐藏重大的内存出错的可能性, 导致最终软件在Release下运行可能发生崩溃.
所以上面MSDN的东西说得模棱两可的, 明明ON_COMMAND_RANGE消息响应函数就只能那样定义, 他却说成那样, ......................
自定义的消息响应函数可以在AFXMSG.H下看到MFC默认的定义, 为了不引起上面谈到的问题, 最好遵照这个头文件里面说明的进行定义, 它可以在VC安装目录下的VC98/MFC/SRC查找到.