WinAPIとは、Windowsの機能をプログラムから直接操作するための関数群です。
ウィンドウの表示、ファイル操作、描画、入力、ネットワークなど様々なアプリケーションを作成するための強力なツールとなります。
本記事はWinAPIを使用したプログラミングの第一歩として、Visual Studioで空のアプリケーションを選択してウィンドウを表示させるまでの手順を紹介します。
想定環境
- Windows10以降
- Visual Studio2022(C++デスクトップが有効)
- Windows 10 SDK(Visual Studio2022と同時にインストールされるので問題なし)
前提知識
-
C/C++の基本文法を理解している
プロジェクトのセットアップ
まず、Visual Studio2022を開き、画面右側の新しいプロジェクトの作成をクリックしてください。

空のプロジェクトを選択して、画面右下の次へをクリックしてください。

プロジェクト名を決めて、画面右下の作成をクリックしてください。
画面右側のソリューションエクスプローラーから、C++ファイルを追加してください。

画面上部のGUIからプロジェクト->プロパティをクリックしてプロパティウィンドウを開きます。

プロパティウィンドウから構成をすべての構成にします。

プラットフォームをすべてのプラットフォームに変更。

プロパティウィンドウ左側の構成プロパティからリンカー->システムを選択して、サブシステムを/SUBSYSTEM:WINDOWSにしてください。

プロパティウィンドウ下部の適用をクリックして、変更を適用します。

コード例
セットアップが完了したので、コードを書いていきます。
メッセージボックスの表示
まずは、メッセージボックスを表示できるか試してみたいと思います。
#include <Windows.h>
//--------------------------------------------------------------
// アプリケーションのエントリポイント
//! @param hInstance アプリケーションインスタンス
//! @param hPrevInstance 非推奨(常にNULL)
//! @param lpCmdLine コマンドライン引数
//! @param nCmdShow ウィンドウ表示状態(例:SW_SHOW)
//! @return 終了コード(通常は0)
//--------------------------------------------------------------
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
// メッセージボックスを表示
MessageBox(nullptr,L"HelloWorld",L"メッセージボックス",MB_OK);
return 0; //< 正常終了
}
実行結果

