一次スタックオーバーフローの解析
3803 ワード
プロジェクト開発の過程で、プログラムのクラッシュを引き起こす問題をよく調べます.この間、テストグループは一つの現象をフィードバックし、ある機能をコピーする過程で、約30分後にプログラムがクラッシュし、数回の繰り返しテストを経て、以上の現象が発生した.クラッシュログの初歩的な分析を経て、私は発見して、毎回クラッシュする地方は一致していますが、ソースコードを読むことを通じて、私はそれらの地方が一般的に問題が発生しないことを発見して、まさかコードを読むのがあまり注意深くなくて、私はよくクラッシュが発生するコンテキストを分析して、すべてとても正常な呼び出し状況です.まさかまた他のモジュールの干渉が現れて、主なスレッドのメモリが錯乱して、いつも崩壊を探している人はすべて知っていて、もしこのような情況が現れたら、それは天に登るのと同じで、豊富な位置の崩壊の経験がありますが、しかしこのような問題に出会うたびに、あなたの経験は往々にして少しの補助作用しかできません.
私が困っている間に、テストスタッフはコピーをしながら、「この窓はどうして私が位置を移動したばかりなのに、今また元の位置に戻ったの?」と言った.山が貧しくて道がないと疑うたびに、どんな細部も突破の鍵になるかもしれない.私は「新しい弾の窓が元の窓を置いたのかもしれない」と言った.テスト担当者は「このようなインタラクションは友好的ではないようだ」と答えた.この時私が関心を持っていたのはインタラクティブな問題ではなく、この崩壊がどのように解けたのかで、勝手に承諾して、「コードを見に行きます」と言った.
ソースコードを開くと、簡単なコードが目の前に表示されます.
この関数はポップアップボックスの開始点であり、コピーのタイミングにメッセージが生成されます.つまり、この関数はタイミング的に呼び出されます.この関数の大まかな意味は次のとおりです.
1:ポップアップボックスがあり、ポップアップボックスが表示されている場合は、WM_を送信します.CLOSEメッセージ、オフにします.
2:新しいポップアップボックスを作成し、ShowModal表示を呼び出します.
简単で间违いのないロジックのように见えて、私の警戒を引き起こして、私は意识してShowModalのソースコードを开いて、ShowModalがこのバージョンで无数の问题を生んだため、その原因を正して、DUIのShowModalもメッセージのループを引き継いで、しかも简単なロジックの処理をして、コードは以下の通りです:
コードから分かるように、メッセージループはウィンドウかどうかを判断し、メッセージを取り出して処理し、WM_を受信するとCLOSEメッセージ後、WM_CLOSEメッセージでは、通常、ウィンドウを閉じてIsWindow判定に失敗し、メッセージループを終了します.非常に正常な論理でもある.
しかし、上段のコードの呼び出しと組み合わせると、問題があることがわかります.
いくつか明確な点があります.
1.XXX::YYYが呼び出されたときは、必ずメッセージサイクルにあります.
2.SendMessageがWM_に転送されたときCLOSEの場合、ウィンドウプロシージャを直接呼び出し、WM_を処理します.CLOSEブランチの業務は、フォームを破棄するのが一般的で、この時もXXX::YYYが呼び出されたときのメッセージループにあります.
3.しかし、SendMessageは戻り、ShowModalを呼び出すと、前の2つのステップの同じメッセージループの中で、メッセージループが出ていません.
問題はこのように発生し、ShowModelはメッセージループを作成し、ShowModelは返さず、スタックは下に上昇し続け、XXX::YYY関連のメッセージが再生成されると、前のShowModelで生成されたメッセージループでXXX::YYYを処理し、その後、SendMessage and ShowModalの再作成を継続し、週を重ねて、メッセージループを終了したことがなく、もちろん関数を返したことがありません.関数は呼び出されるだけで返されず、もちろんスタックは下に上昇し、最後にスタックがオーバーフローすることを知っています.
思い出してみると、テスト記述の問題は、一定時間間隔でプログラムが必ず崩壊することであり、これも崩壊シーンに合致している.コピー機は固定時間でこのメッセージを生成し、呼び出し環境が同じであるため、スタックの成長速度は必然的に同じであり、スタックの大きさは固定され、固定時間間隔でスタックがオーバーフローすることになる.これで、この崩壊が位置決めされ、解決方法は簡単になる.私たちは同僚の非常に良い提案を採用しました.このようなユーザーが確定する通知型メッセージは必要ありません.モードボックスを使わずに完成したので、Popボックスを直接使って通知しました.最後にその同僚(weilaitao)がコードの修正を行いました.
スタックオーバーフローの問題は非常に頭が痛い場合があるが,このような特殊な業務による間接的なスタックオーバーフローも珍しく,調べて大切にする.
私が困っている間に、テストスタッフはコピーをしながら、「この窓はどうして私が位置を移動したばかりなのに、今また元の位置に戻ったの?」と言った.山が貧しくて道がないと疑うたびに、どんな細部も突破の鍵になるかもしれない.私は「新しい弾の窓が元の窓を置いたのかもしれない」と言った.テスト担当者は「このようなインタラクションは友好的ではないようだ」と答えた.この時私が関心を持っていたのはインタラクティブな問題ではなく、この崩壊がどのように解けたのかで、勝手に承諾して、「コードを見に行きます」と言った.
ソースコードを開くと、簡単なコードが目の前に表示されます.
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を呼び出すと、前の2つのステップの同じメッセージループの中で、メッセージループが出ていません.
問題はこのように発生し、ShowModelはメッセージループを作成し、ShowModelは返さず、スタックは下に上昇し続け、XXX::YYY関連のメッセージが再生成されると、前のShowModelで生成されたメッセージループでXXX::YYYを処理し、その後、SendMessage and ShowModalの再作成を継続し、週を重ねて、メッセージループを終了したことがなく、もちろん関数を返したことがありません.関数は呼び出されるだけで返されず、もちろんスタックは下に上昇し、最後にスタックがオーバーフローすることを知っています.
思い出してみると、テスト記述の問題は、一定時間間隔でプログラムが必ず崩壊することであり、これも崩壊シーンに合致している.コピー機は固定時間でこのメッセージを生成し、呼び出し環境が同じであるため、スタックの成長速度は必然的に同じであり、スタックの大きさは固定され、固定時間間隔でスタックがオーバーフローすることになる.これで、この崩壊が位置決めされ、解決方法は簡単になる.私たちは同僚の非常に良い提案を採用しました.このようなユーザーが確定する通知型メッセージは必要ありません.モードボックスを使わずに完成したので、Popボックスを直接使って通知しました.最後にその同僚(weilaitao)がコードの修正を行いました.
スタックオーバーフローの問題は非常に頭が痛い場合があるが,このような特殊な業務による間接的なスタックオーバーフローも珍しく,調べて大切にする.