Windows APIを使ったウィンドウプログラミング

Windows APIを使ったウィンドウプログラミンを行うために、Microsoftの提供している次のチュートリアルに習ってシンプルなウィンドウアプリケーションの作成を行っている。

Module 1. Your First Windows Program - Win32 apps | Microsoft Docs

ただし、チュートリアルに掲載されているサンプルソースコードは、用意した開発環境ではビルドできないしウィンドウも表示されないので、多少変更を加えてある。今のところ、次のようなソースコードを使ってサンプルのウィンドウアプリケーションを作っている(コメントを増やしたり、ソースコードの内部配置を入れ替えたりと、説明しやすいように前回よりも手を加えてある)。

winmain.c

#ifndef UNICODE

#define UNICODE

#endif

#include

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

/*

* ウィンドウプログラムエントリポイント関数

*/

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)

{

// ウィンドウクラス名

const wchar_t CLASS_NAME[] = L"ウィンドウ作成の学習用プログラムクラス";

// ウィンドウタイトル

const wchar_t WINDOW_TITLE[] = L"ウィンドウプログラミング学習";

// ウィンドウクラス構造体を用意

WNDCLASS wc;

wc.style = CS_HREDRAW | CS_VREDRAW;

wc.lpfnWndProc = WindowProc;

wc.cbClsExtra = 0;

wc.cbWndExtra = 0;

wc.hInstance = hInstance;

wc.hIcon = NULL;

wc.hCursor = LoadCursor(NULL, IDC_ARROW);

wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);

wc.lpszMenuName = NULL;

wc.lpszClassName = CLASS_NAME;

// style ウィンドウクラススタイル

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

// cbClsExtra クラス構造体以降の追加確保分指定

// cbWndExtra ウィンドウインスタンス以降の追加確保分指定

// hInstance ウィンドウプロシージャインスタンス

// hIcon クラスアイコンハンドラ

// hCursor クラスカーソルハンドラ

// hbrBackground ウィンドウ背景色

// lpszMenuName クラスメニューのリソース名

// lpszClassName ウィンドウクラス名

// ウィンドウクラス構造体を登録

RegisterClass(&wc);

// ウィンドウを作成

HWND hwnd = CreateWindowEx(

0, // 拡張ウィンドウスタイル

CLASS_NAME, // ウィンドウクラス名

WINDOW_TITLE, // ウィンドウタイトル

WS_OVERLAPPEDWINDOW, // ウィンドウスタイル

// サイズとポジション

CW_USEDEFAULT, // X座標

CW_USEDEFAULT, // Y座標

1200, // 幅

800, // 高さ

NULL, // 親ウィンドウハンドラ

NULL, // メニューハンドラ

hInstance, // インスタンスハンドラ

NULL // 追加のアプリケーションデータ

);

if (hwnd == NULL)

{

return 0;

}

// ウィンドウを表示

ShowWindow(hwnd, nCmdShow);

// メッセージループを実行

MSG msg;

while (GetMessage(&msg, NULL, 0, 0))

{

TranslateMessage(&msg);

DispatchMessage(&msg);

}

return 0;

}

/*

* ウィンドウプロシージャ関数

*/

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)

{

switch (uMsg)

{

case WM_DESTROY:

PostQuitMessage(0);

return 0;

case WM_PAINT:

{

PAINTSTRUCT ps;

HDC hdc = BeginPaint(hwnd, &ps);

FillRect(hdc, &ps.rcPaint, (HBRUSH) (COLOR_WINDOW+1));

EndPaint(hwnd, &ps);

}

return 0;

}

return DefWindowProc(hwnd, uMsg, wParam, lParam);

}

ビルドするためのnmake用Makefileは次のとおりだ。

nmake用Makefile

CMD= winmain

CC= cl.exe

CFLAGS= /c /source-charset:utf-8

LINK= link.exe

LIBS= user32.lib

LFLAGS=

EXIST= cmd.exe /C if exist

all: build

build: $(CMD).exe

$(CMD).exe: $(CMD).obj

$(LINK) $(LFLAGS) $(LIBS) *.obj

dir

.c.o:

$(CC) $(CFLAGS) $*.c

run: build

$(CMD).exe

clean:

$(EXIST) $(CMD).obj del *.obj

$(EXIST) $(CMD).exe del $(CMD).exe

dir



サンプルコードの実行結果

実行すると次のようなシンプルなウィンドウが表示される。

winmain.exeの実行サンプル

チュートリアルのソースコードとしては長くなってきているが、基本的には次の流れで処理を行っているだけだ。

ウィンドウクラスの作成・登録

ウィンドウの作成・表示

ウィンドウメッセージループを実行

