Win32API入門 学習記録 ~ウィンドウの作成~


Win32APIのウインドウについて学習したことをまとめます。

環境

Windows10 Home ver1909
Visual Stadio 2017
VC++

参考サイト

Win32 API入門
http://wisdom.sakura.ne.jp/system/winapi/
標準 Windows APIの70までは学習完了

学習内容

言葉とか理解とか間違っていたら申し訳ございません

最小のプログラム

まずWindows APIを使う最小のプログラム

Win32API1.cpp
#include <windows.h>

int WINAPI WinMain
(
    //引数
    HINSTANCE hInstance, HINSTANCE hPrevInstance, 
    LPSTR lpCmdLine, int nCmdShow
)
{
    return 0;//windowを作るときはreturn 0だとバグる
} 

おそらくこれが最小のプログラム

int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)

がメイン関数でこの関数の中に書いたプログラムが実行される。

return 0

現状の中身はこれだけ
実行後に終了するだけです。

ウィンドウの作成

windowsだしウィンドウは作れないと
おもしろくないので

Win32API2.cpp

#include <windows.h>

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
    switch (msg)
    {
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    DefWindowProc(hwnd, msg, wp, lp);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    HWND hwnd;//ウィンドウハンドル
    MSG  msg;//メッセージ構造体
    WNDCLASS winc;

    winc.style = CS_HREDRAW | CS_VREDRAW;
    winc.lpfnWndProc = WndProc;
    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("HOGE");

    if (!RegisterClass(&winc)) return -1;


    hwnd = CreateWindow
    (
        TEXT("HOGE"),                    //ウィンドウクラスの指定
        TEXT("TEST"),                      //ウィンドウの名前
        WS_OVERLAPPEDWINDOW | WS_VISIBLE,  //ウィンドウのスタイル
        200, 200, 500, 500,                //ウィンドウの座標と大きさ
        NULL,                              //親ウィンドウならNULL
        NULL,                             //子ウィンドウがあればそのIDを指定
        hInstance,                        //インスタンスハンドル
        NULL                              //ウィンドウを作るときにOSに伝えることらしい
    );
    if (hwnd == NULL) { return -1; }

    while (GetMessage(&msg, NULL, 0, 0))//メッセージループ
    {
        DispatchMessage(&msg); //ウィンドウに命令を伝える
    }
    return msg.wParam;
}

これでウィンドウが作られる。

実行結果

こんな感じで真っ白のウィンドウが作られる。

解説(というか考察)

ウィンドウの設定をしているのは


 WNDCLASS winc;

    winc.style = CS_HREDRAW | CS_VREDRAW;
    winc.lpfnWndProc = WndProc;
    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("HOGE");

の部分で`WNDCLASSという構造体(※)に各種パラメータを設定することができる。
※構造体は関数のないクラスみたいなもの(厳密には違うらしいけど)

メインとなるウィンドウを作っている部分はこっち


CreateWindow
    (
        TEXT("HOGE"),                    //ウィンドウクラスの指定
        TEXT("TEST"),                      //ウィンドウの名前
        WS_OVERLAPPEDWINDOW | WS_VISIBLE,  //ウィンドウのスタイル
        200, 200, 500, 500,                //ウィンドウの座標と大きさ
        NULL,                              //親ウィンドウならNULL
        NULL,                             //子ウィンドウがあればそのIDを指定
        hInstance,                        //インスタンスハンドル
        NULL                              //ウィンドウを作るときにOSに伝えることらしい
    );

これがウィンドウを作る関数。
今は拡張機能が追加されたCreateWindowExという関数もあるらしいけど、そっちはまだ学習ができていません。(といっても引数が増える程度の違いなので難しくはないかも...)

結構面倒な数の引数があるけど重要だと感じた箇所は
TEXT("HOGE")
WS_OVERLAPPEDWINDOW | WS_VISIBLE

第1引数と第3引数の2つ
第1引数はウィンドウのひな型を指定している引数
でCreateWindowの上にあるwincに設定した"HOGE"というウィンドウのひな型を指定している。
第3引数はウィンドウの挙動を指定している引数

この2つは設定できる値がたくさんあるので使いこなすには、何ができるかを把握しておくことが重要であると感じる。

WindowsというOSはウィンドウとOS、ウインドウとウィンドウの間で情報のやり取りを行い動作するらしい。
それを実現しているのがウィンドウプロシージャと呼ばれる関数で
下のプログラムはウィンドウが削除された場合の処理を記述している。


LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
    switch (msg)
    {
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    DefWindowProc(hwnd, msg, wp, lp);
}

ウィンドウが削除されるとWM_DESTROYというマクロに設定されている数値がmsgの中に引数として渡される。
PostQuitMessage(0)はWindowsに対してアプリケーションが終了したこと伝える役目を持つ。

最後のDefWindowPorcはウィンドウの削除以外はデフォルトの処理をしてくださいというメッセージ。

メッセージはウィンドウに対する命令orウィンドウからWindowsに対する状態遷移を格納しているらしい
メッセージループでそのメッセージを処理している。

Win32APIのプログラムはこのウィンドウプロシージャを設定したり、新たにウィンドウを作成して行うらしい。


while (GetMessage(&msg, NULL, 0, 0))//メッセージループ
    {
        DispatchMessage(&msg); //ウィンドウに命令を伝える
    }

この部分がメッセージループの部分で4行しかないが、これがないとウィンドウが維持できなく実行した瞬間にウィンドウが消える。

終わりに

ここまでの私の認識をまとめました。
間違えている箇所があれば教えて頂けると幸いです。

Win32APIではウィンドウプロシージャというかメッセージのやり取りがが最も重要な要素だと思われるので、今後はそのあたりを意識して学習を進めようと思います。
(次はDirectXに手を出そうと思っていますが...)