Win32APIでresource(.rc)ファイルを使う方法をまとめる
最近、ゲーム開発の勉強の一環でWin32APIを使っている。
そこで今回は、resource(.rc)ファイルを使う方法をまとめていく。
開発環境
- Windows11
- MinGW-w64
- VSCode
Visual Studioの方が楽にできるようだが、宗教上の理由でVSCodeを使っている。
resource(.rc)ファイルとは?
主にWindowsアプリのアイコンやメニューを定義するためのファイル。
用意するのはresource.h
とresource.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_APPICON
とIDR_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公式ドキュメントはわかりずらいのでパス。