いつもお世話になります。
ebimayoです。
以前お聞きした内容で作成していた箇所でエラーが発生したので、
その原因が分かる方がいれば教えていただきたく投稿しました。
参考までに以前聞いた内容は以下の通りです。
http://rararahp.cool.ne.jp/cgi-bin/lng/vc/vclng.cgi?print+200603/06030042.txt
// 上記で聞いた内容を元に現在は以下のようなコードを書いています。
void CButtonTest::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
/* ここに兄弟ウィンドウを上書きしない処理AAAを入れています */
AAA();
CBitmapButton::DrawItem(lpDrawItemStruct);
CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
pDC->SetBkMode(TRANSPARENT);
pDC->DrawText( _T(表示したい文字),-1, &lpDrawItemStruct->rcItem,
DT_SINGLELINE|DT_CENTER );
}
このコードの「CBitmapButton::DrawItem(lpDrawItemStruct);」の内部の
CreateCompatibleDCで失敗しているようです。
失敗していると思った理由は、そのすぐ後に実行しているSelectObjectで
アサートのダイアログが表示され原因が「m_hDC」がNULLのためでした。
このエラーは常に起こるものではなく、自分の環境では
発生しておらず再現性や原因についてよく分かっていませんが、
このエラーが起きたときの一つ聞いている操作としては、問題になっている
CButtonTestクラス(これはビットマップボタンを表示するための拡張クラスで
自作したものです)のコントロールを画面上に表示している状態で
長時間(3,4時間)放置したあとスクリーンセーバーが起動していたので
それを解除したらこのエラーが発生していたようです。
このDrawItemが呼ばれた原因としては、lpDrawItemStructのitemActionに1
itemStateに0がセットされていたことからスクリーンセーバーを解除したときに
描画処理が呼ばれそこで出たようです。
同様に自分の開発環境にて同じ画面(ビットマップボタン)が表示されている
状態で放置しスクリーンセーバーが起動したあとにマウスを動かして同じ現象が
出るか確かめましたが、同じ現象が発生せず何事も無く処理が完了しました。
CreateCompatibleDCが失敗する要因として自分が考えられるのは、マルチスレッド
等で同様のDCに対して処理しようとした場合しか思いつきませんが、
このCButtonTestクラスに対する処理をマルチスレッドで行っておらず、
エラー原因の手がかりさえない状態です。
何か分かる方がいましたら、宜しくお願いします。
開発環境
Microsoft WindowsXP(SP2)
Microsoft Visual C++ 2005
ダイアログベースアプリケーション(MFC)
どこかでリソースリークしていて、
長時間稼動する中で、たまたまその部分でリソースが足りなくなった
と言うことは考えられませんか。
dairygoodsさん ありがとうございます
そのような気もしますが、複数人で作成しているもので
中々原因を突き止められずにいます。自分が関わったところで
異常が出ていたため、まず自分の処理を疑っているところです。
ソース全て合わせると数万~10数万行くらいのプログラムで
自分が作成していない箇所のほうが多いこともあり、
何かヒントもらえればと思ったのですが、リークの可能性となると
見つけるのが難しそうですね。。。
もうしばらく処理を追って見ます。
以下追記:
デバッグビルドしたEXEで実行していてエラーが出ていたため
デバッガを起動したままにしているのですが、エラーが出てから
ずっとCPU使用率が96%~100%というのが気になっています。
タスクマネージャで見たところ、イメージ名:System ユーザー名:SYSTEM
がCPU使用率96%~100%常に使用中になっているのですが、
デバッガや他のソフト(メモ帳など)は特に遅延なく動いているのも
気になるところです。
> 何かヒントもらえればと思ったのですが、リークの可能性となると
> 見つけるのが難しそうですね。。。
BoundsCheckerとかPurifyとか、チェックツールの購入を検討されてみては。
# ツールに書けると、気付いてなかった別の問題もぽろぽろ見つかる気がします。
> # ツールに書けると、気付いてなかった別の問題もぽろぽろ見つかる気がします。
# ツールにかけると、気付いてなかった別の問題もぽろぽろ見つかる気がします。
未だに原因不明ですが、調べて分かったことを書きます。
エラーは画像描画処理を何千か何万回繰り返したときに発生して
再現性がありました。
上記DrawItemと別の箇所で既にエラーが発生していました。
そのエラーが出ていた箇所で処理していたことは、
1. LOGPALETTEの領域確保&データ作成
2. CreatePaletteで論理カラーパレットを作成
3. SelectPaletteで論理パレットを選択
4. RealizePaletteで論理パレットからシステムパレットへマップ
この処理でCreatePaletteで作成したパレットをDeleteObjectして
いなかったので、それが原因かと思いDeleteObject処理をいれたのですが
やはり描画処理を繰り返し行うと同様のエラーが発生しました。
そこで、SelectPaletteの戻り値をDeleteObjectする前にSelectPaletteで
戻してあげる処理を追加したところ描画処理を繰り返し行うことによる
エラーが発生しなくなりました。
SelectPaletteのヘルプを見ると、SelectObjectのように元に戻す必要がある
とは書いてなく、また、他サイトでSelectPaletteを使っている方の処理を
見ても上記のように戻り値をとっておいて戻す処理を書いていないため
本当にこの処理を追加してよかったのかどうかが不明です。
そこで、質問がひとつ増えるのですがSelectPaletteはSelectObjectのように
戻り値(デバイスコンテキストの以前の論理パレットのハンドル)を戻す
必要があるのでしょうか?
論理パレットのヘルプを見ると、
> When an application no longer needs a logical palette,
> it can delete it by using the DeleteObject function.
> The application must make sure the logical palette is
> no longer selected into a device context before deleting
> the palette.
とあるので、削除するまえに選択状態を解除する必要があります。
Kerryさんありがとうございます。
使用する関数のヘルプだけ見ていて、Logical Paletteの記述見落としていました。
こんなところに書いてあるとは。。。
自分の調べ方が甘すぎました。
#英語だと必要なとこ(だと思っている箇所)だけ見て
#読み飛ばす悪い癖もなんとかしないと・・・
一応、これで再現することは今のところなくなったので
解決とさせていただきます。
回答してくださった方々、本当にありがとうございました。
チェック漏れ