LoadImageでhBitmapにNULLが入る場合があります。
この場合は再度LoadImageを呼ぶようにしていますが
またhBitmapにNULLが入ってしまい、BMPが読めません。
どのような原因でこのような症状が発生するのか
教えていただきたくお願いします。
<補足>
・BMP連続描画を行うことができるが、なんらかの条件により失敗している。
(原因は不明。描画回数or描画間隔が原因かも)
・10回以上描画した後にメモリリークなし
・デバッグmodeで確認したとき問題発生後に、添付画像の警告が出た。
<ソース>
HBITMAP hBitmap = (HBITMAP)::LoadImage(AfxGetInstanceHandle(),(LPCSTR)csBmpName,IMAGE_BITMAP,0,0,LR_LOADFROMFILE | LR_CREATEDIBSECTION);
static BITMAP bmpBack;
if(hBitmap == NULL)return FALSE;
GraphicMap.Attach(hBitmap);
GetObject(hBitmap , sizeof(BITMAP) , &bmpBack);
CDC cdcSubDC;
cdcSubDC.CreateCompatibleDC(pDC);
CBitmap* oldBMP=cdcSubDC.SelectObject(&GraphicMap);
cdcMain->SetStretchBltMode(COLORONCOLOR) ;
if(0==cdcMain->StretchBlt(iXzahyou,iYzahyou,iMainYoko,iMainTate,
&cdcSubDC,iXzahyou*bmpBack.bmWidth/iMainYoko,iYzahyou*bmpBack.bmHeight/iMainTate,
iMainYoko*bmpBack.bmWidth/iMainYoko,iMainTate*bmpBack.bmHeight/iMainTate,SRCCOPY)){
iReturnValue=FALSE;
}
cdcSubDC.SelectObject(oldBMP);
読み込まれたhBitmapは、StretchBltの後再利用されていないので、DeleteObjectで削除すべきではないでしょうか?
頭にHのつくハンドルと呼ばれるものはWindowsサイドで管理しているものなので、メモリリークとしては現れなかったような気もします。
念のため、LoadImageの結果がNULLのとき、GetLastErrorを取得することをオススメします(FormatMessageと併用するとなおGood)。
もしかすると解決の糸口が見つかるかもしれません。
それと、お節介かも知れませんがいくつか。
ただ、この部分の処理は重いので、OnPaintなどには使わない方がいいかもしれませんね。
備考: VC++7.0以降なら、MFCのCImageクラスを使って、
LPCTSTR lpszFileName = TEXT( "bitmap.bmp" ); CImage image; image.Load( lpszFileName ); image.StretchBlt( cdcMain->GetSafeDC(), iXzahyou, iYzahyou, iMainYoko, iMainTate, iXzahyou * image.GetWidth() / iMainYoko, iYzahyou * image.GetHeight() / iMainTate, image.GetWidth(), image.GetHeight() );
とできるようです。
http://rararahp.cool.ne.jp/cgi-bin/lng/vc/vclng.cgi?print+200607...
oldBMPのDeleteObject
cdcSubDCのDeleteDc
が抜けてませんか?
回答ありがとうございます。試してみても改善しませんでしたが
そこに問題があるのかもしれませんね。
私が用いている教科書(新VisualC++6.0入門ビギナー編)には、oldBMPや
cdcSubDCを削除するように書かれていませんでしたので
教えていただいたコードを追加する必要性について説明していただきたくよろしくお願いします。
それと、下記のように私の理解に基づきコメントを追加しました。
何か不自然なところなどありましたらご指摘いただけると嬉しいです。
よろしくお願いします。
HBITMAP hBitmap = (HBITMAP)::LoadImage(ファイル名等) //ビットマップロード
if(hBitmap == NULL)return FALSE; //ロードできなかったらReturn
GraphicMap.Attach(hBitmap); //GraphicMapとビットマップの関連付け
GetObject(hBitmap , sizeof(BITMAP) , &bmpBack);
CDC cdcSubDC; //裏画面作成
cdcSubDC.CreateCompatibleDC(pDC); //裏画面にメイン画面(pDC)との互換性を持たせる
CBitmap* oldBMP=cdcSubDC.SelectObject(&GraphicMap); //裏画面とGraphicMapの関連付け
cdcMain->SetStretchBltMode(COLORONCOLOR); //
//メイン画面に裏画面の画像を描画
if(0==cdcMain->StretchBlt(iXzahyou,iYzahyou,iMainYoko,iMainTate,
&cdcSubDC,iXzahyou*bmpBack.bmWidth/iMainYoko,iYzahyou*bmpBack.bmHeight/iMainTate,
iMainYoko*bmpBack.bmWidth/iMainYoko,iMainTate*bmpBack.bmHeight/iMainTate,SRCCOPY)){
iReturnValue=FALSE;
}
cdcSubDC.SelectObject(oldBMP); //なぜそうするのか
//理解していない参考書どおり
cdcSubDC.DeleteDC();
oldBMP->DeleteObject();
読み込まれたhBitmapは、StretchBltの後再利用されていないので、DeleteObjectで削除すべきではないでしょうか?
頭にHのつくハンドルと呼ばれるものはWindowsサイドで管理しているものなので、メモリリークとしては現れなかったような気もします。
念のため、LoadImageの結果がNULLのとき、GetLastErrorを取得することをオススメします(FormatMessageと併用するとなおGood)。
もしかすると解決の糸口が見つかるかもしれません。
それと、お節介かも知れませんがいくつか。
ただ、この部分の処理は重いので、OnPaintなどには使わない方がいいかもしれませんね。
備考: VC++7.0以降なら、MFCのCImageクラスを使って、
LPCTSTR lpszFileName = TEXT( "bitmap.bmp" ); CImage image; image.Load( lpszFileName ); image.StretchBlt( cdcMain->GetSafeDC(), iXzahyou, iYzahyou, iMainYoko, iMainTate, iXzahyou * image.GetWidth() / iMainYoko, iYzahyou * image.GetHeight() / iMainTate, image.GetWidth(), image.GetHeight() );
とできるようです。
いろいろと、教えていただきありがとうございます。
GetLastErrorとFormatMessageを併用したところ
『このコマンドを実行するのに十分な記憶域がありません』とでていました。
やはり、ご指摘いただいているhbitmap等のリソース開放処理がないためかな?
>>cdcSubDC.CreateCompatibleDCの引数は、pDCではなくcdcMainがふさわしいのでは?
pDCはcdcMainのアドレスが入っているので、cdcMainと同じです。
>>読み込みは、HBITMAP LoadBitmap( LPCTSTR lpszFileName )のような関数に
>>処理をまとめた方が楽
確かにそうしたほうがソースも読みやすいですね。
コーディングに対する指摘も参考になり大変ありがたいです。
他にまた気になる点などありましたらまた、ご指摘いただきたくよろしくお願いします。
>>頭にHのつくハンドルと呼ばれるものはWindowsサイドで管理しているものなので、メモリリークとしては現れなかったような気もします。
あれっっ
ひょっとしてHICONなども開放する必要がありますか?
自信はありませんが…
HICONがプログラムモジュール内のリソースをロードするだけなら、リリースする必要はないと思います。
無論、ハンドルやポインタは、ちゃんと解放する癖をつけておくのは正しいことだと思います。
回答ありがとうございます。
WindowsタスクマネージャのPF使用量を見ながら
メモリリークしている処理を探したのですが、
SHGetFileInfo関数でHICONを受け取るときに
PF使用量が増加していることがわかりました。
そこでネットで検索したDestroyIcon関数の
開放処理を追加したところ、
SHGetFileInfo関数を呼び出すごとに
増加していたPF使用量が増加しなくなり、
BMP描画関数でこけることもなくなりました!!
裏づけが信頼性の高い資料などで確認取れていませんが、
SHGetFileInfo関数で取得した
HICONをDestroyIcon関数で
開放していなかったため、アイコンを再描画するたびに
メモリリークが発生し、
その影響からBMPファイル読み込みに支障がでたようです。
いろいろと、教えていただきありがとうございます。
GetLastErrorとFormatMessageを併用したところ
『このコマンドを実行するのに十分な記憶域がありません』とでていました。
やはり、ご指摘いただいているhbitmap等のリソース開放処理がないためかな?
>>cdcSubDC.CreateCompatibleDCの引数は、pDCではなくcdcMainがふさわしいのでは?
pDCはcdcMainのアドレスが入っているので、cdcMainと同じです。
>>読み込みは、HBITMAP LoadBitmap( LPCTSTR lpszFileName )のような関数に
>>処理をまとめた方が楽
確かにそうしたほうがソースも読みやすいですね。
コーディングに対する指摘も参考になり大変ありがたいです。
他にまた気になる点などありましたらまた、ご指摘いただきたくよろしくお願いします。
>>頭にHのつくハンドルと呼ばれるものはWindowsサイドで管理しているものなので、メモリリークとしては現れなかったような気もします。
あれっっ
ひょっとしてHICONなども開放する必要がありますか?