堆栈溢出分析

        在项目开发过程,我会经常检查的一些问题,导致程序崩溃。只是前一段时间,测试组的反馈现象,当处理用于寻呼机的特定功能,经过约半小时进行,该计划将导致崩溃,反复试验几次后,均高于现象。对崩溃日志的初步分析。我发现。尽管每次崩溃的地方一致。但通过阅读源代码,我发现那些地方通常是不会出现故障的,难道读代码不够细致,我仔细致细地分析了一遍发生崩溃的上下文,都是很正常的调用情况。难道又出现了其他模块的干扰,造成主线程内存错乱了,常常查找崩溃的人都知道。假设出现了这种情况,那查找起来无异于登天,尽管有丰富的定位崩溃的经验,但每次遇到这种问题,你的经验往往仅仅能起到一丁点的辅助作用。

        就在我一筹莫展的时候。測试人员边进行拷机,边随口说了一声,“这个窗体怎么我刚移动了位置,如今又回到了原来位置?”。每当山穷水复疑无路的时候。不论什么一丝的细节都可能成为突破的关键,我说。“可能新弹的窗体又把原来的窗体置位了吧。”。測试人员便应声到,“这样交互好像不是非常友好吧!”,而此时我关心的并非交互的问题,而是这个崩溃何解。便任意答应了一下,说“那我去看一下代码。”

        打开源代码。简单几句代码出如今了眼前:

bool XXX::YYY( WPARAM wParam, LPARAM lParam, bool& bHandled )
{
	HWND hWnd = CConfCtrlLogic::Instance()->GetApplyChimeRspWnd();
	if ( ::IsWindow( hWnd ) && ::IsWindowVisible( hWnd ) )
	{
		SendMessage( hWnd, WM_CLOSE, 0, 0 );	
	}

	CStdString strInfo = wParam ? STRING_JOIN_DISCUSS_SUCC : STRING_JOIN_DISCUSS_FAIL;
	CMessageBoxDlg dlg( IDR_XML_MSG_NOTIFY_DLG, 255, false );
	dlg.EnbaleAutoClose( FALSE );
	dlg.SetInfo( strInfo, STRING_TIP, g_pMainLogic->GetMainHwnd(), ID_OK );
	dlg.Create( g_pMainLogic->GetMainHwnd(), STRING_TIP, UI_WNDSTYLE_BOX, WS_EX_TOOLWINDOW );
	CConfCtrlLogic::Instance()->SetApplyChimeRspWnd( dlg.GetHWND() );
	dlg.CenterWindow();
	dlg.ShowModal();
	bHandled = true;
	return true;
}

        这个函数便是弹出框起始的地方,拷机会定时有消息产生。也就是这个函数会被定时调用。该函数的大致意思是:

                1:假设有弹出框而且弹出框是显示的。就发个 WM_CLOSE 消息,关掉它。

                2:新建一个弹出框,并调用ShowModal 显示。

        看似简单而且无差错的逻辑,却引起了我的警觉,我下意识地点开了 ShowModal 的源代码,由于 ShowModal 在这个版本号产生了无数个问题,纠其原因。就是 DUI 的 ShowModal 也接管了消息循环。而且做了简单的逻辑处理,代码例如以下:

UINT CWindowWnd::ShowModal()
{
	ASSERT( ::IsWindow(m_hWnd) );
	UINT nRet = 0;
	HWND hWndParent = GetWindowOwner( m_hWnd );
	::ShowWindow( m_hWnd, SW_SHOWNORMAL );
	::EnableWindow( hWndParent, FALSE );
	MSG msg = { 0 };
	while( ::IsWindow(m_hWnd) && ::GetMessage(&msg, NULL, 0, 0) ) 
	{
		if( WM_CLOSE == msg.message && msg.hwnd == m_hWnd ) 
		{
			nRet = msg.wParam;
			::EnableWindow( hWndParent, TRUE );
			::SetFocus( hWndParent );
		}

		if( !CPaintManagerUI::TranslateMessage(&msg) ) 
		{
			::TranslateMessage( &msg );
			::DispatchMessage( &msg );
		}

		if( WM_QUIT == msg.message ) 
		{
			break;
		}
	}
	::EnableWindow( hWndParent, TRUE );
	::SetFocus( hWndParent );
	if( WM_QUIT == msg.message ) 
	{
		::PostQuitMessage( msg.wParam );
	}

	return nRet;
}

        通过代码能够看到,消息循环会推断是不是窗体,而且取出一个消息,然后处理掉,当接收到 WM_CLOSE 消息后,在 WM_CLOSE 消息中我们的通常处理都会是关掉窗体,然后导致 IsWindow 判定失败。退出消息循环。也算是很正常的逻辑。

        但结合上段代码的调用就会发现。这当中是有问题的。

        有几点要明白:

                1.  XXX::YYY 被调用的时候,肯定是在一个消息循环里;

                2.  当 SendMessage 传入 WM_CLOSE 的时候,会直接去调用窗口过程,并处理WM_CLOSE分支的业务,通常是销毁窗口,此时也还在XXX::YYY 被调用时的消息循环里;

                3.  但 SendMessage 返回。接着调用 ShowModal 时,依旧是在前两步同样的消息循环里,一直没有出消息循环。

        问题就这样产生了。ShowModal 会创建一个消息循环。且 ShowModal 不返回。堆栈继续向下涨,当 XXX::YYY 相关的消息再产生的时候,就会在上一个 ShowModal 产生的消息循环里处理 XXX::YYY ,然后继续 SendMessage and ShowModal 再创建,周而复始。却一直没有退出过一个消息循环,当然也就没有返回过函数。我们知道,函数仅仅调用不返回,当然堆栈会一直向下涨,最后造成堆栈溢出。

        回顾一下。測试描写叙述的问题是,隔一定时间,程序必崩。这也符合了崩溃场景,由于拷机是以固定时间来产生这个消息。而调用环境同样,那么栈增长速度必定同样,栈大小固定,那么隔固定时间后,也必定会引起栈溢出,至此,这个崩溃被定位,那解决方法就非常easy了。我们採用了一位同事的非常好的建议。像这样的不须要用户确定的通知型消息,根本不用模态框来完毕,所以直接使用了 Pop 框通知一下。最后也由那位同事(weilaitao)行了代码的改动。

        堆栈溢出的问题有时是很头疼的。而像这样的由特殊业堆栈溢出的服务间接原因是罕见,并检查珍惜。



版权声明:本文博客原创文章,博客,未经同意,不得转载。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值