Windows API   はじめの一歩

ホーム   C/C++チュートリアル


Cプログラミンの一環として始めた Windos API ですが、やってみたら面白かったので、もっと詳しく、かつ簡単に、ステップバイステップで学習するコーナーも作りました。
Windows API Primer
Primer は、入門書という意味です。

メッセージボックス

このサイトでは、各GUIフレームワークの「はじめの一歩」でウィンドウを作成しています。しかし、Windows APIは、ウィンドウを表示するだけでもとても多くのコードが必要です。そのため、この章ではまず、ウィンドウに比べると簡単に表示できるメッセージボックスというGUIを使って、プログラムの開始ポイントであるメイン関数について説明します。

次のソースコードを、お好きなエディタで記述して、messagebox.c という名前でお好きな場所に保存してください。

messagebox.c


#include <windows.h>

/*  メイン関数           */
int WINAPI WinMain(
                  HINSTANCE hInstance,
		  HINSTANCE hPrevInstance,
		  LPSTR     lpCmdLine,
		  int       nCmdShow
		  )
{

/*  メッセージボックス       */
    MessageBox    (
                  NULL,
		  TEXT("日本語などの2バイト文字では、\n文字化けする文字もあるみたいです。"),
                  TEXT("はじめの一歩"),
                  MB_OK
                  );

/*  アプリケーションの終了     */
    return 0;
}
    


