1つのウィンドウプロシージャで複数ウィンドウを処理(SetWindowLongPtr)

icon 項目のみ表示/展開表示の切り替え

概要

ウィンドウを開くプログラムです。
1つのウィンドウプロシージャで複数のウィンドウを使用する場合、ウィンドウプロシージャ自体が現在どのウィンドウを処理しているか知る必要があります。
ウィンドウタイトルを調べる方法がありますが、ここでは、ウィンドウクラスを登録するときにウィンドウごとに別々の拡張メモリを割り当てるように設定し、ウィドウの初期化時に読みだされるWM_CREATEメッセージの時に、SetWindowLongPtr APIを用いて拡張メモリに固有の値や変数を設定しておきます。
他のメッセージを処理するときはGetWindowLongPtr APIで拡張メモリを取り出します。
このプログラムではウィンドウを開くCreateWindow関数の最後の引数に固有の文字列を渡し、WM_PATINメッセージを処理するときにその文字列を取り出して表示します。
拡張メモリ、ウィンドウクラスを定義する構造体WNDCLASSのcbWndExtraメンバに必要な拡張メモリのサイズを設定することによりウィンドウを作成するたびに自動的に作成されます。
ウィンドウの拡張メモリはSetWindowLongPtr APIおよびGetWindowLongPtr APIによりアクセスできますが、32bit実行ファイルの場合は32bit単位、64bit実行ファイルの場合は64bit単位となります。キャストする際に32bitまたは64bitで型を使い分ける必要があるため、プリプロセッサの条件付きコンパイルを使用して同じ名前で使用できる型をtypedefにより作成します。

テスト環境

コンパイラ

Visual C++ 2008 Standard 32/64bit
Visual C++ 2013 Express 32/64bit

実行環境

Windows 8.1 Enterprise 64bit
Windows 7 EnterPrise Service Pack 1 64bit
Windows Vista Ultimate Service Pack 2 32bit
Windows XP Professional Service Pack 3 32bit

プログラムソースの概要

_tWinMain関数

Windowsから最初に_tWinMain関数が呼び出されます。 ウィンドウを作成する場合は、RegisterClass APIによりウィンドウクラスを定義してからCreateWindow APIを呼び出しウィンドウを作成します。 Windowsは入力等のイベントが発生するとアプリケーションにメッセージを送付します。 メッセージはキューに保管されます。アプリケーションはメッセージを取り出し、該当ウィンドウにメッセージを配信します。 メッセージの取り出しから配信までループで処理を行いウィンドウから終了メッセージが届くと、ループを抜けるように記述します。 これらの一連の処理は、通常にCreateWindow APIの後に記述しこれをメッセージループと呼んでいます。 詳細は以下を参照してください。
メッセージループについて
今回は複数のウィンドウを作成するため、CreateWindowの部分を関数InitInstanceで記述しています。

WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)ウィンドウプロシージャー

RegisterClass APIにより登録することによりWindowsから呼び出されます。
第2引数にメッセージの種類が格納されていますので、switchステートメントによりメッセージごとの処理を振り分けます。
自分で処理しないメッセージはDefWindowProc APIに渡せばWindowsが標準的な処理を行ってくれます。

case WM_CREATE:

ウィンドウの初期化時に呼び出されます。
CreateWindow APIに渡された引数はCREATESTRUCT構造体へのポインタがLPARAMに格納されています。
LPARAMをCREATESTRUCT構造体へのポインタにキャストし、lpCreateParamsメンバーを取り出します。これがCreateWindow APIの最後の引数です。
この引数は文字列定数へのポインタですので、SetWindowLongPtr APIを用い、ウィンドウごとに確保された拡張メモリに保管します。
SetWindowLongPtr APIの第3引数は、拡張メモリに設定するデータですが、32bit実行ファイルを作成する場合は32bitのデータ(LONG)、64bit実行ファイルを作成する場合は64bit(LONG_PTR)であり、それぞれキャストを使い分ける必要があります。
使い分けが煩雑なので、WINDOW_LONG_PTR型をプリプロセッサで作成しています。
その他の処理は、DefWindowProc APIにそのままメッセージを渡して処理しています。

case WM_DESTROY:

ウィンドウが閉じるときに呼び出されます。
PostQuitMessage APIにより終了コードを指定して、ウィンドウプロシージャーを終了させます。
今回2つのウィンドウを作成していますが、1つのウィンドウを閉じることにより2つのウィンドウが閉じます。

case WM_PAINT:

