visual studio 6.0 sp6 mfc+sdkにてアプリケーションを作成しています。
現在Loadimage関数を使用してBITMAP画像を表示しています。
通常時には問題なく画像データが表示されるのですが、何かのタイミング
(window切り替え等、アプリケーションを連続動作させていると突然発生する)で
読み込みを行ったはずのBITMAP画像では無くWindowsに標準登録してある
「最大化」「最小化」「チェックボックス」といった画像データが読み込まれることが
あります。
この状態になると特定フォルダ内にあるどんなBITMAPを読み込んでも
上記画像データが読み込まれてしまいます。
その後、ソフトを動作させていると突然正常に戻ります。
このような状態を経験された方はいらっしゃらないでしょうか?
よろしくお願いいたします。
読み込み部のソース↓
HBITMAP hbmp, hbmp_old;
HDC bmp_dc;
BITMAP bmp;
char * fn ←読み込みを行うファイル名が格納されています。
if( ( hbmp = (HBITMAP)LoadImage( NULL, fn, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE
| LR_CREATEDIBSECTION )) == NULL ){
return FAILUR;
}
bmp_dc = CreateCompatibleDC( hdc );
if( !bmp_dc ){
return FAILUR;
}
hbmp_old = (HBITMAP)SelectObject( bmp_dc, hbmp );
GetObject(hbmp, sizeof(BITMAP), &bmp);
変数はグローバルもしくはメンバ変数で宣言して
関数を抜けても保持するようにしていますか?
早速の回答ありがとうございます。
ソース内でグローバルやメンバ変数は
fn:関数呼び出し時にファイル名称が入ったポインタとして使用しています。
呼び出し元もローカル変数です。
hdc:グローバル変数です。
その他はローカル変数としています。
不具合発生時にデバッガーにて確認した所、fnに格納されているファイル名は
呼び出しを行いたい正しいファイル名となっていました。
ローカルでは都合の悪い箇所が有るのでしょうか?
よろしくお願いいたします。
hbmp, hbmp_oldはグローバルに移すべきです。
読み込み情報が不定状態になる事になります
(他でその領域を使われたら情報が変わります)。
hbmp_oldをきちんと使用しているかどうかは分かりませんが。
bmpは提示されているコードだけでは判断できません。
通常ローカル変数はサブルーチン内では生存していると思いますが
そういった事もあり得ると認識でよろしいでしょうか?
グローバル変数に変更して試してみます。
今更で申し訳ありませんが、下記に関数全体を記載します。
short fnc_a( HDC hdc, char *fn, short x, short y )
{
HBITMAP hbmp, hbmp_old;
HDC bmp_dc;
BITMAP bmp;
if( ( hbmp = (HBITMAP)LoadImage( NULL, fn, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE
| LR_CREATEDIBSECTION )) == NULL ){
return FAILUR;
}
bmp_dc = CreateCompatibleDC( hdc );
if( !bmp_dc ){
return FAILUR;
}
hbmp_old = (HBITMAP)SelectObject( bmp_dc, hbmp );
GetObject(hbmp, sizeof(BITMAP), &bmp);
BitBlt(hdc, x, y, bmp.bmWidth, bmp.bmHeight, bmp_dc, 0, 0, SRCCOPY);
SelectObject(bmp_dc, hbmp_old);
DeleteDC(bmp_dc);
DeleteObject( hbmp );
return SUCCESS;
}
この関数では WM_PAINT が発生するたびに呼ばないと、
表示されているビットマップはすぐに消えるけど・・・そうしているの?
HBITMAP hbmp, hbmp_old;
HDC bmp_dc;
これらは保持しておいて、WM_PAINT 時には BitBlt だけおこなうように
変更すれば?
状況がいまいちわからないので、質問返し
fnc_aは、どこから、どんなタイミングで呼ばれているのか?
引数のhdcは、表示対象のウィンドウのDCなのかなんなのか?
>「最大化」「最小化」「チェックボックス」といった画像データが読み込まれることが
ここでいう、「読み込まれる」というのは、どういうことがいいたいのか?
表示されるというのなら、表示作業を行っている部分は疑わしい可能性はないのか?
LoadImageの結果として「最大化」ボタンの画像などが読み込まれたことを確認した
というならどういう風に確認したのか?
最大化ボタンなどが読みこまれたという状況におちいったとき、
GetObjectで帰ってくる値は、「読み込みたい画像」の値か?
それとも最大化ボタンのものか?
>この状態になると特定フォルダ内にあるどんなBITMAPを読み込んでも
この際、fnc_aが呼ばれていることを確認してあるか?
現象が発生したときに、
使用メモリやGDIの数が異常に大きくなっていないか?
終了時にエラーがでたりしないか?
以下について追記いたします。
Q)
fnc_aは、どこから、どんなタイミングで呼ばれているのか?
A)
・メインループとして使用していますスレッドでデータを比較し、変化が
起きたときに呼び出します。
・マウスのL_KEYUPメッセージ処理より呼び出しています。
Q)
引数のhdcは、表示対象のウィンドウのDCなのかなんなのか?
A)
表示対象のウインドウDCと裏描画用のDCです。
Q)
>「最大化」「最小化」「チェックボックス」といった画像データが読み込まれることが
ここでいう、「読み込まれる」というのは、どういうことがいいたいのか?
A)
GetObject(hbmp, sizeof(BITMAP), &bmp);
を行った結果を読み込まれたとしています。
Q)
表示されるというのなら、表示作業を行っている部分は疑わしい可能性はないのか?
LoadImageの結果として「最大化」ボタンの画像などが読み込まれたことを確認した
というならどういう風に確認したのか?
最大化ボタンなどが読みこまれたという状況におちいったとき、
GetObjectで帰ってくる値は、「読み込みたい画像」の値か?
それとも最大化ボタンのものか?
A)
GetObject(hbmp, sizeof(BITMAP), &bmp);
の結果読み込まれたbmpのサイズが読み込みを行いたいBITMAPとは異なっていました。
読み込みしたいBITMAP 46×47
不具合時のBITMAP 16×16
その後表示を
BitBlt(hdc, x, y, bmp.bmWidth, bmp.bmHeight, bmp_dc, 0, 0, SRCCOPY);
で行っています。
Q)
>この状態になると特定フォルダ内にあるどんなBITMAPを読み込んでも
この際、fnc_aが呼ばれていることを確認してあるか?
A)
デバッガーで確認しました。
Q)
現象が発生したときに、
使用メモリやGDIの数が異常に大きくなっていないか?
終了時にエラーがでたりしないか?
A)
メモリやGDIは未確認でしたので確認します。
関数の戻り値はデバッガーで確認した限りは出ていませんでした。
どーも、PATIOです。
> Q)
> fnc_aは、どこから、どんなタイミングで呼ばれているのか?
>
> A)
> ・メインループとして使用していますスレッドでデータを比較し、変化が
> 起きたときに呼び出します。
> ・マウスのL_KEYUPメッセージ処理より呼び出しています。
本当にこれだけしかやっていないならうまく行かないと思います。
他の方も書かれていましたが、Windowsの描画処理のお約束として
WM_PAINTを受け取った時にその時点のウインドウの表示内容を
完全に再現できる処理が必要です。
この場合、アプリ側の状況なんてOS側は考慮してくれませんから
いかなる状況下でイベント待ちになっていても再描画できるように
WM_PAINTを受け取ったときの処理を作成しておく必要があります。
私だったらビットマップやメモリDCは一度作成したら保持して置くようにして
ビットマップファイルの読み込みはファイル名が変更されたタイミングのみに
します。読込が発生したらグローバル変数を更新して画面の無効化を行い、
WM_PAINTを発生させます。描画その物は、WM_PAINTを受け取ったときの処理で
行うようにして、ビットマップが指定されていない時などでも描画処理に
影響が出ないようにコードを書くと思います。
ビットマップとメモリDCの開放は、ウインドウ破棄時に確保されているかを
確認しながら開放するようにすれば、毎回読み込む必要は有りません。
補足。
Windowsでの描画結果は一度描画すれば崩れる事が無いと言うようなものではなく、
いつ何時、他のウインドウの表示等で描画結果が崩されるか分からないものです。
したがって、描画結果が崩れてしまった場合、OSからの要請に従って毎回描画を
しなおす必要があります。
ですから、OSからの描画要請であるWM_PAINTを受け取った場合は、
崩された描画結果を修復する事ができるように描画処理をコーディングしておく
必要があります。と言う事は、描画の再現に必要な情報も全て保持しておく必要が
あることを意味しています。
この辺は設計時にはちゃんと認識しておかないといざ描画する時になって
情報が足りないなんて事になります。
あと気になったんですが、
GetObjectはビットマップ構造体に情報を取り出しているだけで
読み込みその物は、LoadImageがやっているのですよね。
おかしくなっている時にLoadImageが参照しているファイル名は
間違いなく対象のファイルパスになっているかどうかの確認は
行ないましたか?
みなさま色々ご意見ありがとうございます。
基本的な動作について再度確認してみます。
(表示方法を見直すのはいろいろな面で難しいですが、、、)
LoadImageが参照しているファイル名については不具合発生時にデバッガーで
停止させた時、正常なファイル名であることを確認しています。
以下、現在確認出来た点です。
試しに不具合発生時にfnのファイル名をデバッガーにより強制的に書き換えを
行い、違うフォルダのファイル名に書き換えをした所、正常に読み込めました。
不具合が出てからでも特定フォルダ以外は正常にデータが読めるようです。
どこかでリークしてたりオーバフローしているとかも疑った方がいいかもしれません。
func_a関数は2個目のif文で
DeleteObject( hbmp );
呼び忘れてる程度で(リークしますよ)、その他は特に問題なく見えます。
個人的にはCreateCompatibleDCはよいとして、
LoadImageは毎回呼ばないように作ります。
2個上のPATIOさんは、fn が相対パス指定の時に、
カレントフォルダが移動した場合等を指摘しているのかと思います。
相対パスの場合、フルパスで指定してみるとどうなりますか?
ZCHさん
ご指摘ありがとうございます。
DeleteObjectが抜けていました。
fnについてはフルパスにて指定しています。
上にてアドバイスのあった
HBITMAPhbmp, hbmp_old;
BITMAP bmp;
先週の金曜日より、ソフトをデバッグ状態で走らせていますが現象が出てません。
ZCHさん
ご指摘ありがとうございます。
DeleteObjectが抜けていました。
fnについてはフルパスにて指定しています。
上にてアドバイスがありましたので
HBITMAP hbmp, hbmp_old;
BITMAP bmp;
をstaticとして先週の金曜日より、ソフトをデバッグ状態で走らせていますが
不具合現象は出てません。
たまたま出ていないだけかもしれませんが、、、。
これで不具合が発生しないとしてもいまいち原因がわかりません。
やはりどこかでリークが発生しているのか?
リークが発生するとこのような現象が発生するんでしょうか?
メモリやリソースを食いつぶしていってソフトが動作しなくなるとは思いますが。