Win32APIでresource(.rc)ファイルを使う方法をまとめる

最近、ゲーム開発の勉強の一環でWin32APIを使っている。

そこで今回は、resource(.rc)ファイルを使う方法をまとめていく。

開発環境

  • Windows11
  • MinGW-w64
  • VSCode

Visual Studioの方が楽にできるようだが、宗教上の理由でVSCodeを使っている。

resource(.rc)ファイルとは?

主にWindowsアプリのアイコンやメニューを定義するためのファイル。

用意するのはresource.hresource.rcの2つ。(ファイル名は任意でよい)

resource.hには#defineでアイコンやメニューなどのIDを定義していく。これは任意の数字でよい。

ただし、以下のルールを守ること。

  • アイコンは101から始める(100以下はシステム側が予約済み)
  • IDは重複しないようにする

例えば、以下のような感じ。

// resource.h

// アイコン
#define IDI_APPICON         101

// メニュー
#define IDR_MAINMENU        102

// メニュー項目
#define IDM_EXIT            1001
#define IDM_ABOUT           1002

続いてresource.rc。 重要なのはIDI_APPICONIDR_MAINMENUの部分。

ただし、メニューはゲーム開発では不要かもしれない。

// resource.rc
#include "resource.h"
#include <windows.h>

LANGUAGE LANG_JAPANESE, SUBLANG_DEFAULT
#pragma code_page(65001)  // UTF-8

// アイコン(ファイルがある場合)
IDI_APPICON ICON "icon.ico"

// メニュー
IDR_MAINMENU MENU
BEGIN
    POPUP "File"
    BEGIN
        MENUITEM "ゲーム終了", IDM_EXIT
    END
    POPUP "Help"
    BEGIN
        MENUITEM "About", IDM_ABOUT
    END
END

では、一旦rcファイルをコンパイルしてみる。windresというrcファイル専用のコンパイラがあるので、それを使う。 (windresはMinGWに標準で入っている。)

windres -DUNICODE -D_UNICODE resource.rc -o resource.o

正常にコンパイルできたはず。

main.cppを作成してrcを組み込む

では、main.cppを作成していく。ちょっと長いがコードは以下の通り。

// UNICODE定義(必ず先頭に)
#ifndef UNICODE
#define UNICODE
#endif
#ifndef _UNICODE
#define _UNICODE
#endif

#include <windows.h>
#include "resource.h"

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine, int nCmdShow)
{
    // ウィンドウクラスの登録
    WNDCLASSEX wc = {0};
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.lpfnWndProc = WndProc;
    wc.hInstance = hInstance;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.lpszClassName = L"GameWindowClass";

    // リソースからアイコンとメニューを読み込み
    wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPICON));
    wc.lpszMenuName = MAKEINTRESOURCE(IDR_MAINMENU);

    RegisterClassEx(&wc);

    // ウィンドウ作成
    HWND hwnd = CreateWindowEx(
        0, 
        L"GameWindowClass", 
        L"オレオレハローワールドの世界です",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,
        NULL, NULL, hInstance, NULL
    );

    if (hwnd == NULL) {
        MessageBox(NULL, L"ウィンドウの作成に失敗しました", 
                   L"エラー", MB_OK | MB_ICONERROR);
        return 0;
    }

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    // メッセージループ
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return (int)msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg) {
        case WM_COMMAND:
            switch (LOWORD(wParam)) {
                case IDM_EXIT:
                    // 終了確認
                    if (MessageBox(hwnd, 
                        L"ゲームを終了しますか?", 
                        L"確認", 
                        MB_YESNO | MB_ICONQUESTION) == IDYES) {
                        PostQuitMessage(0);
                    }
                    break;

                case IDM_ABOUT:
                    MessageBox(hwnd, 
                        L"バージョン v1.0\n\nWin32APIで作成", 
                        L"バージョン情報", 
                        MB_OK | MB_ICONINFORMATION);
                    break;
            }
            break;

        case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hwnd, &ps);

            // 画面に文字を描画
            const wchar_t* text = L"こんにちは、ゲーム開発!";
            TextOut(hdc, 10, 10, text, lstrlen(text));

            EndPaint(hwnd, &ps);
        }
        break;

        case WM_DESTROY:
            PostQuitMessage(0);
            break;

        default:
            return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}

このコードでは、rcファイルで定義したアイコンとメニューを読み込んでいる。

これをコンパイルしてみる。

g++ -o hello.exe main.cpp resource.o -mwindows -lgdi32 -lwinmm -lole32 -luuid

無事にコンパイルできたら、実行してみる。

./hello.exe

すると、ウィンドウが表示され、メニューも表示されるはず。

参考文献

  • rcについての分かりやすい解説記事: リソース

Windows公式ドキュメントはわかりずらいのでパス。