見出し画像

さっくりWINAPI_02 //CreateWindow

はい、という訳で今回はウィンドウの作成までを説明したいと思います。メッセージループとプロシージャ関係はちょっとだけ面倒臭いので次回ということで。

前回


1.ウィンドウ作成

ウィンドウの作成を行うためにはまずWNDCLASS構造体かWNDCLASSEX構造体をうめる必要があります。WNDCLASSには作成するウィンドウの情報をセットすることになります。

実体は以下のように定義されています。

typedef struct tagWNDCLASSW {
  UINT      style;
  WNDPROC   lpfnWndProc;
  int       cbClsExtra;
  int       cbWndExtra;
  HINSTANCE hInstance;
  HICON     hIcon;
  HCURSOR   hCursor;
  HBRUSH    hbrBackground;
  LPCWSTR   lpszMenuName;
  LPCWSTR   lpszClassName;
} WNDCLASSW, *PWNDCLASSW, *NPWNDCLASSW, *LPWNDCLASSW;

一度にすべて覚えきる必要はないので少しずつ慣れていきましょう。

ちなみに命名規則にはハンガリアン記法というものが使われているので一度目を通しておいて損は無いかと思います。


UINT Style
これは、ウィンドウのスタイルをunsinged intで指定します。特定のビットが特定のスタイルを示すような形となり、ORを取ることによって複数のスタイルを指定することができます。

 CS_HREDRAW | CS_VREDRAW

みたいな形ですね。CS_HREDRAWはウィンドウの幅が変更された際に再描画を行う、CS_VREDRAWはウィンドウの高さが変更された際に再描画を行う事を示すスタイルです。再描画はペイントメッセージによって処理されます。
具体的なウィンドスタイルは以下のドキュメントを参照してみてください。


WNDPROC lpfnWndProc
ウィンドプロシージャをここでセットします。
ウィンドウプロシージャと言うのは、まあ、具体的な事は後で説明します。ここではざっくり、ウィンドウの実際の動作を記述するための関数だと思ってください。ウィンドウでは、各種操作によってウィンドウメッセージが作成され、そのメッセ―ジ毎に処理を行う仕組みになっています。プロシージャは実際にそのメッセ―ジに対する処理を行います。

DefWindowProcを指定することで、基本的なウィンドウの操作をフォローしてくれます。具体的なドキュメントは次のサイトになります。

int cbClsExtra,cbWndExtra
基本的に0をセットしておけば大丈夫です。
特別にバイトを割り当てる際にここに具体的なバイト数を指定するそうです。

HINSTANCE hInstance
インスタスへのハンドルを指定します。ウィンドウの実体を扱う際に利用するポインタです。
まあ、WinMain引数で受け取ったHINSTANCEを指定する場所ですね。

HICON hIcon
ウィンドウのアイコンを示す型です。ウィンドウの左上にあるあれですね。LoadIconからHICONデータを取り出して格納します。リースファイルにアイコンを設定して、セットすることもできます。LoadIconでは、標準アイコンを利用するため、NULLをセットし、その定数の数字を送っています。

HCURSOR hCursor
ウィンドウ上で表示されるカーソルを示す型です。LoadCorsorでhcursorデータを取り出して格納します。大体HICONと同じですね。

HBRUSH hbrBackground
ウィンドのバックグランドの色を示す型です。実体はCOLORREFみたいです。ここでは、GetStockObjectから、標準の色を取り出してセットしています。

LPCWSTR lpszMenuName
メニューの識別子を示すメンバーです。メニューは後から動的に割り当てることもできるのですが、リソースファイルに書いてから、ここで指定することができます。これはNULLでも大丈夫です。

LPCWSTR lpszClassName
ウィンドウの識別子を示すメンバーです。ウィンドウには名前を与えてその識別子を保持させる必要があります。まあ、ざっくりウィンドウの名前だと思っておいて大丈夫です。これはほぼ必須なので、名前の指定を行います。

以上、これらのデータを1個1個うめていく必要があります。また、余談ですがこれとは別にWNDCLASSEXという拡張版もありますが、大きくは変わらないので大丈夫かと思われます。

では例を見てみましょう。

