MFC中SendMessage,Release版出错Debug版不出错的一个最常见原因之深入剖析

来源:互联网 发布:js遍历json数组 的key 编辑:程序博客网 时间:2024/04/29 06:48

来自:http://blog.sina.com.cn/s/blog_47fa4cef0100s80j.html

编译器 vc6.0


也不知道网上有没有类似的文章,小弟斗胆在这里献丑一回; 
       最近一段时间,许多人发帖子说自己的MFC程序Release版会出错,而Debug版不会出错,记得在两年前我也曾遇到过类似的问题,但是没有进行深入研究,这两天我对这个问题作了一个深入的探讨发现了一个非常容易犯的错误,这也与VC编译器有关(不知道是微软的BUG还是怎么回事),首先我们看一个事例工程: 
       用VC新建一个Dialog工程,然后加入一个新的对话窗,并且生成一个对话窗类;然后在主对话窗的OnOK事件中建立那个新对话窗的非模态对话窗,例如下面: 
void  CADlg::OnOK()   

           m_pDlg  =  new  CDlg1;//m_pDlg是类成员变量,新对话窗的指针 
           m_pDlg->Create(IDD_DIALOG1); 
           m_pDlg->ShowWindow(SW_SHOW); 

 
       然后加一个自定义消息:WM_MYMSG; 
       在新的对话窗的OnOK事件中向主对话窗发送WM_MYMSG消息; 
void  CDlg1::OnOK()   

           CWnd  *pWnd  =  GetParent(); 
           pWnd->SendMessage(WM_MYMSG); 

       下面我要说的就是最关键的地方了,我们为了响应WM_MYMSG消息通常有两种做法,一种是重载主对话窗的WindowProc虚函数,然后在函数内部响应这个消息,例如下面: 
LRESULT  CADlg::WindowProc(UINT  message,  WPARAM  wParam,  LPARAM  lParam)   

       Switch(message) 
       { 
       case  WM_MYMSG: 
               { 
                   …… 
                   break; 
               } 
       } 
       return  CDialog::WindowProc(message,  wParam,  lParam); 

       这种做法通常不会出错; 
       下面我们看第二种响应这个消息的方法; 
       首先在主对话窗中加入一个函数,例如下面: 
VOID  CADlg::OnMyMsg() 


       然后在主对话窗的消息映射表中加一项: 
BEGIN_MESSAGE_MAP(CADlg,  CDialog) 
                       //{{AFX_MSG_MAP(CADlg) 
                       ON_MESSAGE(WM_MYMSG  ,  OnMyMsg) 
                       //}}AFX_MSG_MAP 
END_MESSAGE_MAP() 
       这样当我们的子对话窗向主对话窗发送WM_MYMSG消息的时候,MFC就会调用我们的OnMyMsg函数,于是错误出现了,首先我们看看ON_MESSAGE宏的定义; 
#define  ON_MESSAGE(message,  memberFxn)  \ 
           {  message,  0,  0,  0,  AfxSig_lwl,  \ 
                       (AFX_PMSG)(AFX_PMSGW)(LRESULT  (AFX_MSG_CALL  CWnd::*)(WPARAM, LPARAM))&memberFxn  } 
       熟悉宏定义的朋友一看就明白,这个宏在展开的时候实际上是将两个参数压栈(WPARAM和LPARAM),然后调用函数指针; 
       而我们的函数OnMyMsg确没有参数定义,换句话说,函数返回的时候不会平栈,这就是Release版程序非法操作的原因; 
       再说具体些,我们把上面的OnMyMsg函数写成这样: 
VOID  CADlg::OnMyMsg() 

       MessageBox("测试"); 

然后看看它的汇编代码: 
push                0 
push                0 
push                403020h 
call                004017CA 
ret 
       前面我们就不看了,看最后一句: 
       ret 
       完了,它直接ret了(当然了,直接ret是我们函数定义的结果),而不是比如说什么 
       ret  8 
       之类的语句(这是因为我们的函数没有定义参数,因此直接ret)。 
       这样我们进入函数前压栈的两个参数就没有进行平栈动作了;函数返回,栈不平当然就会非法操作了; 
       换句话说,如果我们的程序写成这样: 
