WindowsでC言語開発! マウスカーソルの動作の違いを確認する
●
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
●
サンプルコードの実行結果
実行すると次のようなシンプルなウィンドウが表示される。
![](https://image.news.livedoor.com/newsimage/stf/2/5/25f6b_1223_f95cb0c50cb91ed7f4470688e2905445.jpg)
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);
![](https://image.news.livedoor.com/newsimage/stf/c/a/ca905_1223_a6c3e17a155bd01aa71d8cfd0adffa7b.jpg)
実行サンプル - IDC_ARROW
マウスカーソル:十字 IDC_CROSS
wc.hCursor = LoadCursor(NULL, IDC_CROSS);
![](https://image.news.livedoor.com/newsimage/stf/f/1/f16ca_1223_6924c699f307274d381b29486e688a95.jpg)
実行サンプル - IDC_CROSS
マウスカーソル:手 IDC_HAND
wc.hCursor = LoadCursor(NULL, IDC_HAND);
![](https://image.news.livedoor.com/newsimage/stf/7/5/75ab7_1223_2455840c0952a3ac4feb492ba36e1fc3.jpg)
実行サンプル - IDC_HAND
マウスカーソル:北東と南西の二重矢印 IDC_SIZENESW
wc.hCursor = LoadCursor(NULL, IDC_SIZENESW);
![](https://image.news.livedoor.com/newsimage/stf/9/5/95e5b_1223_fa503e0c010fdaf1d1d8822d1f22b05e.jpg)
実行サンプル - IDC_SIZENESW
マウスカーソル:砂時計 IDC_WAIT
wc.hCursor = LoadCursor(NULL, IDC_WAIT);
![](https://image.news.livedoor.com/newsimage/stf/d/a/da058_1223_e392b2d58243202b1f0818e7aa0cb15f.jpg)
実行サンプル - 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
Windows APIを使ったウィンドウプログラミング
Windows APIを使ったウィンドウプログラミンを行うために、Microsoftの提供している次のチュートリアルに習ってシンプルなウィンドウアプリケーションの作成を行っている。
Module 1. Your First Windows Program - Win32 apps | Microsoft Docs
ただし、チュートリアルに掲載されているサンプルソースコードは、用意した開発環境ではビルドできないしウィンドウも表示されないので、多少変更を加えてある。今のところ、次のようなソースコードを使ってサンプルのウィンドウアプリケーションを作っている(コメントを増やしたり、ソースコードの内部配置を入れ替えたりと、説明しやすいように前回よりも手を加えてある)。
#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
●
サンプルコードの実行結果
実行すると次のようなシンプルなウィンドウが表示される。
![](https://image.news.livedoor.com/newsimage/stf/2/5/25f6b_1223_f95cb0c50cb91ed7f4470688e2905445.jpg)
チュートリアルのソースコードとしては長くなってきているが、基本的には次の流れで処理を行っているだけだ。
ウィンドウクラスの作成・登録
ウィンドウの作成・表示
ウィンドウメッセージループを実行
前回はこの「ウィンドウクラスの作成」について説明を行った。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);
![](https://image.news.livedoor.com/newsimage/stf/c/a/ca905_1223_a6c3e17a155bd01aa71d8cfd0adffa7b.jpg)
マウスカーソル:十字 IDC_CROSS
wc.hCursor = LoadCursor(NULL, IDC_CROSS);
![](https://image.news.livedoor.com/newsimage/stf/f/1/f16ca_1223_6924c699f307274d381b29486e688a95.jpg)
マウスカーソル:手 IDC_HAND
wc.hCursor = LoadCursor(NULL, IDC_HAND);
![](https://image.news.livedoor.com/newsimage/stf/7/5/75ab7_1223_2455840c0952a3ac4feb492ba36e1fc3.jpg)
マウスカーソル:北東と南西の二重矢印 IDC_SIZENESW
wc.hCursor = LoadCursor(NULL, IDC_SIZENESW);
![](https://image.news.livedoor.com/newsimage/stf/9/5/95e5b_1223_fa503e0c010fdaf1d1d8822d1f22b05e.jpg)
マウスカーソル:砂時計 IDC_WAIT
wc.hCursor = LoadCursor(NULL, IDC_WAIT);
![](https://image.news.livedoor.com/newsimage/stf/d/a/da058_1223_e392b2d58243202b1f0818e7aa0cb15f.jpg)
このように、ウィンドウクラスで指定するデフォルトのマウスカーソル指定を変更することで、ウィンドウ全体のマウスカーソルを変更できることがわかる。特定のエリアのマウスカーソルだけを変更したり、状態に応じてマウスカーソルを変更したりという操作をどのようにやるのかも、こうした部分に触れておくと理解しやすくなる。
なお、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