前回はこの「ウィンドウクラスの作成」について説明を行った。Windows APIでは「ウィンドウクラス」といっても実態はC++のクラスではなく、WNDCLASSという構造体だ。この構造体に各種データやポインタを配置しておいて、ウィンドウの共有データとして利用する。

上記のサンプルソースコードで、該当する部分は次の部分だ。

ウィンドウクラス構造体にデータを設定

// ウィンドウクラス構造体を用意

WNDCLASS wc;

wc.style = CS_HREDRAW | CS_VREDRAW;

wc.lpfnWndProc = WindowProc;

wc.cbClsExtra = 0;

wc.cbWndExtra = 0;

wc.hInstance = hInstance;

wc.hIcon = NULL;

wc.hCursor = LoadCursor(NULL, IDC_ARROW);

wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);

wc.lpszMenuName = NULL;

wc.lpszClassName = CLASS_NAME;

ウィンドウクラス構造体には、複数のウィンドウに共通する動作セットがまとめられている。このアプリケーションで表示されるウィンドウは、共通の動作としてこのウィンドウクラス構造体の記述に従うということだ。

まずは、この構造体に設定している内容を書き換えることで、どのような違いが出るのかを試して体感してみてほしい。今回は、わかりやすい例として、次の行を書き換えることで動作を変えてみる。

基本となるマウスカーソル(矢印)を指定

wc.hCursor = LoadCursor(NULL, IDC_ARROW);

上記コードはマウスカーソルとして「矢印」のマウスカーソルを指定している。Windowsで最も基本的なマウスカーソルだ。

○カーソルの書き換えと実行サンプル

もう一度、該当するソースコードを見てみよう。

基本となるマウスカーソルを指定

wc.hCursor = LoadCursor(NULL, IDC_ARROW);

「IDC_ARROW」が、マウスカーソルとして「矢印」を指定している部分だ。この部分を変更すると別のマウスカーソルになる。

指定できるマウスカーソルは、次の通りとなっている(参考「LoadCursorA function (winuser.h) - Win32 apps | Microsoft Docs」)。

シンプルな書き換えだが、実際に起きる変化がひと目でわかる。書き換えの演習としては取り組みやすく結果もわかりやすい。早速書き換えてビルドし、実行結果を見てみよう。

マウスカーソル:矢印 IDC_ARROW

wc.hCursor = LoadCursor(NULL, IDC_ARROW);

実行サンプル - IDC_ARROW

マウスカーソル:十字 IDC_CROSS

wc.hCursor = LoadCursor(NULL, IDC_CROSS);

実行サンプル - IDC_CROSS

マウスカーソル:手 IDC_HAND

wc.hCursor = LoadCursor(NULL, IDC_HAND);

実行サンプル - IDC_HAND

マウスカーソル:北東と南西の二重矢印 IDC_SIZENESW

wc.hCursor = LoadCursor(NULL, IDC_SIZENESW);

実行サンプル - IDC_SIZENESW

マウスカーソル:砂時計 IDC_WAIT

wc.hCursor = LoadCursor(NULL, IDC_WAIT);

実行サンプル - IDC_WAIT

このように、ウィンドウクラスで指定するデフォルトのマウスカーソル指定を変更することで、ウィンドウ全体のマウスカーソルを変更できることがわかる。特定のエリアのマウスカーソルだけを変更したり、状態に応じてマウスカーソルを変更したりという操作をどのようにやるのかも、こうした部分に触れておくと理解しやすくなる。

なお、IDC_WAITで表示されるマウスカーソルは以前は砂時計だったが、現在のデザインはもう砂時計ではなくなっている。処理に時間がかかっていることを示すマウスカーソルは、「ローディング中」を意味するようなアニメーションアイコンに変わっている。この辺りはオペレーティングが表示するものなので、オペレーティングシステムの使いが変わると表示されるマウスカーソルも変わることになる。



ウィンドウクラス構造体はよくいじっておこう

今回はマウスカーソルを変えたが、特性の指定という点ではstyleメンバーのほうがわかりやすいだろう。複数の挙動やモードのようなものが用意されており、それらをOR (|)でつなげて指定する。この指定はウィンドウプログラミングの至るところで見られるものだ。それぞれに意味があるので、その振る舞いを把握することが大切になってくる。

では、もう少しこの構造体をいじっていく。ウィンドウプログラミングを進めるにあたって最初の大切な部分なので、じっくりと遊んでいこう。

○参考

Module 1. Your First Windows Program - Win32 apps | Microsoft Docs

WNDCLASSA (winuser.h) - Win32 apps | Microsoft Docs

Window Class Styles (Winuser.h) - Win32 apps | Microsoft Docs

LoadCursorA function (winuser.h) - Win32 apps | Microsoft Docs

GetSysColor function (winuser.h) - Win32 apps | Microsoft Docs