VOID  CADlg::OnMyMsg() 

       MessageBox("测试"); 
       __asm  ret  8; 

       那么Release版就不会报错了(相反Debug版就会报错),不信请试验一下,但是这样写是不对的,请朋友们在编写程序的时候不要这样写,这只是说明消息映射函数平栈情况的一个证据罢了; 
       话分两头,为什么Debug版没有问题呢? 
       先看看下面的汇编: 
mov                  ecx,dword  ptr  [ebp-0Ch] 
mov                  dword  ptr  fs:[0],ecx 
pop                  edi 
pop                  esi 
pop                  ebx 
add                  esp,5Ch 
cmp                  ebp,esp 
call                _chkesp  (004022fc) 
mov                  esp,ebp 
pop                  ebp 
       VC在对MFC的Debug版程序进行编译的时候,会在函数的后面加上一段类似上面的代码,那段代码的功能就是检测esp,看看栈是否是平的,如果不平则强行平栈,因此Debug版程序不会出这种错误,至于微软为什么要这样做,我实在也是想不明白,请各位朋友也一起琢磨一下吧(欢迎跟贴讨论); 
       综上所述,我们在编写MFC程序,映射自己的消息函数的时候要么采用第一种方法,重载WindowProc虚函数,要么采用第二种方法,但是函数要定义两个参数(WPARAM和LPARAM),即使没有用处也要这样定义;这样就可以避免Release版出错Debug版不出错的绝大部分情况了; 
       另外,我这里再提一下这个宏: 
       ON_MESSAGE_VOID 
       这个宏定义在"afxpriv.h",这个宏与ON_MESSAGE相反,他的消息映射函数不能带参数。即如果用这个宏进行消息映射,那么那个消息映射函数就不能带参数,如果带了参数就会发生Release版出错,Debug版不出错的情况了; 
       最后,我们不管用上面那个宏映射消息响应函数,而你的消息响应函数不管定义成什么样子,VC在进行编译的时候都不会报错,因此这个错误将隐藏的很深,直到你即将发布Release版的时候才发现,程序会非法操作的; 
       以上所述仅代表个人看法,如有不同意的朋友,欢迎参加讨论; 
 
--------------------------------------------------------------- 
 
http://expert.csdn.net/Expert/topic/2539/2539864.xml?temp=.9315149 
--------------------------------------------------------------- 
 
哈,vc7就会报错了,我还真不知道,看来微软也意识到这是个BUG了; 
--------------------------------------------------------------- 
 
这是  MFC  的一个  BUG,看看  MSDN  文档说的: 
ms-help://MS.VSCC.2003/MS.MSDNQTR.2003FEB.2052/vccore/html/vcrefwhatsnewlibrariesvisualc70.htm#vcrefwhatsnewlibrariesmfcvisualc70 
 
Visual  C++  .NET  中包含的下列库可能是新增的,也可能是经过更改的。   
....... 
Microsoft  基础类  (MFC)  库 
 有关  MFC  的参考主题包含数百个新的代码示例。   
 静态强制类型转换和  MFC  消息映射      从  Visual  C++  .NET  开始,MFC  对消息处理函数的返回类型和参数类型进行更严格的类型检查。这些新增行为通过使用错误信息标记潜在不安全的消息处理函数,来通知开发人员可能会遇到的问题。MFC  现对  ON_MESSAGE、ON_REGISTERED_MESSAGE、ON_THREAD_MESSAGE  和 ON_REGISTERED_THREAD_MESSAGE  使用静态强制类型转换。   
例如,过去开发人员可以对  ON_MESSAGE  或  ON_REGISTERED_MESSAGE  使用返回  void  而非  LRESULT  的成员函数,并且编译时不报告任何错误。而使用  Visual  C++  .NET,则可以捕获潜在的错误强制类型转换,并将它标记为错误。开发人员可以通过替换返回类型(用  LRESULT  替换)并重新编译来修复这种潜在的问题。   
 
 DHTML  编辑组件:CHtmlEditCtrl、CHTMLEditView、CHtmlEditDoc。   
