アプリ版:「スタンプのみでお礼する」機能のリリースについて

「猫でもわかるWindowsプログラミング」を参考に、ビットマップを表示するプログラムを作っています。

下記ソース(WM_PAINT部分のみ抜粋)のような感じで、
読み込んだビットマップをダブルバッファリングを用いて表示させたいのですが、
何も表示させることができず、困っております。

LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp )
{
  HBITMAP hBmp;
  BITMAP bmp_info;
  HDC hDC, hDC_mem;
  PAINTSTRUCTps;
  int w=0, h=0;

  switch (msg) {

case WM_PAINT:

  hDC = BeginPaint(hwnd,&ps);
  hDC_mem = CreateCompatibleDC(hDC);
  hBmp=LoadBitmap(hInst, TEXT("MYBMP"));
  hBmp = (HBITMAP)LoadImage(hInst, TEXT("MYBMP"), IMAGE_BITMAP,
0, 0, LR_DEFAULTCOLOR);
GetObject(hBmp, (int)sizeof(BITMAP), &bmp_info);
w = bmp_info.bmWidth;
h = bmp_info.bmHeight;
SelectObject(hDC_mem, hBmp);

BitBlt(hDC, 0, 0, w, h, hDC_mem, 0, 0, SRCCOPY);

DeleteDC(hDC_mem);
DeleteObject( hBmp );
EndPaint( hwnd, &ps );
break;
}
}


リソースですが、下記のように bitmap1.bmpを指定しており、画像はもちろんフォルダ内に入れ、
読み込める状態にはしています。
  //
  // Bitmap
  //
  MYBMP BITMAP "bitmap1.bmp"
  #endif // 日本語 resources


しかし、hBmpの戻り値を調べると NULL の値が入っています。当然画像は表示されません。

当方、Microsoft visual studio 2005を使っておりますが、このプロジェクトは、
Win32コンソールアプリケーション⇒Windowsアプリケーション で作成はせず、
Win32コンソールアプリケーション⇒コンソールアプリケーション で作成していますので、
ここが問題になっているのでしょうか?

できれば、コンソールアプリケーションで作成したいですが、LoadImage関数は使えないのでしょうか?
それとも他に問題点があるのでしょうか?


以前は動画処理をしており、SetDIBitsToDevice関数を使って配列の中身を指定し、画像をウィンドウ上に表示していました。
しかし、画像のちらつきが気になったため、ダブルバッファリングに改良しようとし、
調べてみたところ、ビットマップを用いるやり方が一般的なようでした。
そこで、まずはビットマップの表示をさせるプログラムをつくってみようと思ったのですが、つまずいている状態です。

環境は、
Microsoft Windows XP Home Edition Version2002 Service Pack 3
Pentium(R) 4 CPU 2.80GHz 1.0GB RAM
Microsoft visual studio 2005(有料)
です。
何かアドバイスをよろしくお願いします。

A 回答 (3件)

SelectObjectは、デバイスコンテキストにビットマップなどを選択すると同時に、元々選択されていたビットマップなどのハンドルを返します。

メモリDCには、1×1ピクセル モノクロのビットマップが選択されているはずです。なので、このビットマップのハンドルを保存しておかないと、その後このビットマップの操作ができなくなる(リソースリーク)ということです。

デバイスコンテキストに選択されているリソースは、元に戻してからデバイスコンテキストをリリースするなり、削除するなりしないといけません。

HBITMAP hbmpOld;

hbmpOld = (HBITMAP)SelectObject(hDC_mem, hBmp);
BitBlt(hDC, 0, 0, w, h, hDC_mem, 0, 0, SRCCOPY);
SelectObject(hDC_mem, hbmpOld); /* ここでSelectObjectが返すのはhBmpなので、保存する必要はない */
DeleteDC(hDC_mem);
DeleteObject(hBmp);

としましょう。

この回答への補足

回答ありがとうございます。
単純に1つのビットマップを表示させるだけのプログラムは、
正常に動作するようになりました。

これを動画処理でも行えるようにしたいのですが、動画処理した結果をビットマップで連続で表示しようとするプログラムでは、BitBlltでウィンドウにうまく転送できていないようです。
(ウィンドウに変化なし+ウィンドウをクリックしてもアクティブにならない)

動画は25fpsで、1フレームごとに処理結果をビットマップ画像に書き込み、
LoadImage関数でそのビットマップ画像を読み込み、読み込んだ画像をBitBltで転送することの繰り返しを行っています。

試しに50フレーム間、画像処理結果をビットマップ画像に書き込んでみましたが、書き込み自体は正常に動作していると思います。
LoadImage関数も戻り値がNULLではないので問題ないはず。。

問題があるとすれば、ウィンドウやインスタンスのハンドル?と思ったので、
コンソールアプリケーションでつくっていたものを、
Windowsアプリケーションとしてつくりなおしてみましたが、問題はそこでなかったためか効果ありませんでした。


下記は、プログラムのソースになります。
*************************************************
// (1)ビットマップ(MYBMP)に画像処理結果書き込み
 out_bmmap("MYBMP"); //自作のプログラム

 hdc = BeginPaint(hWnd, &ps);
 hdc_mem = CreateCompatibleDC(hdc);
// (2)ビットマップ読み込み
 hBmp = (HBITMAP)LoadImage(hInst,
 TEXT("MYBMP"), IMAGE_BITMAP,
 0, 0, LR_DEFAULTCOLOR);
 GetObject(hBmp, (int)sizeof(BITMAP), &bmp_info);
 w = bmp_info.bmWidth;
 h = bmp_info.bmHeight;
 hBmpOld = (HBITMAP)SelectObject(hdc_mem, hBmp);
 // (3)ビットマップをウィンドウに転送
 BitBlt(hdc, 0, 0, w, h, hdc_mem, 0, 0, SRCCOPY);
 SelectObject(hdc_mem, hBmpOld);
 DeleteDC(hdc_mem);
 DeleteObject( hBmp );
 EndPaint( hWnd, &ps );
