Debug ビルドで実行中に突然「Debug Assertion Failed!」というダイアログが表示されて、何をどう読めばいいか分からず閉じてしまった、という経験は珍しくありません。
この記事では、このダイアログに表示される情報の読み方と、ソースコード上の原因箇所を即座に特定する手順を整理しています。
最初に見る場所:ダイアログの 3 行
Assertion Failed ダイアログには、原因特定に必要な情報がすべて書かれています。まずこの 3 行を読みます。
| 項目 | 意味 | 見方 |
|---|---|---|
| File | ASSERT が書かれているソースファイルのフルパス | MFC 内部のファイルか、自分のコードかを確認する |
| Line | ASSERT マクロが置かれている行番号 | File と合わせてソースを開く手がかりになる |
| Expression | 失敗した条件式そのもの | 何が FALSE だったかがここに出る |
たとえば次のように表示されたとします。
Debug Assertion Failed!
File: f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\wincore.cpp
Line: 981
Expression: pMap != NULL
これは「wincore.cpp の 981 行目にある ASSERT(pMap != NULL) が失敗した」、つまり pMap が NULL だった、という意味です。
ダイアログのボタンの意味
ダイアログには 3 つのボタンがあります。押す順番を間違えると原因箇所を見失います。
| ボタン | 動作 | 推奨 |
|---|---|---|
| 中止 (Abort) | アプリを即座に終了する | 原因を調べたいなら押さない |
| 再試行 (Retry) | デバッガーで停止する(ブレーク) | 最初に押すべきボタン |
| 無視 (Ignore) | ASSERT を無視して続行する | 不安定な状態で続行するため非推奨 |
「再試行 (Retry)」を押してください。Visual Studio のデバッガーが ASSERT の行で停止し、コールスタックと変数の値をその場で確認できます。

File が MFC 内部のソースだった場合
ダイアログの File が wincore.cpp、viewcore.cpp、dlgcore.cpp のような MFC 内部のファイルを示していることがあります。これは MFC のバグではなく、自分のコードが MFC に不正な状態を渡した結果です。
この場合は、コールスタック(呼び出し履歴)を下に辿って、自分のコードが最初に現れる行を探してください。そこが実際の原因箇所です。
- 「再試行」でデバッガーに入る
- コールスタックウィンドウを開く(
デバッグ → ウィンドウ → 呼び出し履歴、またはCtrl + Alt + C) - スタックの上から下へ順に見て、自分のソースファイル名が出てくるフレームをダブルクリック
- その行の前後で、NULL になっている変数や不正な値を渡している箇所を確認する
コールスタックの読み方について詳しくは、No.25「Call Stack (呼び出し履歴) の歩き方」を参照してください。
よくある Assertion 失敗パターン
MFC の Debug ビルドでよく遭遇する Assertion のパターンを整理します。Expression の文字列で原因を絞り込めます。
① pWnd != NULL / pMap != NULL
ウィンドウハンドル(HWND)から MFC オブジェクトへの対応が見つからない状態です。
OnInitDialogより前にコントロールにアクセスしている- ダイアログが閉じたあとにメンバー変数のコントロールを操作している
GetDlgItemの戻り値を確認せず使っている
② afxCurrentInstanceHandle != NULL
MFC のモジュール状態が正しくセットされていません。DLL 内で MFC を使っている場合によく発生します。
- DLL のエクスポート関数の先頭に
AFX_MANAGE_STATE(AfxGetStaticModuleState())が抜けている
③ IsWindow(m_hWnd)
すでに破棄されたウィンドウのハンドルを使おうとしています。
- ダイアログを
DestroyWindowした後にメンバー関数を呼んでいる - タイマーコールバックが、ウィンドウ破棄後に走っている
④ nIndex >= 0 && nIndex < m_nSize
MFC のコレクションクラス(CArray / CObArray など)で、配列の範囲外にアクセスしています。
- ループの終了条件が
GetSize()を超えている - 要素を削除したあとにインデックスを更新していない
Release ビルドでは出ない理由
ASSERT マクロは Debug ビルドでのみ有効です。Release ビルドではプリプロセッサによって完全に消えます。
// MFC の ASSERT マクロ(簡略化)
#ifdef _DEBUG
#define ASSERT(f) \
do { if (!(f)) AfxAssertFailedLine(THIS_FILE, __LINE__); } while (0)
#else
#define ASSERT(f) ((void)0)
#endif
Release では ASSERT が消えるため、エラーダイアログは出ませんが、問題の根本原因が消えるわけではありません。NULL ポインタアクセスや配列の範囲外アクセスは、Release ビルドではクラッシュや未定義動作として現れます。
Debug で ASSERT が出たら、Release でも必ず起きている問題だと考えて、原因を修正してください。
出力ウィンドウの ASSERT メッセージ
ダイアログと同時に、Visual Studio の出力ウィンドウにもメッセージが出力されます。
Debug Assertion Failed!
File: wincore.cpp
Line: 981
For more information on how your program can cause an assertion
failure, see the Visual C++ documentation on asserts.

ダイアログを誤って「中止」で閉じてしまった場合でも、出力ウィンドウにファイル名と行番号が残っています。ここから該当ソースを特定できます。
ASSERT を自分のコードに仕込む
MFC が内部で使っているのと同じ ASSERT マクロは、自分のコードでも使えます。「ここに NULL が来たらおかしい」「この値は正の整数のはず」という前提条件をコードに埋め込んでおくと、不正な状態を早期に発見できます。
void CMyDialog::SetItemCount(int nCount)
{
// nCount が負ならバグ。Debug で即停止させる
ASSERT(nCount >= 0);
m_nItemCount = nCount;
UpdateList();
}
ASSERT の詳しい活用法については、No.23「ASSERT(条件) でバグを早期発見する」を参照してください。
まとめ
- [ ] ダイアログの File / Line / Expression を必ず読む。特に Expression が原因のヒント
- [ ] 「再試行 (Retry)」を押してデバッガーに入り、コールスタックから自分のコードを探す
- [ ] File が MFC 内部のソースなら、コールスタックを下に辿って自分の呼び出し元を特定する
- [ ] Release ビルドでは ASSERT は消えるが、問題自体は消えない。Debug で出たら必ず修正する
