C++异常分析排查软件启动时访问了0xcdcdcdcd内存地址导致内存访问违例的崩溃
Posted dvlinker
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++异常分析排查软件启动时访问了0xcdcdcdcd内存地址导致内存访问违例的崩溃相关的知识,希望对你有一定的参考价值。
最近在使用duilib开源库实现图片查看工具软件ImageViewer,调试时发现,程序刚启动时就访问了0xcdcdcdcd内存地址,触发内存访问违例,导致了软件崩溃。本文分享一下这一问题的排查过程。
1、问题描述
使用Visual Studio对ImageViewer程序进行Debug下的调试,结果还没弹出程序的主窗口,就报出了内存访问违例的提示,提示框如下:
提示在访问0xcdcdcdcd内存时产生了内存访问违例。以前我们多次讲过0xcccccccc、0xcdcdcdcd和0xfeeefeee这几个常见的特殊值,如下所示:
即本例中0xcdcdcdcd含义是:程序在Debug下运行时,Debug版本微软C++运行时库会将未初始化的堆内存中的内容都初始化为0xcdcdcdcd。所以在Debug下调试代码遇到这样的值,可能是因为申请的堆内存没有初始化引起的。
点击提示框中继续按钮,代码会中断在如下的代码处:
即异常发生在在m_pCaption->GetPos这句代码上。此时,查看了一下指针变量m_pCaption内存中的值,就是0xcdcdcdcd,可能是m_pCaption等控件的指针变量没有初始化引起的。此时查看函数的调用堆栈:
函数是执行到CImageViewerWnd::HandMessage函数中触发上述调函数调用的,具体是在处理uMsg==WM_SIZE && wParam==SIZE_RESTORED分支代码中,如下:
CImageViewerWnd就是ImageViewer程序的主窗口类。除了m_pCaption控件变量的值为0xcdcdcdcd,其他标题栏中的控件指针变量m_pBtnMax的值也是0xcdcdcdcd。接下来我们以m_pBtnMax值为0xcdcdcdcd为线索继续分析。
2、WM_SIZE(wParam:SIZE_RESTORED)消息与窗口Init接口调用的时序问题
m_pCaption和m_pBtnMax等控件指针变量都是在CImageViewerWnd::Init函数中初始化获取到值的,如下所示:
而上面异常发生在运行到uMsg==WM_SIZE && wParam==SIZE_RESTORED代码分支时,运行到该处代码,这些控件指针变量是没有初始化的,所以可以判定,在执行到此处的代码之前还没执行到CImageViewerWnd::Init函数。
那CImageViewerWnd::Init函数是在什么被调用的呢?这个要看duilib开源库中框架的代码,框架中会调用系统API函数CreateWindow(Ex)去创建CImageViewerWnd窗口,API函数CreateWindow(Ex)会产生WM_CREATE消息,dui框架在收到WM_CREATE消息时就会去自动调用CImageViewerWnd::Init函数。
所以,这说明什么呢?说明WM_SIZE消息会先于WM_CREATE消息产生,即初始化控件指针变量的函数CImageViewerWnd::Init还没执行时,就先收到了WM_SIZE消息,进入了WM_SIZE消息处理分支,就访问了未初始化的指针变量。
这显然和我们原先的认知是不一样的,我们原先一度认为,创建窗口时产生的消息WM_CREATE会先产生了,等窗口创建出来后,才会收到WM_SIZE消息。此案例让我们认识到,窗口的WM_SZIE会先于WM_CREATE消息产生。
3、解决办法
我们可以在CImageViewerWnd::HandMessage函数处理WM_SZIE消息的分支中添加处理,先判断m_pBtnMax指针是否为空?如下所示:
这样显然是不行的,本例中正是访问到了未初始化的m_pCaption控件指针变量(变量值是运行时库在初始化堆内存时设置的默认值0xcdcdcdcd),0xcdcdcdcd也是不等于NULL的,所以仅仅添加控件指针变量是否为空的判断是不够的。
其实我们以前说过,我们在定义好变量之后,应该在最开始的时候就对变量进行初始化,即在C++类的构造函数中进行初始化的,即将所有控件指针变量都初始化为NULL,如下所示:
这样我们在CImageViewerWnd::HandMessage函数处理WM_SZIE消息的分支中,判断控件指针变量是否为NULL,就没问题了,就不会再有崩溃了。
最后,我们要强调一下,无论是函数中的局部变量,还是C++类中成员变量,都要进行初始化,要养成变量初始化的习惯。
以上是关于C++异常分析排查软件启动时访问了0xcdcdcdcd内存地址导致内存访问违例的崩溃的主要内容,如果未能解决你的问题,请参考以下文章
C++异常分析使用windbg分析dump文件,排查模态框返回时的崩溃问题