*************************************************

(1)~(3)の処理を1フレームごとに行うために、
http://okwave.jp/qa/q6366968.html
を参考にし、InvalidateRectとUpdateWindowを1フレームの処理ごとに使っています。
なかなかうまくいかないです;;

補足日時:2010/12/07 05:17
    • good
    • 0

> wc.hInstance = (HINSTANCE)GetWindowLong( HWND_DESKTOP, GWL_HINSTANCE );



wcとは何? また、デスクトップはユーザーごとに1つなので、実行しているインスタンスのハンドルにはならないのでは?

ビットマップが表示されないのとは関係ないかもしれませんが、

> hBmp=LoadBitmap(hInst, TEXT("MYBMP"));
>  hBmp = (HBITMAP)LoadImage(hInst, TEXT("MYBMP"), IMAGE_BITMAP,
> 0, 0, LR_DEFAULTCOLOR);

どちらか一方にしたほうが良いのでは?

> SelectObject(hDC_mem, hBmp);

> BitBlt(hDC, 0, 0, w, h, hDC_mem, 0, 0, SRCCOPY);

> DeleteDC(hDC_mem);
> DeleteObject( hBmp );

hDC_memに最初に選択されていたビットマップのハンドルが保存されていません。
hBmpを選択後、選択を解除せずにhDC_memを削除しています。

この回答への補足

ビットマップなんですが、表示できました!
どうやらウィンドウハンドルを複数作成していたため、ごちゃごちゃになっていたのが原因みたいです。

表示はできたのですが、新たに教えていただきたいこともありますので、
以下、(1)~(3)に分けてコメントさせていただきます


(1)WNDCLASSEX wcは、ウィンドウのクラスです。
 説明不足ですみません。

typedef WNDCLASSEXA WNDCLASSEX;

typedef struct tagWNDCLASSEXA {
UINT cbSize;
/* Win 3.x */
UINT style;
WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HINSTANCE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCSTR lpszMenuName;
LPCSTR lpszClassName;
/* Win 4.0 */
HICON hIconSm;
}

(2)hBmpについては、試行錯誤していた際のソースを載せてしまいました;;
 普段は、LoadImageのほうだけを書いていました。

(3)「hDC_memに最初に選択されていたビットマップのハンドルが保存されていません。hBmpを選択後、選択を解除せずにhDC_memを削除しています。」
 とのことですが、私の理解力が足らずよく分かりませんでした。
申し訳ありませんが、もう少し詳しく教えていただいてもよろしいでしょうか?

私は、下記のような感じで、ダブルバッファリングをしているつもりだったのですが、まったく違うのでしょうか?

 hDC = BeginPaint(hwnd,&ps);
 hDC_mem = CreateCompatibleDC(hDC);
 hBmp = (HBITMAP)LoadImage(hInst, TEXT("MYBMP"),
  IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
 GetObject(hBmp, (int)sizeof(BITMAP), &bmp_info);
 w = bmp_info.bmWidth;
 h = bmp_info.bmHeight;
 SelectObject(hDC_mem, hBmp);

 // ここに hDC_memを対象とした描画処理を追加していく
 //     [追加ソース]

 BitBlt(hDC, 0, 0, w, h, hDC_mem, 0, 0, SRCCOPY);// 一度だけ転送
 DeleteDC(hDC_mem);
 DeleteObject( hBmp );

補足日時:2010/12/06 23:20
    • good
    • 0

コンソールアプリで元を作ったのですか?



WinMainからこないと色々と面倒なことを自前でやら無ければいけなくなりますが ・・・
リソースを読み込むにしても HINSTANCEが不正なら読み込めませんよ

この回答への補足

コンソールがあったほうがデバッグしやすかったので。。
いろんな本を参考に自前で作成しました。

以前は、下記のようにSetDIBitsToDevice関数を使い、lpBmpDataの中身を
画面に表示していました。これについては、特に問題はなく表示できていたと思います。


//グラフィック用ウィンドウのデータをまとめた構造体
typedef struct
{
 HINSTANCE hi;
 int x;// 表示開始位置
 int y;
 HWND hwnd;// 自分のウィンドウハンドル
 BYTE *lpBmpData; // BMPのデータ部分
 BITMAPINFOHEADER bih;
} IMG0;

// ウィンドウクラス登録時にインスタンスハンドルの取得
wc.hInstance = (HINSTANCE)GetWindowLong( HWND_DESKTOP, GWL_HINSTANCE );
//グローバル変数hInstに格納
hInst = wc.hInstance;

IMG0 * pimg;

hDC = BeginPaint(hwnd,&ps);

SetDIBitsToDevice( hDC, 0, 0,// コピー先x,y座標
   pimg -> bih.biWidth,// DIBの幅
   pimg -> bih.biHeight,// DIBの高さ
   0, 0,// DIBの座標
   0,// 走査線
   pimg -> bih.biHeight,// 走査線数
   pimg -> lpBmpData,// 画像処理後のデータをここにいれています
   (BITMAPINFO *)&( pimg -> bih),// BITMAPINFOにキャスト
DIB_RGB_COLORS );

EndPaint( hwnd, &ps );


私自身プログラミング初心者ですので、どこかのソース記述が間違っており、HINSTANCEが不正ということもあると思います。
上記ソースでは、hInst使ってないですし。。

一度Windowsアプリケーションで作成してみます!!
アドバイスありがとうございました。

補足日時:2010/12/06 02:38
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!