Win32APIを使ってアプリケーションのウィンドウを作る方法

今回は、Win32APIを使ってアプリケーションのウィンドウを作る方法を、手順に沿って解説していく。

1,ウィンドウクラスを作成する

Win32APIでウィンドウを作るためには、WNDCLASS構造体を使ってウィンドウクラスを作る必要がある。

ウィンドウクラスとは、今作成しているウィンドウがどんなものかを示したもので、例えば、「マウスカーソルのデザインは何にするか?」とか「ウィンドウのスタイルはどうするか?」等の、ウィンドウの基本的な情報を作成するものだ。

typedef struct tagWNDCLASS {
  UINT      style;
  WNDPROC   lpfnWndProc;
  int       cbClsExtra;
  int       cbWndExtra;
  HINSTANCE hInstance;
  HICON     hIcon;
  HCURSOR   hCursor;
  HBRUSH    hbrBackground;
  LPCTSTR   lpszMenuName;
  LPCTSTR   lpszClassName;
} WNDCLASS, *PWNDCLASS;

実際に、ウィンドウクラスの作成は以下のコードのように書くことになる。参考:ウィンドウクラスの登録

int WINAPI WinMain(
        HINSTANCE hInstance ,
        HINSTANCE hPrevInstance ,
        PSTR lpCmdLine ,
        int nCmdShow ) {
    HWND hwnd;
    WNDCLASS winc;

    winc.style      = CS_HREDRAW | CS_VREDRAW;
    winc.lpfnWndProc    = DefWindowProc;
    winc.cbClsExtra = winc.cbWndExtra   = 0;
    winc.hInstance      = hInstance;
    winc.hIcon      = LoadIcon(NULL , IDI_APPLICATION);
    winc.hCursor        = LoadCursor(NULL , IDC_ARROW);
    winc.hbrBackground  = (HBRUSH)GetStockObject(WHITE_BRUSH);
    winc.lpszMenuName   = NULL;
    winc.lpszClassName  = TEXT("KITTY");

以下は、変数の意味を1つ1つ解説していく。

style

ウィンドウの動きや描写などを指定できるメンバ変数。

winc.style       = CS_HREDRAW | CS_VREDRAW;

多くのアプリでは、上記の様に書かれていることが多いが、CS_HREDRAWとは「Class Style Horizon Redraw」の略で、ウィンドウの横サイズを変更した時にウィンドウを再描写してくれるようになる。

また、CS_VREDRAWは「Class Style Virtical Redraw」の略で、ウィンドウの縦サイズの再描写をしてくれる。

つまり、CS_HREDRAW | CS_VREDRAWと書くことで、ウィンドウのサイズを変更した時に縦横関係なく再描写してくれるようになるのだ。

他にも、色々なClass Styleがあるので、詳しくは公式ページを見ると良い。

参考:Window Class Styles (Windows)

lpfnWndProc

lpfnWndProcとは、メッセージを受け取るWndProc関数のポインタを格納するもの。

メッセージとは、「マウスの左クリックをした」とか「ウィンドウを閉じた」とか「キーボードのhキーを押した」等の様に、Windowsに何かしらの情報を送る事を言い、WndProc関数とは、Windowsがメッセージを受け取った時に呼び出される関数と言える。

ちなみに、関数名は自分の好きなように決めれるが、多くのアプリでは~WndProcみたいな関数名が多い。

cbClsExtra

ウィンドウクラスの後ろに余分なメモリを確保するための変数。例えば、cbClsExtra = 3としたら3byte分のメモリが動的に確保される。

WinMain関数からメインウィンドウハンドルを手に入れるまでのおさらい」によると、この動的メモリはアプリ共通のポインタを格納したりするのに便利みたいだ。

cbWndExtra

ウィンドウクラスのインスタンスが新たに作られるたびに、メモリを確保するもの。

hInstance

アプリのインスタンスハンドルを格納する変数。基本的には、WinMain関数の引数のhInsntanceを格納することになる。

hIcon

アプリのアイコンを描写するもの。

hIcon = LoadIcon(NULL, IDI_APPLICATION)のように書くことで、エラーアイコンだったり警告アイコンだったりを表示することができる。

参考:LoadIconA function (winuser.h) | Microsoft Docs

hCursor

カーソルの描写を指定する変数。

アイコンと同じように使う。

参考:LoadCursorA function (winuser.h) | Microsoft Docs

hbrBackground

「handle Bruch BackGroound」の略で、ウィンドウの背景色などを決めれるもの。

hbrbackground = GetStockObject(WHITE_BRUSH)のように書くことで背景色を決めていく。

参考:GetStockObject function (Windows)

lpszMenuName

作成したメニューの名前を登録する変数。 ただ、個人的にはあまり使わない。

lpszClassName

アプリの名前。基本的にwinc.lpszClassName = TEXT("KITTY");みたいな感じで登録する。

ウィンドウクラスを登録する

先ほど紹介したWNDCLASS(ウィンドウクラス)を作り終えたら、次はそのウィンドウクラスを登録する必要がある。とは言っても簡単で、RegisterClassEXと言う関数を使えばよい。

    WNDCLASS winc;

    winc.style      = CS_HREDRAW | CS_VREDRAW;
    winc.lpfnWndProc    = DefWindowProc;
    winc.cbClsExtra = winc.cbWndExtra   = 0;
    winc.hInstance      = hInstance;
    winc.hIcon      = LoadIcon(NULL , IDI_APPLICATION);
    winc.hCursor        = LoadCursor(NULL , IDC_ARROW);
    winc.hbrBackground  = (HBRUSH)GetStockObject(WHITE_BRUSH);
    winc.lpszMenuName   = NULL;
    winc.lpszClassName  = TEXT("KITTY");

if(!RegisterClassEX(&winc){
return 0;
}

RegisterClassEXは引数にWNDCLASSのポインタを格納して、戻り値は成功したら、そのクラスを識別する一意のATOM(整数みたいなやつ)が返されて、失敗したら0を返す。

また、以前はクラスの登録にはRegisterClassという関数が使われているが、RegisterClassEXはウィンドウを小さくした時に表示される小さなアイコンの表示も設定できるので、RegisterClassEXRegisterClassの上位互換と言える。

実際に、マイクロソフトの公式ページにも以下の様に書かれている。

Note The RegisterClass function has been superseded by the RegisterClassEx function. You can still use RegisterClass, however, if you do not need to set the class small icon. RegisterClass function (Windows)

参考:RegisterClassEx function (Windows)

CreateWindowでウィンドウを表示する

CreateWindow関数は、ウィンドウに関する設定&表示をする関数。

具体的には、以下のように書く。

 if (CreateWindow(APP_NAME, APP_NAME, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
                   CW_USEDEFAULT, CW_USEDEFAULT, 800, 800, NULL, NULL,
                   hInstance, NULL) == NULL) {
    return 0;
  }

第1引数にはクラス名、第2引数にはウィンドウタイトルを書く。そして第3引数のWS_OVERLAPPEDWINDOW | WS_VISIBLEだが、これはウィンドウをどのように表示するかを決めてくれる。

例えば、今回の場合はWS_OVERLAPPEDWINDOW | WS_VISIBLE,としているが、WS_OVERLAPPEDWINDOWWS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIMZEBOXと言ったオプションをまとめて指定してくれるもの。これを指定することで、ウィンドウの最小ボタン、最大ボタンを表示したり、ウィンドウのサイズを調整できたり、タイトルを表示してくれたり等、アプリを使う時に便利な機能を搭載してくれる。

また、WS_VISIBLEは初期状態で表示してくれるものとなる。

第4,5引数にはウィンドウをどの位置から表示するか、第6,7引数にはウィンドウのサイズを設定できる。

これらの引数は自分で自由に設定できるが、CW_USEDEFAULTを使う事で、Windowsが設定してある値を使うこともできる。

参考:CreateWindowA macro (winuser.h) | Microsoft Docs

参考:Window Styles - Windows applications | Microsoft Docs