int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR nCmdLine, _In_ int nShowCmd){
    HWND hWnd;
    WNDCLASS wc;
    static WCHAR AppName = TEXT("ウィンドウの名前");

    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = DefWindowProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(NULL, IDI_APLLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = szAppName;

}

こんな感じです。何回もコードを見ていく内に慣れていくので、無理に覚えようとはせず、気軽に眺めてください。

2.RegisterClass

設定したWNDCLASSを利用してウィンドウを登録するためのクラスです。登録に成功すればウィンドウを識別するための数値を、失敗すれば0を返します。この関数を利用しないと後後、CreateWindowでウィンドウの生成を行うことが出来ません。

使い方は非常に簡単です。

if(!RegisnterClass(&wc))    return 0;

これだけです。

3.CreateWindow

それでは、ついにウィンドウの作成を行います。ウィンドウの生成を行うためにはCreateWindowを利用します。


HWND CreateWindowW(
  [in, optional]  lpClassName,
  [in, optional]  lpWindowName,
  [in]            dwStyle,
  [in]            x,
  [in]            y,
  [in]            nWidth,
  [in]            nHeight,
  [in, optional]  hWndParent,
  [in, optional]  hMenu,
  [in, optional]  hInstance,
  [in, optional]  lpParam
);


ちょっとだけ厄介ですね。ですがこれもまたおいおい慣れて行けば大丈夫です。

LPCWSTR lpClassName
WNDCLASSに設定し、登録したウィンドウのクラスの名前をここで指定します。指定した名前からウィンドウの作成を行います。

LPCWSTR lpWindowName
ウィンドウの名前を指定します。ウィンドウの上、タイトルバーに表示される文字列の事ですね。

int dwStyle
ウィンドウのスタイルを指定します。引数には定数を指定することで、特定のスタイルにすることができます。主な定数は次のドキュメントに在ります。取り合えず、WS_OVERLAPPEDWINDOWを指定しておけば一般的なウィンドウのスタイルになります。また、WS_VISIBLEで可視化できますが、これはShowWindowでも代用が可能です。

int X,Y
ウィンドウの初期位置を指定することができます。左上を(X,Y)=(0,0)として、どれだけ離れているかで表現されています。CW_DEFAULTでデフォルト値を設定することができます。

int nWidth, nHeight
ウィンドウの幅と高さを指定することができます。これもまたCW_USEDEFAULTでデフォルト値を指定することができます。

HWND hWndParent
作成するウィンドウの親のウィンドウハンドルを指定することができます。CreateWindowではHWNDを作成するため、先に別の方法でHWNDを作成しhておく必要があります。また、親がいない孤児はNULLをしてします。

HMENU hMenu
作成するウィンドウに保持させるメニューを指定します。リソースファイルから読み込ませることもでき、後でメニューの指定を行うこともできます。MENUITEMINFOやらCreateMenuやらSetMenuやら色々ゴリゴリ作成することもできます。また、NULLを指定することで、メニューを保持させないこともできます。

HINSTANCE hInstance
ウィンドウに関連付けられるハンドルインスタンスを指定します。まあ、つまりWinMain引数で引き取ったHINSTANCEのことです。

LPVOID lpParam
ウィンドウプロシージャに送るlParamのデータを指定します。より具体的にはメッセージWM_CREATEが送られた時のlParamの事です。ウィンドウプロシージャとは、WNDCLASSで設定したlpfnWndProcの奴の事です。まあ、いまは気にしなくて大丈夫です。

大体こんな感じです。まあ、この辺りはコードを見て慣れる方が速いとおもいます。

と、いう訳で言ってみよー!!(いかりやさん風)

4.サンプルコード


#include<windows.h>

int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd) {
	WNDCLASS wc;
	HWND hWnd;

	static const WCHAR AppName[] = TEXT("Sample Window");

	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)GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName = NULL;
	wc.lpszClassName = AppName;

	if(!RegisterClass(&wc)) return 0;

	hWnd = CreateWindow(AppName, TEXT("Window"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);

	MessageBox(NULL, TEXT("CreateWindow"), TEXT("MessageBox"), MB_OK);

	return 0;
}


これまで説明した通りです。ただしこのままではプログラムがすぐ終了してしまいます。そのため一時的にMessageBoxで係留しています。

通常はメッセージループを利用してプログラムが終了しないようにする必要があるのですが、その際メッセージを処理するためにプロシージャを利用する必要があります。メッセージループとプロシージャについては次回に回そうかよ思います。

5.結び

と、いうことでウィンドウズの作成に成功しました!!(やったぜ)
後は細かいパーツの利用方法を勉強していくことになります。

という訳で次回はプロシージャとメッセージループです。

次回


この記事が気に入ったらサポートをしてみませんか?