Cプログラミンの一環として始めた Windos API ですが、やってみたら面白かったので、もっと詳しく、かつ簡単に、ステップバイステップで学習するコーナーも作りました。
Windows API Primer
Primer は、入門書という意味です。
このサイトでは、ユーザーが操作できるGUI部品のことをコントロールと呼ぶことにしています。
Windows APIでは、ボタンなどのコントロールもウィンドウです。
ほとんどのコントロールは、WNDCLASSがデフォルトで登録されていますので、CreateWindow関数ですぐに作成できます。
#include <windows.h>
#define BEEP 100
#define QUIT 200
#define EDIT 300
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain (HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
WNDCLASS wc;
HWND hwnd;
MSG msg;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH) (COLOR_APPWORKSPACE + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = TEXT("ButtonControl");
RegisterClass(&wc);
hwnd = CreateWindow (TEXT("ButtonControl"),
TEXT("Button"),
WS_OVERLAPPEDWINDOW,
(GetSystemMetrics(SM_CXSCREEN) - 400) / 2,
(GetSystemMetrics(SM_CYSCREEN) - 250) / 2,
400,
250,
NULL,
NULL,
hInstance,
NULL);
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam)
{
static HWND beep, quit, edit;
RECT clientRc;
static HFONT font;
switch (msg)
{
case WM_CREATE:
GetClientRect(hwnd, &clientRc);
font = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
beep = CreateWindow(TEXT("Button"),
TEXT("ビープ"),
WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
(clientRc.right - 170),
(clientRc.bottom - 30),
80, 25,
hwnd,
(HMENU) BEEP,
NULL,
NULL);
SendMessage(beep, WM_SETFONT, (WPARAM)font, MAKELPARAM(FALSE, 0));
quit = CreateWindow(TEXT("Button"),
TEXT("終 了"),
WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
(clientRc.right - 85),
(clientRc.bottom - 30),
80, 25,
hwnd,
(HMENU) QUIT,
NULL,
NULL);
SendMessage(quit, WM_SETFONT, (WPARAM)font, MAKELPARAM(FALSE, 0));
edit = CreateWindow(TEXT("Edit"),
NULL,
WS_VISIBLE | WS_CHILD |WS_BORDER | ES_MULTILINE,
5, 5,
(clientRc.right - 10),
(clientRc.bottom - 40),
hwnd,
(HMENU) EDIT, // 今回の場合はNULLでも良い
NULL,
NULL);
SendMessage(edit, WM_SETFONT, (WPARAM)font, MAKELPARAM(FALSE, 0));
break;
case WM_SIZE:
GetClientRect(hwnd, &clientRc);
SetWindowPos(beep, HWND_TOP,
(clientRc.right - 170),
(clientRc.bottom - 30), 0, 0, SWP_NOSIZE);
SetWindowPos(quit, HWND_TOP,
(clientRc.right - 85),
(clientRc.bottom - 30), 0, 0, SWP_NOSIZE);
SetWindowPos(edit, HWND_TOP,
0, 0,
(clientRc.right - 10),
(clientRc.bottom - 40),
SWP_NOMOVE);
break;
case WM_COMMAND:
if (LOWORD(wParam) == BEEP) MessageBeep(MB_OK);
if (LOWORD(wParam) == QUIT) PostQuitMessage(0);
break;
case WM_DESTROY:
DeleteObject(font);
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain (HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
WNDCLASS wc;
HWND hwnd;
MSG msg;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance,
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpszMenuName = NULL;
wc.lpszClassName = TEXT("CheckBox");
RegisterClass(&wc);
hwnd = CreateWindow (TEXT("CheckBox"),
TEXT("Check box"),
WS_OVERLAPPEDWINDOW,
(GetSystemMetrics(SM_CXSCREEN) - 400) / 2,
(GetSystemMetrics(SM_CYSCREEN) - 250) / 2,
400,
250,
NULL,
NULL,
hInstance,
NULL);
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam)
{
static HWND check;
RECT clrc;
static HFONT font;
UCHAR checked = 1;
switch (msg)
{
case WM_CREATE:
GetClientRect(hwnd, &clrc);
font = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
check = CreateWindow(TEXT("Button"),
TEXT("Show title"),
WS_VISIBLE | WS_CHILD | BS_CHECKBOX,
(clrc.right - 70) / 2,
(clrc.bottom - 20) / 2,
70, 20,
hwnd,
(HMENU) 1,
NULL,
NULL);
CheckDlgButton(hwnd, 1, BST_CHECKED);
SendMessage(check, WM_SETFONT, (WPARAM)font,
MAKELPARAM(FALSE, 0));
break;
case WM_SIZE:
GetClientRect(hwnd, &clrc);
SetWindowPos(check, HWND_TOP,
(clrc.right - 70) / 2,
(clrc.bottom - 20) / 2,
0, 0, SWP_NOSIZE);
break;
case WM_COMMAND:
checked = IsDlgButtonChecked(hwnd, 1);
if (checked)
{
CheckDlgButton(hwnd, 1, BST_UNCHECKED);
SetWindowText(hwnd, TEXT(""));
} else {
CheckDlgButton(hwnd, 1, BST_CHECKED);
SetWindowText(hwnd, TEXT("Check box"));
}
break;
case WM_DESTROY:
DeleteObject(font);
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
Windows APIでは、スライダーのことをトラックバーと呼びます。
トラックバーは、コモンコントロールというグループに所属しています。コモンコントロールには、ツールバー・ステータスバー、リストビュー・ツリービューなども所属しています。
Visual Studioでコモンコントールを使うには、次のようなコードが必要になります。
// ヘッダーの読み込み
#include <commctrl.h>
// ライブラリーへのリンク
#pragma comment(lib, "ComCtl32.lib")
// コモンコントロールの初期化
InitCommonControls();
しかし、Msys2のMinGWのGNUコンパイラを使う場合は、ヘッダーの読み込みだけでOKです。
#include <windows.h>
#include <commctrl.h>
#include <stdio.h> // for snprintf
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain (HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
WNDCLASS wc;
HWND hwnd;
MSG msg;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpszMenuName = NULL;
wc.lpszClassName = TEXT("TrackBar");
RegisterClass (&wc);
hwnd = CreateWindow (TEXT("TrackBar"),
TEXT("TrackBar 50"),
WS_OVERLAPPEDWINDOW,
(GetSystemMetrics(SM_CXSCREEN) - 400) / 2,
(GetSystemMetrics(SM_CYSCREEN) - 250) / 2,
400,
250,
NULL,
NULL,
hInstance,
NULL);
ShowWindow (hwnd, nCmdShow);
UpdateWindow (hwnd);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage (&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static HWND trackbar;
RECT clientRect;
static HFONT font;
int Pos;
char str[16];
switch(msg)
{
case WM_CREATE:
/* コモンコントロールの初期化 */
//InitCommonControls(); // なぜかいらない、逆に記述するとエラーになる
GetClientRect(hwnd, &clientRect);
trackbar =
CreateWindow (TEXT("msctls_trackbar32"), // TRACKBAR_CLASSでもOK
TEXT(""), WS_VISIBLE | WS_CHILD | TBS_AUTOTICKS,
//TBS_TOOLTIPS これも記述するとトラックバーのつまみの位置に現在の値が表示される
(clientRect.right - 360) / 2,
(clientRect.bottom - 20) / 2,
360, 20,
hwnd, (HMENU) 100, NULL, NULL);
SendMessage(trackbar, TBM_SETRANGE, TRUE, MAKELPARAM(0, 100));
SendMessage(trackbar, TBM_SETTICFREQ, 10, 0);
SendMessage(trackbar, TBM_SETPOS, TRUE, 50);
SendMessage(trackbar, TBM_SETPAGESIZE, 0, 10);
break;
case WM_SIZE:
GetClientRect(hwnd, &clientRect);
SetWindowPos(trackbar, HWND_TOP,
20, (clientRect.bottom - 20) / 2,
(clientRect.right - 40), 20,
SWP_NOZORDER); // SWP_SHOWWINDOWでも良い
break;
case WM_HSCROLL:
Pos = SendMessage(trackbar, TBM_GETPOS, 0, 0);
snprintf(str, -1, "TrackBar %d", Pos);
SetWindowText(hwnd, TEXT(str));
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
ラジオボタンは複数の項目の中から、一つだけを選択するに使われます。
#include <windows.h>
#define ID_BLUE 1
#define ID_YELLOW 2
#define ID_ORANGE 3
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hinst;
COLORREF g_color;
int WINAPI WinMain (HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLite,
int nCmdShow)
{
WNDCLASS wc;
HWND hwnd;
MSG msg;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpszMenuName = NULL;
wc.lpszClassName = TEXT("RadioButton");
g_hinst = hInstance;
RegisterClass(&wc);
hwnd = CreateWindow (TEXT("RadioButton"),
TEXT("Radio Button"),
WS_OVERLAPPEDWINDOW,
(GetSystemMetrics(SM_CXSCREEN) - 400) / 2,
(GetSystemMetrics(SM_CYSCREEN) - 250) / 2,
400,
250,
NULL,
NULL,
hInstance,
NULL);
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam)
{
RECT clientRect;
static HFONT font;
static HWND box, blue, yellow, orange;
HDC hdc;
PAINTSTRUCT ps;
HBRUSH hBrush, holdBrush;
HPEN hPen, holdPen;
switch(msg)
{
case WM_CREATE:
GetClientRect(hwnd, &clientRect);
font = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
box = CreateWindow(TEXT("Button"), TEXT("Choose color"),
WS_CHILD | WS_VISIBLE | BS_GROUPBOX,
10, 10, 120, (clientRect.bottom - 20), hwnd, (HMENU) 0, g_hinst, NULL);
blue = CreateWindow(TEXT("Button"), TEXT("Blue"),
WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON,
20, 30, 100, 30, hwnd, (HMENU) ID_BLUE, g_hinst, NULL);
yellow = CreateWindow(TEXT("Button"), TEXT("Yellow"),
WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON,
20, 55, 100, 30, hwnd, (HMENU) ID_YELLOW, g_hinst, NULL);
orange = CreateWindow(TEXT("Button"), TEXT("Orange"),
WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON,
20, 80, 100, 30, hwnd, (HMENU) ID_ORANGE, g_hinst, NULL);
SendMessage(box, WM_SETFONT, (WPARAM)font, MAKELPARAM(FALSE, 0));
SendMessage(blue, WM_SETFONT, (WPARAM)font, MAKELPARAM(FALSE, 0));
SendMessage(yellow, WM_SETFONT, (WPARAM)font, MAKELPARAM(FALSE, 0));
SendMessage(orange, WM_SETFONT, (WPARAM)font, MAKELPARAM(FALSE, 0));
break;
case WM_SIZE:
GetClientRect(hwnd, &clientRect);
SetWindowPos(box, HWND_TOP,
0, 0, 120, (clientRect.bottom - 20), SWP_NOMOVE);
case WM_COMMAND:
if (HIWORD(wParam) == BN_CLICKED)
{
switch (LOWORD(wParam))
{
case ID_BLUE:
g_color = RGB(0, 76, 255);
break;
case ID_YELLOW:
g_color = RGB(255, 255, 0);
break;
case ID_ORANGE:
g_color = RGB(255, 123, 0);
break;
}
InvalidateRect(hwnd, NULL, TRUE);
}
break;
case WM_PAINT:
GetClientRect(hwnd, &clientRect);
hdc = BeginPaint(hwnd, &ps);
hBrush = CreateSolidBrush(g_color);
hPen = CreatePen(PS_NULL, 1, RGB(0, 0, 0));
holdPen = SelectObject(hdc, hPen);
holdBrush = (HBRUSH) SelectObject(hdc, hBrush);
Rectangle(hdc, 140, 15, (clientRect.right - 10),
(clientRect.bottom - 10));
SelectObject(hdc, holdBrush);
SelectObject(hdc, holdPen);
DeleteObject(hPen);
DeleteObject(hBrush);
EndPaint(hwnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
トグルボタンは、押された状態と、押されていない状態があるボタンです。
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hinst;
COLORREF g_color;
int WINAPI WinMain (HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
WNDCLASS wc;
HWND hwnd;
MSG msg;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpszMenuName = NULL;
wc.lpszClassName = TEXT("ToggleButton");
g_hinst = hInstance;
RegisterClass(&wc);
hwnd = CreateWindow (TEXT("ToggleButton"),
TEXT("ToggleButton"),
WS_OVERLAPPEDWINDOW,
(GetSystemMetrics(SM_CXSCREEN) - 400) / 2,
(GetSystemMetrics(SM_CYSCREEN) - 250) / 2,
400,
250,
NULL,
NULL,
hInstance,
NULL);
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage (&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static HWND red, green, blue;
static HFONT font;
static UCHAR r = 0;
static UCHAR g = 0;
static UCHAR b = 0;
UCHAR toggled = 1;
HDC hdc;
PAINTSTRUCT ps;
HBRUSH hBrush, holdBrush;
HPEN hPen, holdPen;
#define RED 1
#define GREEN 2
#define BLUE 3
switch (msg)
{
case WM_CREATE:
font = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
red = CreateWindow(TEXT("Button"),
TEXT("Red"),
WS_VISIBLE | WS_CHILD | BS_CHECKBOX | BS_PUSHLIKE,
20, 20,
100, 30,
hwnd,
(HMENU) RED,
NULL,
NULL);
green = CreateWindow(TEXT("Button"),
TEXT("Green"),
WS_VISIBLE | WS_CHILD | BS_CHECKBOX | BS_PUSHLIKE,
20, 70,
100, 30,
hwnd,
(HMENU) GREEN,
NULL,
NULL);
blue = CreateWindow(TEXT("Button"),
TEXT("Blue"),
WS_VISIBLE | WS_CHILD | BS_CHECKBOX | BS_PUSHLIKE,
20, 120,
100, 30,
hwnd,
(HMENU) BLUE,
NULL,
NULL);
CheckDlgButton(hwnd, RED, BST_UNCHECKED);
CheckDlgButton(hwnd, GREEN, BST_UNCHECKED);
CheckDlgButton(hwnd, BLUE, BST_UNCHECKED);
SendMessage(red, WM_SETFONT, (WPARAM)font, MAKELPARAM(FALSE, 0));
SendMessage(green, WM_SETFONT, (WPARAM)font, MAKELPARAM(FALSE, 0));
SendMessage(blue , WM_SETFONT, (WPARAM)font, MAKELPARAM(FALSE, 0));
break;
case WM_COMMAND:
if (LOWORD(wParam) == RED)
{
toggled = IsDlgButtonChecked(hwnd, RED);
if (toggled)
{
CheckDlgButton(hwnd, RED, BST_UNCHECKED);
r = 0;
} else {
CheckDlgButton(hwnd, RED, BST_CHECKED);
r = 255;
}
}
if (LOWORD(wParam) == GREEN)
{
toggled = IsDlgButtonChecked(hwnd, GREEN);
if (toggled)
{
CheckDlgButton(hwnd, GREEN, BST_UNCHECKED);
g = 0;
} else {
CheckDlgButton(hwnd, GREEN, BST_CHECKED);
g = 255;
}
}
if (LOWORD(wParam) == BLUE)
{
toggled = IsDlgButtonChecked(hwnd, BLUE);
if (toggled)
{
CheckDlgButton(hwnd, BLUE, BST_UNCHECKED);
b = 0;
} else {
CheckDlgButton(hwnd, BLUE, BST_CHECKED);
b = 255;
}
}
g_color = RGB(r, g, b);
InvalidateRect(hwnd, NULL, TRUE);
break;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
hBrush = CreateSolidBrush(g_color);
hPen = CreatePen(PS_NULL, 1, RGB(r, g, b));
holdPen = SelectObject(hdc, hPen);
holdBrush = (HBRUSH) SelectObject(hdc, hBrush);
Rectangle(hdc, 140, 20, 365, 190);
SelectObject(hdc, holdBrush);
SelectObject(hdc, holdPen);
DeleteObject(hPen);
DeleteObject(hBrush);
EndPaint(hwnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
red = CreateWindow(TEXT("Button"),
TEXT("Red"),
WS_VISIBLE | WS_CHILD | BS_CHECKBOX | BS_PUSHLIKE,
・・・
if (LOWORD(wParam) == RED)
{
toggled = IsDlgButtonChecked(hwnd, RED);
if (toggled)
{
CheckDlgButton(hwnd, RED, BST_UNCHECKED);
r = 0;
} else {
CheckDlgButton(hwnd, RED, BST_CHECKED);
r = 255;
}
}