ウィンドウを再描画する必要があるときに呼び出されます。
他のウィンドウに隠れ再びフォアグラウンドになった場合などウィンドウの再描画はWindowsが面倒を見ないのでプログラマの仕事となっています。
BeginPaint APIを呼び出して、描画に必要なデバイスコンテキストのハンドルを取得します。
BeginPaint APIの第2引数のポインタにはPAINTSTRUCT構造体のポインタを渡します。
BeginPaint API終了後、PAINTSTRUCT構造体には再描画が必要な領域の座標等の値が格納されています。
GetWindowLongPtr APIによりウィンドウごとに確保されている拡張メモリの値を取得します。
TextOut APIにより拡張メモリに格納されている文字列へのポインタを用いてウィンドウに文字を描画します。
文字の種類や大きさ等はディフォルトの値が使われます。
変更したい場合はCreateFont APIで新たにフォントを作成しSelectObjectでフォントを選択し、フォントが不要になったら、DeleteObject APIでフォントを削除します。 再描画が終了したことをWindowsに知らせるためにEndPaint APIを使用してます。このAPIを呼び出さないと何度もWM_PAINTメッセージが発生し暴走します。

プログラムソース

//      ウィンドウを開く(拡張メモリを使用)
//      Visual C++ 2013 32/64bit

#include <windows.h>
#include <tchar.h>

TCHAR szClassName[] = TEXT("TestWindow");

#ifdef _WIN64
        typedef LONG_PTR WINDOW_LONG_PTR;
#else
        typedef LONG WINDOW_LONG_PTR;
#endif

        //      ウィンドウプロシージャー
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

//      ウィンドウを作成する
BOOL InitInstance(HINSTANCE hInst, TCHAR* szClassName, int nCmdShow, LPVOID para);

int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPreInst, TCHAR* lpszCmdLine, int nCmdShow){
        MSG lpMsg;
        WNDCLASS wc;

        if (!hPreInst) {
                wc.style = CS_HREDRAW | CS_VREDRAW;
                wc.lpfnWndProc = WndProc;
                wc.cbClsExtra = 0;
                wc.cbWndExtra = sizeof(WINDOW_LONG_PTR);
                wc.hInstance = hInstance;
                wc.hIcon = NULL;
                wc.hCursor = LoadCursor(NULL, IDC_ARROW);
                wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
                wc.lpszMenuName = NULL;
                wc.lpszClassName = szClassName;
                if (!RegisterClass(&wc))
                        return FALSE;
        }
        if (!InitInstance(hInstance, szClassName, nCmdShow, TEXT("1個目のウィンドウです"))) {    // ウィンドウの作成
                return FALSE;
        }
        if (!InitInstance(hInstance, szClassName, nCmdShow, TEXT("2個目のウィンドウです"))) {    // ウィンドウの作成
                return FALSE;
        }

        while (GetMessage(&lpMsg, NULL, 0, 0)) {
                TranslateMessage(&lpMsg);
                DispatchMessage(&lpMsg);
        }
        return int(lpMsg.wParam);
}

//      ウィンドウプロシージャー

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam){
        HDC hdc;
        PAINTSTRUCT ps;

        switch (msg) {
        case WM_CREATE:{
                CREATESTRUCT* cp;
                cp = (CREATESTRUCT*)lParam;
                SetWindowLongPtr(hWnd, 0, (WINDOW_LONG_PTR)cp->lpCreateParams);
                break;
        }
        case WM_DESTROY:
                PostQuitMessage(0);
                break;
        case WM_PAINT:{
                hdc = BeginPaint(hWnd, &ps);
                TCHAR* buf = (TCHAR*)GetWindowLongPtr(hWnd, 0);
                TextOut(hdc, 0, 0, buf, (int)_tcslen(buf));
                EndPaint(hWnd, &ps);
                break;
        }
        default:
                return(DefWindowProc(hWnd, msg, wParam, lParam));
        }
        return (0L);
}

//      ウィンドウを作成する
BOOL InitInstance(HINSTANCE hInst, TCHAR* szClassName, int nCmdShow,LPVOID para){
        HWND hWnd = CreateWindow(szClassName,
                _TEXT("タイトル"),
                WS_OVERLAPPEDWINDOW,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                NULL,
                NULL,
                hInst,
                (LPVOID)para);
        if (!hWnd)
                return FALSE;
        ShowWindow(hWnd, nCmdShow);
        UpdateWindow(hWnd);
        return TRUE;
}

ソースファイルと実行ファイルのダウンロード

ダウンロード createwindow2.zip(37.2kByte)

ZIPファイルに含まれるファイル
createwindow2.cpp
createwindow2.exe