トップ回答者
ハンドルされていない例外が発生しました

質問
-
いつもおせわになっています。
Visual C++ MFCでプログラムを作成しているのですが、OnPaint内でピクチャーボックスのビットマップを更新する時に「MCSP.exe の 0x004033ca でハンドルされていない例外が発生しました: 0xC0000005: 場所 0x00000004 を読み込み中にアクセス違反が発生しました。」というメッセージがでて、処理が中断します。このエラーを回避する方法となぜエラーが発生するのかわかりません。どなたかお教え下さい。
ソースは以下のように記述しています。
pDC = m_xcPicLamp[i].GetDC(); // デバイスコンテキスト 取得
myDC.CreateCompatibleDC(pDC); // pDCと互換のあるmyDC生成
oldBMP = myDC.SelectObject(&myBMP); // 画像選択
pDC->BitBlt(0, 0, 16, 16, &myDC, 0, 0, SRCCOPY); // 画像転送 ←この行で発生しています。
myDC.SelectObject(oldBMP); // 元のビットマップに戻す
回答
-
例外の発生するのはどの処理をしたタイミングですか?
CResourceException は、文字通りリソース(システムリソースではなく、.rcに含まれるような情報)にアクセスできない場合や、CGdiObject の派生クラスのハンドルがNULLだった時などに発生します。
デバッガなどでどこで例外が発生しているか?を確実に抑え、その原因となるものを究明してください。
このソースだけをみる限り、可能性としtえ問題がありそうなのは。。。
myBMP くらいですが、もしかしたら m_xcPicLamp[i].GetDC() のなかかもしれませんし。。。
あと、MFCの例外は、必ずTRYマクロを使ってトラップしてください。状況によって ex->Delete() はしない場合とする場合があります。
わんくま同盟,Microsoft MVP for Visual C++(Oct 2005-) http://blogs.wankuma.com/tocchann/- 回答としてマーク 高橋 春樹 2010年3月10日 8:17
-
myDCを生成後に破棄するように変更し、IF文はコメントとして実行しましたが、
この場合、IF文というのはpDCの判定文だと思うのですけれど、取得エラーの可能性があるのであればこの文は無いとだめだと思います。
pDC->BitBlt(0, 0, 16, 16, &myDC, 0, 0, SRCCOPY);
の部分でエラーが発生します。
この時、m_picLamp[i]には値が入っています。pDCの値は見ることができませんでした。
既にどこかで取得済みの値を保持していてそれがNULLでない事が検査済みで保持されていればなくても良いかもしれませんが、GetDC関数に失敗の可能性があるなら判定はすべきです。m_picLampが何者なのか良く分からないのでCWnd::GetDCの事を指しているのか、オリジナルの関数なのかもわかりませんし。
解決した時は、参考になったレスポンスの所にある[回答としてマーク]ボタンをクリックしてスレッドを締めましょう。- 回答としてマーク 高橋 春樹 2010年3月10日 8:18
-
yominetさん、ありがとうございます。
推測ですが、崩れ方や、1400回未満までは起こらないことから、
1.発生頻度
発生頻度は、約1400回以上呼ばれたら発生します(30秒間隔で発生)。
2.崩れるとは
崩れるとは、デザイナで配置したコントロールの位置がバラバラになり、かつ画面全体が灰色(ダイアログ
の背景色)になります。この時、ダイアログの枠も消えています。
3.myDCは、生成だけで破棄していませんでした。
メモリやらGDIオブジェクトやらが解放されずにどんどん溜まり、限界がきたところでおかしくなってると思います。
このような場合、エラーとして止まった場所・報告がなされた場所に問題があるのではなく
別の場所に、本当の問題がある場合が多いです。
♯1400回呼ばれるのがOnPaintのみであるなら、OnPaint内にあると思いますが…
ソースからはmyDCのみしか気になりませんでしたが
myBMPもふくめ、生成したものをきちんと解放・破棄しているかどうか確認しなおすべきだと思います。
デバッグモードで1400回未満のエラーが出ない段階で終了させてみて、リークがないか確認したり
タスクマネージャで、メモリ使用量、仮想メモリサイズ、また、GDIオブジェクト数やハンドル数を確認すれば
解放し忘れなどがあるかわかります。- 回答としてマーク 高橋 春樹 2010年3月10日 8:17
-
すんません。すごく大事な部分を見落としていました。
OnPaint で処理してるってことは、最終的な画面出力先は、CPaintDC を指定すると思うのですが、そうはなってませんよね?
あと、現状の最新状態がよくわからんのですが...
GetDC で取り出してきたDCは、ReleaseDC がいるような気がします。
配列だったやつは今は配列じゃない?という感じで状況がよくわかりません。
ずるずるとやっていても、B_Wolf さんはちっとも解決できないままなので
ごく一般的なリソースリークを発生しないようにするための指針を挙げておきます。
1.CWnd::GetDC で取得したDCは、そのイベントハンドラから戻る前に CWnd::ReleaseDC を呼び出して解放する。
2.CDC::CreateDC または、CDC::CreateCompatibleDC で作成したDCは、不要になったら DeleteDC をする(デストラクタ呼び出しでもよい)
3.すでに有効なHDCを保持しているCDCで作成系メンバー関数を呼び出してはならない。
4.SelectObject して選択したものは、そのDCを解放(削除)する前にもともと選択されていたオブジェクトが選択されるようにする。
5.SelectObject された状態の CGdiObject(の派生クラスオブジェクト) は、選択を解除してから削除する。
6.CWnd::OnPaint() のハンドラでは、CPaintDC dc( this ); を行い、再描画用DCを構築する。
#7個目が思い浮かばなかった...orz
わんくま同盟,Microsoft MVP for Visual C++(Oct 2005-) http://blogs.wankuma.com/tocchann/ -
あれ?現行のソースは最初のソースとそれほど変わらないんですよね?
であれば、myBMP を SelectObject していると思うのですが?
6 の CPaintDC の利用は、OnPaint がWM_PAINTのイベントハンドラであれば、最初のスケルトンコードに含まれていたはずです。
そうではないのであれば(名前が OnPaint というだけでイベントハンドラではない)、当てはまらないので関係ありません。
先にあげたものはごくごく一般的に発生しやすいリソースリークの事例を羅列しただけですので。
わんくま同盟,Microsoft MVP for Visual C++(Oct 2005-) http://blogs.wankuma.com/tocchann/- 回答としてマーク 高橋 春樹 2010年3月10日 7:35
すべての返信
-
とっちゃんさん、ありがとうございます。
try
{
pDC = m_xcPicLamp[i].GetDC(); // デバイスコンテキスト 取得
if ( pDC !=NULL)
{
myDC.CreateCompatibleDC(pDC); // pDCと互換のあるmyDC生成
oldBMP = myDC.SelectObject(&myBMP); // 画像選択
pDC->BitBlt(0, 0, 16, 16, &myDC, 0, 0, SRCCOPY); // 画像転送
myDC.SelectObject(oldBMP);
}
}
catch (CException* ex)
{
GetLocalTime(&datNow); // 現在日時 取得
strTemp = _T("画像転送に失敗しました。"); // エラーメッセージ 取得
LogListInsertItem(datNow, _T(""), _T(""), strTemp); // ログリストに追加
ex->Delete();
}
上記のように、変更したのですが、今度は「Microsoft C++ の例外: CResourceException (メモリの場所 0x0012c9d0)」というエラーが発生して、画面が崩れてしまうようになりました。
回避策は上記変更ではできないのでしょうか。 -
例外の発生するのはどの処理をしたタイミングですか?
CResourceException は、文字通りリソース(システムリソースではなく、.rcに含まれるような情報)にアクセスできない場合や、CGdiObject の派生クラスのハンドルがNULLだった時などに発生します。
デバッガなどでどこで例外が発生しているか?を確実に抑え、その原因となるものを究明してください。
このソースだけをみる限り、可能性としtえ問題がありそうなのは。。。
myBMP くらいですが、もしかしたら m_xcPicLamp[i].GetDC() のなかかもしれませんし。。。
あと、MFCの例外は、必ずTRYマクロを使ってトラップしてください。状況によって ex->Delete() はしない場合とする場合があります。
わんくま同盟,Microsoft MVP for Visual C++(Oct 2005-) http://blogs.wankuma.com/tocchann/- 回答としてマーク 高橋 春樹 2010年3月10日 8:17
-
myDCを生成後に破棄するように変更し、IF文はコメントとして実行しましたが、
この場合、IF文というのはpDCの判定文だと思うのですけれど、取得エラーの可能性があるのであればこの文は無いとだめだと思います。
pDC->BitBlt(0, 0, 16, 16, &myDC, 0, 0, SRCCOPY);
の部分でエラーが発生します。
この時、m_picLamp[i]には値が入っています。pDCの値は見ることができませんでした。
既にどこかで取得済みの値を保持していてそれがNULLでない事が検査済みで保持されていればなくても良いかもしれませんが、GetDC関数に失敗の可能性があるなら判定はすべきです。m_picLampが何者なのか良く分からないのでCWnd::GetDCの事を指しているのか、オリジナルの関数なのかもわかりませんし。
解決した時は、参考になったレスポンスの所にある[回答としてマーク]ボタンをクリックしてスレッドを締めましょう。- 回答としてマーク 高橋 春樹 2010年3月10日 8:18
-
yominetさん、ありがとうございます。
推測ですが、崩れ方や、1400回未満までは起こらないことから、
1.発生頻度
発生頻度は、約1400回以上呼ばれたら発生します(30秒間隔で発生)。
2.崩れるとは
崩れるとは、デザイナで配置したコントロールの位置がバラバラになり、かつ画面全体が灰色(ダイアログ
の背景色)になります。この時、ダイアログの枠も消えています。
3.myDCは、生成だけで破棄していませんでした。
メモリやらGDIオブジェクトやらが解放されずにどんどん溜まり、限界がきたところでおかしくなってると思います。
このような場合、エラーとして止まった場所・報告がなされた場所に問題があるのではなく
別の場所に、本当の問題がある場合が多いです。
♯1400回呼ばれるのがOnPaintのみであるなら、OnPaint内にあると思いますが…
ソースからはmyDCのみしか気になりませんでしたが
myBMPもふくめ、生成したものをきちんと解放・破棄しているかどうか確認しなおすべきだと思います。
デバッグモードで1400回未満のエラーが出ない段階で終了させてみて、リークがないか確認したり
タスクマネージャで、メモリ使用量、仮想メモリサイズ、また、GDIオブジェクト数やハンドル数を確認すれば
解放し忘れなどがあるかわかります。- 回答としてマーク 高橋 春樹 2010年3月10日 8:17
-
yominetさん
OnPaint内で生成したものは、全て解放・破棄するように変更しました。また、「if (pDC != NULL)」文も追加しました。
この状態でプログラムを稼動すると、一応エラーは発生しないのですが、スクリーンセーバからの復帰時等で同様のエラーがCMCSPApp::InitInstance()内の最終行「// ダイアログは閉じられました。アプリケーションのメッセージ ポンプを開始しないで
// アプリケーションを終了するために FALSE を返してください。
return FALSE;」で発生します。
また、何もしない状態で1400回を越えたあたりで終了ボタンをクリックしたときも同様のことが発生します。
何が悪いのかわかりません。ご教授下さい。
ハンドルは増減はしますが、増えているようには見えません。メモリまでは確認できていません。 -
すんません。すごく大事な部分を見落としていました。
OnPaint で処理してるってことは、最終的な画面出力先は、CPaintDC を指定すると思うのですが、そうはなってませんよね?
あと、現状の最新状態がよくわからんのですが...
GetDC で取り出してきたDCは、ReleaseDC がいるような気がします。
配列だったやつは今は配列じゃない?という感じで状況がよくわかりません。
ずるずるとやっていても、B_Wolf さんはちっとも解決できないままなので
ごく一般的なリソースリークを発生しないようにするための指針を挙げておきます。
1.CWnd::GetDC で取得したDCは、そのイベントハンドラから戻る前に CWnd::ReleaseDC を呼び出して解放する。
2.CDC::CreateDC または、CDC::CreateCompatibleDC で作成したDCは、不要になったら DeleteDC をする(デストラクタ呼び出しでもよい)
3.すでに有効なHDCを保持しているCDCで作成系メンバー関数を呼び出してはならない。
4.SelectObject して選択したものは、そのDCを解放(削除)する前にもともと選択されていたオブジェクトが選択されるようにする。
5.SelectObject された状態の CGdiObject(の派生クラスオブジェクト) は、選択を解除してから削除する。
6.CWnd::OnPaint() のハンドラでは、CPaintDC dc( this ); を行い、再描画用DCを構築する。
#7個目が思い浮かばなかった...orz
わんくま同盟,Microsoft MVP for Visual C++(Oct 2005-) http://blogs.wankuma.com/tocchann/ -
あれ?現行のソースは最初のソースとそれほど変わらないんですよね?
であれば、myBMP を SelectObject していると思うのですが?
6 の CPaintDC の利用は、OnPaint がWM_PAINTのイベントハンドラであれば、最初のスケルトンコードに含まれていたはずです。
そうではないのであれば(名前が OnPaint というだけでイベントハンドラではない)、当てはまらないので関係ありません。
先にあげたものはごくごく一般的に発生しやすいリソースリークの事例を羅列しただけですので。
わんくま同盟,Microsoft MVP for Visual C++(Oct 2005-) http://blogs.wankuma.com/tocchann/- 回答としてマーク 高橋 春樹 2010年3月10日 7:35