Win32 APIでウィンドウを作成する
開発環境
- Visual Studio 2022
- Windows11
最低限覚えるべき概念
- ウィンドウクラス
- ウィンドウプロシージャ
- メッセージとメッセージキュー
ウィンドウクラス
ウィンドウクラスはウィンドウ生成における共通情報をまとめたテンプレート。 基本的にアプリケーションが起動時に作成 & 登録を行う。
アプリケーション終了時に登録が解除されるため、解除処理を書く必要はない。
参考: https://learn.microsoft.com/ja-jp/windows/win32/winmsg/window-classes
ウィンドウプロシージャ
ウィンドウプロシージャ(WndProc)は特定のクラスに属する全てのウィンドウからのメッセージを処理する関数。
メインウィンドウ用のWndProcは、WM_DESTROY
というメッセージを受け取った際に、PostQuitMessage
関数を実行するのが定石な気がする。
// ウィンドウプロシージャLRESULT CALLBACK MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_DESTROY: // メッセージキューにWM_QUITメッセージをエンキュー PostQuitMessage(0); break; } return DefWindowProc(hwnd, msg, wParam, lParam);}
参考: https://learn.microsoft.com/ja-jp/windows/win32/winmsg/window-procedures
メッセージとメッセージキュー
メッセージはシステム・アプリケーションから来るイベント通知。メッセージキューは、それらを一時的に格納するFIFOキュー。
キューを通さずウィンドウプロシージャが直接呼ばれることもある。
Windowsのデスクトップアプリでは、これらを以下のように組み合わせる。
参考: https://learn.microsoft.com/ja-jp/windows/win32/winmsg/about-messages-and-message-queues
ウィンドウの生成
ウィンドウの生成には、CreateWindowEx
関数を使用する。その際、以下の要素が必要になる。
- 拡張ウィンドウスタイル- ウィンドウクラス名- ウィンドウのタイトル- ウィンドウスタイル- ウィンドウの左上の座標 (x,y)- ウィンドウのサイズ (width, height)- 親ウィンドウのハンドル- メニューのハンドル- ウィンドウを追跡するためのインスタンスハンドル- 追加パラメータ
以下の4つはシンプルなウィンドウを作成する際には不要なのでNULLを指定するのがお勧め。
- 親ウィンドウのハンドル
- メニューのハンドル
- 追加パラメータ
また以下の4つはCW_USEDEFAULT
を指定するのが楽。
- ウィンドウの左上の座標 (x,y)
- ウィンドウのサイズ (width, height)
拡張ウィンドウスタイルとウィンドウスタイルは、とりあえず以下が無難。
- 拡張ウィンドウスタイル:
0
- ウィンドウスタイル:
WS_OVERLAPPEDWINDOW
興味がある場合はWS_OVERLAPPEDWINDOW
からWinUser.h
を開いて、どんなスタイルが定義されているか確認するのがお勧め。
実装
#include <Windows.h>#include <string>
// ウィンドウプロシージャLRESULT CALLBACK MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_DESTROY: // メッセージキューにWM_QUITメッセージをエンキュー PostQuitMessage(0); break; } return DefWindowProc(hwnd, msg, wParam, lParam);}
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd){ // ウィンドウクラスの生成と登録 WNDCLASS wc = {}; wc.lpfnWndProc = MainWndProc; wc.hInstance = GetModuleHandle(NULL); wc.lpszClassName = L"MainWindowClass";
RegisterClass(&wc);
// ウィンドウの生成 HWND hwnd; hwnd = CreateWindowEx( 0, L"MainWindowClass", L"Main Window", WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL );
// ウィンドウの表示 ShowWindow(hwnd, nShowCmd);
// メッセージループ while (true) { MSG msg = {};
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) { break; } TranslateMessage(&msg); DispatchMessage(&msg); }
Sleep(30); }
return 0;}