Tips of VC++ > ウィンドウ > ウィンドウクラスを作るには
★ 前へ戻る ★ 次へ進む


ウィンドウクラスを作るには

ウィンドウのベースとなるものに、ウィンドウクラスというものがあります。 ただ、ウィンドウクラスとは言っても、 C++の機能の一つであるクラスとは関係ありません。 これは、ウィンドウのテンプレートのようなもので、 プロシージャ・アイコン・カーソル・背景色など 基本的な情報が登録されています。
一つのウィンドウクラスから複数のウィンドウを作ることもできます。 どんなウィンドウでも先にウィンドウクラスを登録しなければいけません。

ウィンドウクラスは文字列で識別されます。 あらかじめ登録されているウィンドウクラスとして、 "BUTTON", "COMBOBOX", "EDIT", "LISTBOX", "MDICLIENT", "RichEdit", "RICHEDIT_CLASS", "SCROLLBAR", "STATIC"があります。 名前を見て分かる方もおられるでしょうが、 これはコントロールを示しています。 もちろん、コントロールもウィンドウの一種です。 コントロールと親ウィンドウの差はここで現れるのですね。

つまり、プログラマが新しいコントロールを新しく作る場合、 ウィンドウクラスを登録すればいいのですね。 実際は、プロシージャを完璧に実装しなければいけないので、かなり難しい。

登録の手順は、WNDCLASSEXという構造体に適切な値を入れ、 RegisterClassExというAPIに渡す、というものです。 WNDCLASS構造体を使うRegisterClassというAPIもありますが、 MSDNにRegisterClassExを使えと書いてあるので、EXのほうを使います。

typedef struct _WNDCLASSEX {
    UINT cbSize;
    UINT style;
    WNDPROC lpfnWndProc;
    int cbClsExtra;
    int cbWndExtra;
    HINSTANCE hInstance;
    HICON hIcon;
    HCURSOR hCursor;
    HBRUSH hbrBackground;
    LPCTSTR lpszMenuName;
    LPCTSTR lpszClassName;
    HICON hIconSm;
} WNDCLASSEX,PWNDCLASSEX;

cbSize

構造体のサイズを入れます。sizeof(WNDCLASSEX)を入れます。

style