コード説明

  1. #include <windows.h>
    Windows APIと標準Cのほとんどの型・関数・定数・マクロなどが定義されているヘッダーファイルです。
  2. int WINAPI WinMain(
    Windows APIのプログラムはWinMain関数から始まる決まりになっています。WinMain関数はint型を返します。WinMain関数にはWINAPIという呼び出し規約(calling convention)を記述しなければなりません。呼び出し規約は、その関数の呼び出し方法を表しています。WinMain関数には次の4つの引数があります。
    1. HINSTANCE hInstance,
      第1引数のHINSTANCEは、Windowsのシステムがアプリケーションを識別するための識別値が入る型です。この引数には、現在のアプリケーションの識別値が入ります
    2. HINSTANCE hPrevInstance,
      第2引数は、16bit Windowsのために設けられた引数です。32bit以上のWindowsではNULLが入ります
    3. LPSTR lpCmdLine,
      第3引数のLPSTRは、文字列が入る型です。この引数にはコマンドライン引数が入ります。標準Cのコマンドライン引数はスペースで区切った複数の文字列として認識されますが、Windows APIでは、スペースで区切っても、一つ文字列として認識されます
    4. int nCmdShow
      第4引数には、ウィンドウの表示状態を表す整数値が入ります
  3. MessageBox(
    MessageBox関数は、メッセージボックスと呼ばれる簡単なダイアログを表示する関数です。MessageBoxには次の4つの引数があります。
    1. 第1引数に所属するウィンドウを指定します。所属するウィンドウがない場合はNULLを指定します
    2. 第2引数にメッセージボックスに表示される文字列を指定します。TEXT( )マクロは1バイト文字列と2バイト文字列を適宜マッチした型にしてくれます
    3. 第3引数にメッセージボックスのタイトルの文字列を指定します
    4. 第4引数にメッセージボックのボタンの種類とアイコンの種類を定数で指定します。MB_OKはOKボタンを表示します
  4. return 0;
    WinMain関数は最後に整数値を返します。正常終了した場合は 0 を返します。


コンパイル

ソースコードの記述が終わりましたら、ソースコードを機械語に変えるコンパイルという作業が必要です。

コマンドプロンプトもしくはMsys2を起動して、messagebox.cを保存したディレクトリに移動してください。そして次のコマンドでコンパイルします。


gcc messagebox.c -o messagebox -mwindows
    

gcc のあとに ファイル名を指定します。-o のあとにはコンパイルされる実行ファイル名を指定します。このオプションを省略すると a.exe という実行ファイルが作成されます。また、-mwindows オプションを省略すると、実行ファイルをダブルクリックで実行した場合、コマンドプロンプトが一緒に起動します。


実行方法

コマンドプロンプトもしくはMsys2で次のようにコマンドするか、実行ファイルをダブルクリックしてください。


// コマンドプロンプトの場合
messagebox

// Msys2の場合
./messagebox
    


実行結果


ウィンドウ

次に、この節では、ウィンドウを作成します。Windows APIでは、ウィンドウを表示するために次の5つステップが必要です。

  1. WNDCLASSというウィンドウのテンプレート(template、雛形)を作る
  2. 作成したWNDCLASSをWindowsシステムに登録する
  3. 作成したWNDCLASSを元にしてウィンドウを作成する
  4. 作成したウィンドウを表示する
  5. 作成したウィンドウの表示を維持する

では、次のソースコードを、お好きなエディタで記述して、window.c という名前でお好きな場所に保存してください。このコードではウィンドウの表示を維持するためにメッセージボックスを使っています。

window.c


#include <windows.h>

    /*  メイン関数             */
int WINAPI WinMain      (HINSTANCE hInstance,
                         HINSTANCE hPrevInstance,
                         LPSTR lpCmdLine,
                         int nCmdShow)
{

    /*  ローカル変数           */
    WNDCLASS wc;
    HWND hwnd;
    
    /*  WNDCLASSの作成        */
    wc.style         = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc   = DefWindowProc;
    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("MainWindow");
    
    /*  WNDCLASSの登録         */
    RegisterClass(&wc);
    
    /*  ウィンドウの作成        */
    hwnd = CreateWindow (TEXT("MainWindow"),
                         TEXT("メインウィンドウ"),
                         WS_OVERLAPPEDWINDOW,
                         CW_USEDEFAULT,
                         CW_USEDEFAULT,
                         CW_USEDEFAULT,
                         CW_USEDEFAULT,
                         NULL,
                         NULL,
                         hInstance,
                         NULL);
    
    /*  ウィンドウの表示        */
    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);
    
    /*  メッセージボックス       */
    MessageBox          (hwnd,
			 TEXT("OKボタンをクリックすると、\nアプリケーションが終了します。"),
			 TEXT("はじめの一歩"),
			 MB_OK);
	
	/*  アプリケーションの終了     */
	return 0;
}
    


コード説明

  1. WNDCLASS wc;
    HWND hwnd;
    WNDCLASSがWNDCLASSを入れる型で、HWNDがウィンドウを入れる型です。
  2. WNDCLASSには次の10個の属性を設定します
    1. wc.style = CS_HREDRAW | CS_VREDRAW;
      style属性のCS_HREDRAWは水平方向にサイズが変更された時に再描画します。CS_VREDRAWは垂直方向にリサイズされた時に再描画します
    2. wc.lpfnWndProc = DefWindowProc;
      lpfnWndProc属性にはウィンドウと関連付ける関数を設定します。DefWindowProcはWindows APIが事前に用意しているウィンドウ用のデフォルト関数です
    3. wc.cbClsExtra = 0;
      WNDCLASS構造体の後に確保する領域のバイト数を設定します
    4. wc.cbWndExtra = 0;
      WNDCLASSインスタンスの後に確保する領域のバイト数を設定します
    5. wc.hInstance = hInstance;
      hInstance属性には、アプリケーションの識別値を設定します
    6. wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
      hIcon属性には、アイコンを設定します。LoadIcon関数の第1引数にNULLを指定するとシステムで定義されているアイコンを指定できます。IDI_APPLICATIONはシステムのデフォルトアプリケーションアイコンです
    7. wc.hCursor = LoadCursor(NULL, IDC_ARROW);
      hCursor属性には、カーソルを設定します。LoadCusor関数の第1引数にNULLを指定するとシステムで定義されているカーソルを指定できます。IDC_ARROWは矢印アイコンです
    8. wc.hbrBackground = (HBRUSH) (COLOR_APPWORKSPACE + 1);
      hbrBackground属性には、ウィンドウの背景色を設定します。(HBRUSH)(COLOR_APPWORKSPACE + 1)と設定するとグレーの背景になります。(HBRUSH) (COLOR_WINDOW + 1)と設定するとホワイトの背景になります
    9. wc.lpszMenuName = NULL;
      lpzMenuName属性には、メニューを設定します。設定するメニューがない場合はNULLを指定します
    10. wc.lpszClassName = TEXT("MainWindow");
      lpszClassName属性には、アプリケーション内でWNDCLASSを識別するための文字列を設定します。アプリケーション内にウィンドウが一つとは限りません
  3. hwnd = CreateWindow (TEXT("MainWindow"),
    CreateWindow関数でWNDCLASSから実際のウィンドウを作ります。CreateWindow関数には次の11個の引数があります。
    1. TEXT("MainWindow"),
      第1引数に元になるWNDCLASSを文字列で指定します
    2. TEXT("Windows API ウィンドウ"),
      第2引数にウィンドウのタイトルバーに表示する文字列を指定します
    3. WS_OVERLAPPEDWINDOW,
      第3引数にウィンドウの表示スタイルを整数で表す定数で指定します。WS_OVERLAPPEDWINDOW以外の定数については、その都度説明します
    4. CW_USEDEFAULT,
      第4引数にウィンドウがスクリーン(デスクトップ)に表示される左上のX地点を数値で指定します。CW_USEDEFAULTを指定するとデフォルトの位置になります
    5. CW_USEDEFAULT,
      第5引数にウィンドウがスクリーン(デスクトップ)に表示される左上のY地点を数値で指定します。CW_USEDEFAULTを指定するとデフォルトの位置になります
    6. CW_USEDEFAULT,
      第6引数にウィンドウの横のサイズを数値で指定します。CW_USEDEFAULTを指定するとデフォルトの位置になります
    7. CW_USEDEFAULT,
      第7引数にウィンドウの縦のサイズを数値で指定します。CW_USEDEFAULTを指定するとデフォルトの位置になります
    8. NULL,
      第8引数に付属するウィンドウ(親ウィンドウ)を指定します。親ウィンドウのなメインウィンドウを作成する場合は、NULLを指定します
    9. NULL,
      第9引数に使用するメニューを指定しますう。使用するメニューがなければNULLを指定します
    10. hInstance,
      第10引数にアプリケーションの識別値を指定します
    11. NULL);
      第11引数にウィンドウの作成に失敗した場合の返り値を指定します。NULLを指定すると、ウィンドウの作成に失敗した場合にNULLが返されます
  4. ShowWindow(hwnd, nCmdShow);
    ShowWindow関数でウィンドウを可視状態にします。ウィンドウは作成した時点では不可視状態です。ShowWindow関数の第1引数にはウィンドウを、第2引数にはメイン関数の第4引数を指定します。
  5. UpdateWindow(hwnd);
    UpdateWindow関数でウィンドウの再描画を行なっています。引数にはウィンドウを指定します。この関数がなくてもShowWindow関数でウィンドウは表示されますが、念のために再描画しています。
  6. MessageBox(hwnd,
    アプリケーションはメッセージループと呼ばれるものでループ状態を維持しなければ、ウィンドウを一瞬表示して終了します。ここでは簡易的にメッセージボックスを表示して、アプリケーションがすぐに終了するのを止めています。メッセージループは他の開発環境でメインループやイベントループと呼ばれるものと同じです。メッセージループについては次節で説明します。


コンパイル

ソースコードの記述が終わりましたら、ソースコードを機械語に変えるコンパイルという作業が必要です。

コマンドプロンプトもしくはMsys2を起動して、window.cを保存したディレクトリに移動してください。そして次のコマンドでコンパイルします。


gcc window.c -o window -mwindows
    

gcc のあとに ファイル名を指定します。-o のあとにはコンパイルされる実行ファイル名を指定します。このオプションを省略すると a.exe という実行ファイルが作成されます。また、-mwindows オプションを省略すると、実行ファイルをダブルクリックで実行した場合、コマンドプロンプトが一緒に起動します。


実行方法

コマンドプロンプトもしくはMsys2で次のようにコマンドするか、実行ファイルをダブルクリックしてください。


// コマンドプロンプトの場合
window

// Msys2の場合
./window
    


実行結果


ウィンドウプロシージャ

Windows APIのアプリケーションは、「メッセージループ」と呼ばれるものでアプリケーションの実行を維持し続けます。メッセージループはwhileループで作ります。

アプリケーションを終了するには、メッセージループにメッセージを送ります。そのメッセージの受け手となる関数も必要になります。このメッセージの受け手となる関数のことをWindows APIでは「ウィンドウプロシージャ」と呼びます。他の開発環境でイベントハンドラー(evnt handler)と呼ばれるものとだいたい同じですが、Windows APIでは次の点で違います。

Windows APIでは、すべてのGUI部品はウィンドウの一種として認識されています。ボタンもウィンドウの一種です。そしてそのウィンドウをウィンドウプロシージャと接続しておきます。ウィンドウプロシージャでは、送られてきたメッセージによって処理を選り分けます。つまりメッセージ(イベント)によって接続する関数を選り分けるのではなく、関数側でメッセージによって処理を選り分けます


hello.c

次のソースコードを、お好きなエディタで記述して、hello.c という名前でお好きな場所に保存してください。


#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 = (HBRUSH) (COLOR_APPWORKSPACE + 1);
    wc.lpszMenuName  = NULL;
    wc.lpszClassName = TEXT("MainWindow");

    RegisterClass(&wc);

    hwnd = CreateWindow (TEXT("MainWindow"),
                         TEXT("Hello Windows API"),
                         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)
{
    switch (msg)
    {
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd,
                         msg,
                         wParam,
                         lParam);
}
    


コード説明

  1. LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
    ウィンドウのウィンドウプロシージャのプロトタイプ(関数前方宣言)です。ウィンドウプロシージャはLRESULT型を返し、CALLBACKという呼び出し規約をつける決まりになっています。
  2. MSG msg;
    ウィンドウから送られてくるメッセージを受け取るMSG型の変数を定義しています。
  3. (GetSystemMetrics(SM_CXSCREEN) - 400) / 2,
    CreateWindow関数の第4引数でウィンドウの横位置を指定しています。GetSystemMetrics(SM_CXSCREEN)でスクリーン(デスクトップ)の横サイズが得られます。そこからウィンドウの横サイズを引いて、2で割れば、ウィンドウをスクリーンの中央に表示する場合の横位置が得られます。
  4. (GetSystemMetrics(SM_CYSCREEN) - 250) / 2,
    CreateWindow関数の第5引数でウィンドウの縦位置を指定しています。GetSystemMetrics(SM_CYSCREEN)でスクリーン(デスクトップ)の縦サイズが得られます。そこからウィンドウの縦サイズを引いて、2で割れば、ウィンドウをスクリーンの中央に表示する場合の縦位置が得られます。
  5. while (GetMessage(&msg, NULL, 0, 0))
    GetMessage関数でメッセージを取得します。この関数はアプリケーションを終了させるWM_QUITメッセージを取得した場合にだけ 0 を返します。つまりWM_QUITメッセージを取得した場合は、whileループが終了することになります。なおWM_QUIT以外のメッセージの場合は、0 以外の数値を返し、エラーの場合は、-1 を返します。メッセージがない場合は次のメッセージが来るまでは待機します。4つの引数は次のとおりです。
    1. 第1引数にメッセージをやり取りするためのMSG型の変数のアドレスを指定します
    2. 第2引数にメッセージの発信元のウィンドウを指定します。特に指定しない場合はNULLを指定します
    3. 第3引数に取得するメッセージの最小値を指定します。通常は取得するメッセージに制限を設けないので 0 を指定します
    4. 第4引数に取得するメッセージの最大を指定します。通常は取得するメッセージに制限を設けないので 0 を指定します
  6. TranslateMessage(&msg);
    TranslateMessage関数は、メッセージがキーボード入力だった場合に、そのメッセージを文字メッセージに変換します。
  7. DispatchMessage(&msg);
    DispatchMessage関数は、メッセージをウィンドウプロシージャに送ります。
  8. return msg.wParam;
    ウィンドウプロシージャは、最後にMSG構造体のwParamメンバ変数に、終了コードを入れます。メイン関数はその終了コードを返して終了します。
  9. LRESULT CALLBACK WndProc
    ウィンドウプロシージャ関数です。関数名は任意ですが、LRESULT型を戻り値とし、CALLBACK呼び出し規約をつけなければなりません。4つの引数は次のとおりです。
    1. HWND hwnd,
      第1引数に、ウィンドウを受け取ります
    2. UINT msg,
      第2引数に、メッセージを受け取ります
    3. WPARAM wParam,
      第3引数に、メッセージの付加情報を受け取ります。どのような情報なのかはメッセージの種類によって違います
    4. LPARAM lParam)
      第4引数にも、メッセージの付加情報を受け取ります。どのような情報なのかはメッセージの種類によって違います
  10. switch (msg)
    ウィンドウプロシージャでは、switch文を用いて各メッセージの処理を選り分けます。
  11. case WM_DESTROY:
    WM_DESTROYメッセージは、ウィンンドウが破棄される前に送信されます。
  12. PostQuitMessage(0);
    PostQuitMessage関数は、メッセージループにWM_QUITメッセージを送ります。WM_QUITメッセージを受け取ったメッセージループは 0 と評価されて、メッセージループが終了します。この関数の引数 0 は、MSGのwParamに格納されてメイン関数の戻り値になります。
  13. return 0;
    ウィンドウプロシージャのLRESULT型は整数型ですので 0 を返しています。しかしLRESULTをintなどと書き換えるとエラーになります。switch文の常套句の break; を使っても問題ありません。

  14. return DefWindowProc
    caseで設定していないメッセージについては、Windows APIが用意しているデフォルトのウィンドウプロシージャであるDefWindowProcに送ります。引数は自作のウィンドウプロシージャと同じです。このDefWindowProc関数は基本的に何もしないみたいです。


コンパイル

ソースコードの記述が終わりましたら、ソースコードを機械語に変えるコンパイルという作業が必要です。

コマンドプロンプトもしくはMsys2を起動して、hello.cを保存したディレクトリに移動してください。そして次のコマンドでコンパイルします。


gcc hello.c -o hello -mwindows
    

gcc のあとに ファイル名を指定します。-o のあとにはコンパイルされる実行ファイル名を指定します。このオプションを省略すると a.exe という実行ファイルが作成されます。また、-mwindows オプションを省略すると、実行ファイルをダブルクリックで実行した場合、コマンドプロンプトが一緒に起動します。


実行方法

コマンドプロンプトもしくはMsys2で次のようにコマンドするか、実行ファイルをダブルクリックしてください。


// コマンドプロンプトの場合
hello

// Msys2の場合
./hello
    


実行結果


ウィンドウを閉じるとアプリケーションが終了します。



29717 visits
Posted: Jan. 25, 2020
Update: Jan. 29, 2020

ホーム   C/C++チュートリアル   目次