......... 
 
VC7  的界面用不惯,而且速度慢,否则值得升级。 
 
--------------------------------------------------------------- 
 
对了,建议大家以后做东西的时候都用Release版本做,这样像上面的问题就不会出现,有的人 
可能会说用Release版本怎么调试啊。下面的办法可以解决。调试完了改回原来的设置再发布 
http://expert.csdn.net/Expert/TopicView1.asp?id=2555224 
看看我的回答 
 
Release版本单步跟踪方法:(可能对大家来说早就是小菜一碟了) 
选中Win32  Release然后 
Project-》setting-》C/C++  -》Category-》General 
                                                           -》Optimization-》Disable(Debug) 
                                                           -》Debug  Info-》Program  DataBase 
                                   -》Link---》Generate  Debug  Info打上钩 
 
另外请朋友们去捧捧这两个帖子。这个帖子我的回复花了我长时间。也许对那些 
Debug和release不太懂得人有帮助。虽然不是我的帖子,但是所涉及到的知识 
挺全的。 
 
http://expert.csdn.net/Expert/TopicView1.asp?id=2539864 
http://expert.csdn.net/Expert/topic/2553/2553540.xml?temp=.2079431 
本人关心技术。希望和有志之士(关心技术)成为朋友 
 
很同意woaini5994(孤独的猪)  的话 
这不算是作广告吧。希望版主不要删除 
 
 
--------------------------------------------------------------- 
 
遇到并解决的第一个release  mode  bug 
 
       “在  Class  Wizard  添加的响应函数中使用手动添加的参数将导致  Debug  模式运行正常,但Release  模式运行时非法操作。” 
 
       估计原因:MFC默认的  ON_CONTROL  消息响应函数原型为  (void)pfn(void),  因此在未改变MFC函数类型声明时,用额外的参数调用会导致Release  mode下,程序堆栈上的函数返回地址被作为函数参数来错误使用,而函数返回地址也就自然不对了,从而导致Access  Violation。 
 
 
 
       解决办法: 
 
       1.把消息响应函数声明对应的  AfxSig_vv,改为相应函数类型的  AfxSig_xx,然后用新的语句(原来的宏展开后把AfxSig_vv换成AfxSig_XX)替代ON_CONTROL等宏。 
 
       2.把消息响应函数的函数体移到一个自定义的一般类函数中,在类函数中使用参数,消息响应函数只对类函数进行调用。(此方法仅适用于在消息响应函数中添加默认参数的情况)


原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 高中分到平行班怎么办 夏季皮肤起皮屑怎么办 皮肤局部起皮屑怎么办 嘴角两边起皮屑怎么办 嘴角旁边起皮屑怎么办 春季脸上起皮怎么办 手上起白皮屑怎么办 手背起皮干燥怎么办 不会读的英语怎么办 中学生不准时回家怎么办 汽车轮胎锈住了怎么办 新轮胎不好装怎么办 学it找不到工作怎么办 血液里有微生物怎么办 机械硬盘不显示怎么办 六安建筑机械租赁公司怎么办 别墅楼梯太陡怎么办 不锈钢楼梯太陡怎么办 真皮白色鞋脏了怎么办 白色牛皮包脏了怎么办 白色羊皮鞋脏了怎么办 白色真皮染色了怎么办 白色漆皮鞋脏了怎么办 对鉴定结果不服怎么办 洗衣机入水口堵塞怎么办 华科办临时饭卡怎么办 血浆乳酸测定高怎么办 olay大红瓶过敏怎么办 饥一顿饱一顿怎么办 朝鲜人向去韩国怎么办? 我是超级大方脸怎么办 被精神病砍断四根筋怎么办 40小时不睡觉怎么办 边防转业军人安置不好怎么办 转业军人孩子读书怎么办玉环 军官证丢了怎么办 飞行学员停飞了怎么办 丹东港债券违约怎么办 对村党支部贿选怎么办 马自达2油耗高怎么办 马自达1.5油耗高怎么办