CS_で始まる定数のフラグの組み合わせです。 ほとんどの場合、CS_HREDRAW|CS_VREDRAWを入れればいいです。 私はこれしか使ったことがないです。(^^;
他の定数は後でまとめて載せます。

lpfnWndProc

ウィンドウプロシージャを指定します。 このウィンドウクラスを使うウィンドウは全てこのプロシージャを使います。

cbClsExtra

構造体が使用する「余分な(extra)」メモリのサイズを指定するそうです。 何に使うのか分かりません。とりあえず0にしておきましょう。(^^;

cbWndExtra

今度はウィンドウが使用する「余分な(extra)」メモリのサイズです。 「余分な」ら0でいいジャン、って気がしますが…。 てなわけで、これも0にしておきましょう。(^^;;;

hInstance

プロシージャを所有しているモジュールのインスタンスハンドルを指定します。 WinMainのパラ―メータを使用すると便利です。

hIcon

アイコンリソースへのハンドルを指定します。 NULLにするとコンソールアプリ特有の「あの」アイコンを使われてしまいます。

hCursor

カーソルリソースへのハンドルを指定します。 NULLにすると、マウスカーソルがウィンドウの中に移動したときに 常に「はっきりと(explicitly)」カーソルをセットしなければならないそうです。 この副詞強調。

hbrBackground

ウィンドウの背景色をブラシで指定します。 CreateBrushとかCreateSolidBrushの戻り値も使えますが、 システムカラーの定数(COLOR_WINDOWなど)を使えます。 それに1を足してHBRUSHにキャストすると言う方法です。 指定したブラシは、クラスがフリー(free)になったときに システムが自動的に削除するので、アプリケーションが削除しないで下さい。

lpszMenuName

メニューリソースを示すNULLで終わる文字列か、 リソースの識別子をMAKEINTRESOURCEマクロを使って指定します。

lpszClassName

NULLで終わる文字列か、クラスアトムを指定します。 クラスアトムなんて使用しなくてもできるので、 できるだけ文字列を指定しましょう。 ようするに、作成するウィンドウクラスの名前です。 普通(私の場合)は、アプリケーション名を指定します。

hIconSm

アプリケーションから連想される(be associated)スモールアイコンを指定します。 NULLにするとhIconを無理矢理縮めたものを使います。 私はNULLにすることが多いです。

構造体に値を一通り入れたら、APIを呼びます。

ATOM RegisterClassEx(
  CONST WNDCLASSEXlpwcx  // クラスデータ
);

クラスアトムが戻り値ですが、 lpszClassNameで文字列を指定した場合は戻り値を無視して結構です。 CreateWindowではその文字列を指定すればいいのです。

最後に例を載せます。 多くの場合、WinMainの先頭にガリガリ書く作業なので、 その場合を想定して載せます。

const char szClassName[]="アプリケーション名";

int APIENTRY WinMain(HINSTANCE hInstance,
        HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)
{
    WNDCLASSEX wc;
    wc.cbClsExtra =0;
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.cbWndExtra = 0;
    wc.hbrBackground = (HBRUSH)(COLOR_DESKTOP+1);
    wc.hCursor = ::LoadCursor(NULL,MAKEINTRESOURCE(IDC_ARROW));
    wc.hIcon = NULL;
    wc.hIconSm = NULL;
    wc.hInstance = hInstance;
    wc.lpfnWndProc = (WNDPROC)WndProc;
    wc.lpszClassName = szClassName;
    wc.lpszMenuName = NULL;
    wc.style = CS_HREDRAW|CS_VREDRAW;
    ::RegisterClassEx(&wc);

    // これより先はウィンドウの表示など
}

この後に書く作業については次のスレッド、 ウィンドウベースのアプリケーションを作る を見てください。

最後に、WNDCLASSEX::styleで指定する Class Styleのリファレンスを載せときます。 若干、直訳っぽい表現があるのはご愛敬。

CS_BYTEALIGNCLIENT, CS_BYTEALIGNWINDOW

ウィンドウのクライアント領域、または、ウィンドウ自身を バイト境界(byte boudary)に揃えます。 試してみたんですが、何の効果があるのか分かりません。(ダメジャン)

CS_DBLCLKS

ウィンドウ上でのマウスのダブルクリックを通知します。 (WM_LBUTTONDBLCLKメッセージを通知) このフラグを設定しないと、ダブルクリックは通知されません。

CS_HREDRAW, CS_VREDRAW

ウィンドウがサイズ変更可能で、かつ ユーザーがウィンドウのサイズを水平・垂直方向に変更したときに クライアント領域を再描画します。 これを指定しない場合、サイズ変更の後でも クライアント領域のグラフィックは残ります。 当然ですが、このフラグを指定しなくても、 ウィンドウを縮めて隠れた部分は再描画されます。

CS_NOCLOSE

ウィンドウの右上の×ボタンを無効にします。 Alt+F4なども効かなくなるので、 プログラマは終了処理をどこかに書かなければなりません。 WM_DESTROYのルーチンを書いているのであれば、 終了処理はDestroyWindowで構いません。

CS_SAVEBITS

このフラグの指定されたウィンドウクラスから派生するウィンドウが 移動したとき、ウィンドウによって隠される画面の一部が ビットマップとしてメモリに保存されます。 ウィンドウが再び移動、または破棄されて、 隠されていた部分が表示されるとき、 システムは、保存されているビットマップを使用します。 したがって、背景の再描画時にはWM_PAINTメッセージは送られません。 このスタイルはメニューやダイアログなどの小さなウィンドウに便利です。 ただし、ウィンドウ表示の動作は遅くなります。

CS_OWNDC

ウィンドウごとに独立したデバイスコンテキストを供給します。
このフラグを指定した場合、描画時にわざわざ GetDCを呼び出す必要がなくなります。 一度、GetDCを使ってデバイスコンテキストを取得すれば、 ウィンドウが破棄されるまで、そのハンドルが有効になります。 なお、WM_PAINTメッセージのルーチンを記述するときは BeginPaint関数を呼び出さなければなりませんが、 戻り値のハンドルを使う必要はありません。

CS_CLASSDC

このフラグを指定すると、 同じウィンドウクラスを使うウィンドウ全てに、 同じデバイスコンテキストが割り当てられます。 ウィンドウクラスはプロセスで特有なので、 マルチスレッドのアプリケーションが 同じクラスを持つウィンドウを作成したり、 同時にそのデバイスコンテキストを使おうと試みることはできます。 しかし、システムが描画処理を成功させるのは、一つのスレッドのみです。

CS_PARENTDC

子ウィンドウのクリッピング領域を、 親ウィンドウのクリッピング領域にセット(set)し、 子ウィンドウが親ウィンドウ上で描画できるようにします。
このフラグを持つウィンドウクラスから派生するウィンドウは、 システムにキャッシュされているデバイスコンテキストから、 デバイスコンテキストを取得します。 また、システムは子ウィンドウに親ウィンドウのデバイスコンテキストや、 デバイスコンテキストの設定(device context settings)を提供しません。 このフラグは、アプリケーションのパフォーマンスを高めます。

CS_GLOBALCLASS

このウィンドウクラスをapplication global classに指定します。

Oct.22, 2002