ひとまず、メッセージボックスを表示することに成功しました。
WinMain関数とは、GUIアプリケーションにおける開始地点で、OSが呼び出す関数となります。
引数は固定で、使用する際に随時説明します。
//--------------------------------------------------------------
// アプリケーションのエントリポイント
//! @param hInstance アプリケーションインスタンス
//! @param hPrevInstance 非推奨(常にNULL)
//! @param lpCmdLine コマンドライン引数
//! @param nCmdShow ウィンドウ表示状態(例:SW_SHOW)
//! @return 終了コード(通常は0)
//--------------------------------------------------------------
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
//--------------------------------------------------------------
// 中略
//--------------------------------------------------------------
return 0; //< 正常終了
}
MessageBoxは、Windowsの標準ダイアログボックスを表示するためのWinAPI関数です。
引数は以下の通りになります。
| 引数 | 型 | 内容 | 説明 |
|---|---|---|---|
hWnd |
HWND |
親ウィンドウハンドル | 独立したダイアログならnullptr で省略可能。親ウィンドウがある場合はそのハンドルを指定。 |
lpText |
LPCWSTR |
メッセージ本文 | ダイアログ中央に表示される文字列。Unicode文字列(L"...")を使う。 |
lpCaption |
LPCWSTR |
タイトル | ダイアログのタイトルバーに表示される文字列。 |
uType |
UINT |
ボタン・アイコンの種類 |
MB_OK, MB_YESNO, MB_ICONINFORMATION などを指定可能。 |
// メッセージボックスを表示
MessageBox(nullptr,L"HelloWorld",L"メッセージボックス",MB_OK);
本コードではMB_OKとしたので、OKボタンが表示されるメッセージボックスが表示されました。
ここで正しく表示された場合、セットアップが正しく完了しています。
ウィンドウを表示させる関数
次に、ウィンドウを表示させるコードを記述します。
#include <Windows.h>
//--------------------------------------------------------------
/// ウィンドウプロシージャ:各種メッセージを処理する関数
//! @param hwnd ウィンドウハンドル
//! @param msg メッセージID(例:WM_DESTROY)
//! @param wParam メッセージの追加情報(unsigned)
//! @param lParam メッセージの追加情報(long)
//! @return 処理結果(DefWindowProcまたは0)
//--------------------------------------------------------------
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_DESTROY:
PostQuitMessage(0); ///< アプリケーション終了メッセージを送信
return 0;
}
return DefWindowProc(hwnd, msg, wParam, lParam); ///< その他のメッセージはデフォルト処理
}
//--------------------------------------------------------------
// アプリケーションのエントリポイント
//! @param hInstance アプリケーションインスタンス
//! @param hPrevInstance 非推奨(常にNULL)
//! @param lpCmdLine コマンドライン引数
//! @param nCmdShow ウィンドウ表示状態(例:SW_SHOW)
//! @return 終了コード(通常は0)
//--------------------------------------------------------------
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
// ウィンドウクラス名(任意の識別子)
const wchar_t CLASS_NAME[] = L"MyWindowClass";
/// ウィンドウクラス構造体の初期化
WNDCLASS wc = {}; //ウィンドウクラスの宣言
wc.lpfnWndProc = WndProc; //< メッセージ処理関数
wc.hInstance = hInstance; //< アプリケーションインスタンス
wc.lpszClassName = CLASS_NAME; //< クラス名
RegisterClass(&wc); //< ウィンドウクラスを登録
/// ウィンドウの作成
HWND hwnd = CreateWindowEx(
0, //< 拡張スタイルなし
CLASS_NAME, //< クラス名
L"Hello WinAPI", //< ウィンドウタイトル
WS_OVERLAPPEDWINDOW, //< 標準的なウィンドウスタイル
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, //< 位置とサイズ(自動)
nullptr, nullptr, hInstance, nullptr //< 親ウィンドウ、メニュー、インスタンス、追加情報
);
if (hwnd == nullptr) {
return 0; //< ウィンドウ作成失敗時は終了
}
ShowWindow(hwnd, nCmdShow); //< ウィンドウを表示
// メッセージループ:ユーザー操作やシステムイベントを処理
MSG msg = {};
while (GetMessage(&msg, nullptr, 0, 0)) {
DispatchMessage(&msg); //< メッセージをWndProcへ送信
}
return 0; //< 正常終了
}
実行結果
一気にコードが複雑になった気がします。
順を追ってコードについて説明します。
ウィンドウクラス
まず、ウィンドウクラスとは、ウィンドウの見た目や動作、メッセージ処理方法などをまとめた構造体です、これを設計図にしてウィンドウを作成します。
本記事では、メッセージ処理関数、アプリケーションインスタンス、ウィンドウクラスの名前を初期化しています。
| メンバー名 | 役割 | 本記事での設定値 |
|---|---|---|
lpfnWndProc |
メッセージ処理関数。WndProcを指定することで、ウィンドウに届いたイベントを処理できる |
WndProc |
hInstance |
アプリケーションのインスタンス。リソース読み込みなどに使われる識別子 |
hInstance(WinMainから受け取る) |
lpszClassName |
ウィンドウクラスの名前。CreateWindowでこの名前を使ってウィンドウを作成する | L"MyWindowClass" |
hInstanceは、WinMain関数の第一引数だったものです。
これをウィンドウクラスの初期化に使用することで、リソースの読み込みなどに使用します。
// ウィンドウクラス名(任意の識別子)
const wchar_t CLASS_NAME[] = L"MyWindowClass";
/// ウィンドウクラス構造体の初期化
WNDCLASS wc = {}; //ウィンドウクラスの宣言
wc.lpfnWndProc = WndProc; //< メッセージ処理関数
wc.hInstance = hInstance; //< アプリケーションインスタンス
wc.lpszClassName = CLASS_NAME; //< クラス名
メッセージ処理関数
メッセージ処理関数(WndProc)とは、Windowsアプリケーションに届く命令を受け取り、処理を行う関数です。
wc.lpfnWndProcには、このWndProcを代入します。
引数は固定で以下の通りとなっています。
| 引数名 | 型 | 説明 |
|---|---|---|
hwnd |
HWND |
メッセージを受け取ったウィンドウのハンドル。どのウィンドウ宛かを識別する。 |
msg |
UINT |
メッセージID。イベントの種類(例:WM_DESTROY, WM_PAINT)を表す。 |
wParam |
WPARAM |
メッセージに付随する追加情報(数値やフラグなど)。意味はメッセージによって異なる。 |
lParam |
LPARAM |
メッセージに付随する追加情報(座標やポインタなど)。意味はメッセージによって異なる。 |
//--------------------------------------------------------------
/// ウィンドウプロシージャ:各種メッセージを処理する関数
//! @param hwnd ウィンドウハンドル
//! @param msg メッセージID(例:WM_DESTROY)
//! @param wParam メッセージの追加情報(unsigned)
//! @param lParam メッセージの追加情報(long)
//! @return 処理結果(DefWindowProcまたは0)
//--------------------------------------------------------------
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_DESTROY:
PostQuitMessage(0); ///< アプリケーション終了メッセージを送信
return 0;
}
return DefWindowProc(hwnd, msg, wParam, lParam); ///< その他のメッセージはデフォルト処理
}
第二引数のメッセージIDが重要で、ユーザーがどのような操作を行ったかが送信されてきます。
WM_DESTROYとは、ウィンドウの破棄を表すマクロで、本コードではこれが送られてきた場合、PostQuitMessage(0);で、Windowsアプリケーションを終了させるための合図をメッセージループに送っています。
それ以外の場合は、Windowsに既定の動作を行わせるために、DefWindowProc(hwnd, msg, wParam, lParam);を返しています。
ウィンドウクラスの登録
ウィンドウクラスの初期化が完了したら、登録を行います。
RegisterClass();に引数としてウィンドウクラスの参照を渡すことで、登録が完了します。
この作業を行わずにウィンドウを作成すると、失敗してしまいます。
RegisterClass(&wc); //< ウィンドウクラスを登録
ウィンドウの作成
CreateWindowEx()を呼ぶことで、ウィンドウを作成することができます。
引数は以下の通りです。
| 引数名 | 内容 | 説明 |
|---|---|---|
0 |
拡張スタイル |
WS_EX_ から始まる拡張スタイル。ここでは未使用(0) |
CLASS_NAME |
クラス名 |
RegisterClass で登録したウィンドウクラス名 |
"Hello WinAPI" |
ウィンドウタイトル | タイトルバーに表示される文字列 |
WS_OVERLAPPEDWINDOW |
スタイル | 枠付き・タイトル付きの標準ウィンドウスタイル |
CW_USEDEFAULT ×4 |
位置とサイズ | OSに任せて自動決定させる指定値 |
nullptr ×4 |
親ウィンドウ、メニュー、インスタンス、追加情報 | 今回はすべて未使用(最小構成) |
/// ウィンドウの作成
HWND hwnd = CreateWindowEx(
0, //< 拡張スタイルなし
CLASS_NAME, //< クラス名
L"Hello WinAPI", //< ウィンドウタイトル
WS_OVERLAPPEDWINDOW, //< 標準的なウィンドウスタイル
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, //< 位置とサイズ(自動)
nullptr, nullptr, hInstance, nullptr //< 親ウィンドウ、メニュー、インスタンス、追加情報
);
if (hwnd == nullptr) {
return 0; //< ウィンドウ作成失敗時は終了
}
戻り値としてハンドルが返ってくるので、失敗していたら終了するなどのエラーハンドリングを行ってください。
ウィンドウの表示
まだウィンドウを作成しただけで、これだけでは画面に何も表示されません。
ShowWindow()を呼び出すことで、画面にウィンドウを表示させることができます。
引数は以下の通りです。
| 引数名 | 型 | 説明 |
|---|---|---|
hwnd |
HWND |
表示対象のウィンドウハンドル。CreateWindowEx で取得したもの。 |
nCmdShow |
int |
表示方法を指定するフラグ。WinMain の引数として渡される。 |
第二引数はWinMainの第4引数としてOSから渡されるものです。
ShowWindow(hwnd, nCmdShow); //< ウィンドウを表示
メッセージループ
OSから送られてくるクリックやキー入力などをメッセージといい、それの受け取りをループさせてWndProcに渡して処理させる流れをメッセージループといいます。
これを書かないと、ウィンドウが開いた瞬間にプログラムが終了します。
// メッセージループ:ユーザー操作やシステムイベントを処理
MSG msg = {};
while (GetMessage(&msg, nullptr, 0, 0)) {
DispatchMessage(&msg); //< メッセージをWndProcへ送信
}
GetMessage()は、Windowsのメッセージキューから1件のメッセージを取り出す関数です。 この関数が返すメッセージをDispatchMessage()に渡すことで、ユーザー操作やシステムイベントを処理する流れが成立します。
引数は以下の通りです。
| 引数名 | 型 | 説明 |
|---|---|---|
lpMsg |
LPMSG |
メッセージ情報を格納する構造体へのポインタ。MSG 構造体に取得結果が格納される。 |
hWnd |
HWND |
対象ウィンドウのハンドル。nullptr を指定すると、すべてのウィンドウが対象になる。 |
wMsgFilterMin |
UINT |
メッセージIDの下限。0を指定するとフィルタなし(すべてのメッセージを対象)。 |
wMsgFilterMax |
UINT |
メッセージIDの上限。0を指定するとフィルタなし(すべてのメッセージを対象)。 |
ループを終了する際に、GetMessage()がfalseを返します。
DispatchMessage()を呼ぶことで、GetMessage()で取得したメッセージをWndProc()へ渡すことができます。
この関数を呼ぶことで、OSからのメッセージを適切に処理することができます。
総括
-
WinAPIはデスクトップアプリを作るための強力なツール -
WinAPIではWinMain()関数がエントリポイントとなる。 -
ウィンドウクラスが設計図となってウィンドウを作成する。 -
WndProcがユーザーからの入力情報(メッセージ)を処理する関数となる。 -
メッセージループでDispatchMessageを呼ぶことで、WndProcにメッセージを渡すことができる。
