第二回目はウィンドウの作成方法について説明します
次のような至ってシンプルなウィンドウを作ってみましょう
window.GIF

まず、以下の用語は
ウィンドウ作成において
非常に重要ですので覚えてください
・ウィンドウクラス
・ウィンドウスタイル
・ウィンドウプロシージャ

ウィンドウクラスとウィンドウスタイルは
ウィンドウの性質を決めます
この2つを変更することで普通のウィンドウから、ボタン、エディットコントロールなど
さまざまな種類のウィンドウを作成することができます

ウィンドウプロシージャとは
ウィンドウに送られてくるメッセージを処理する関数のことです
ウィンドウズプロシージャでの処理を記述することで
さまざまなメッセージに対応することができます

では、早速作ってみましょう


Win32プロジェクトを選択します
(実はWin32コンソールでも作成できます、このやり方については後述します)

project.GIF

「次へ」を押して進んでください(完了を押してはいけません)

project2.GIF

・Windowsアプリケーションにチェックがついているか確認して
・空のプロジェクトを選んでください
(空のプロジェクト以外を選ぶと余計なヘッダーファイルが作成されてしまいます)

project3.GIF

次に、main.cppを作ります
コンソールの時と同じように作ります
project4.GIF
project5.GIF

main.cppができたら
まず、次のコードを記述してください

	#include <tchar.h>
	#include <windows.h>
	#include <windowsx.h>

	int __stdcall WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd)
	{
		MessageBox(NULL,_T("Hello World"),_T("Msg Box"),MB_OK);
		return 0;
	}

次のような、メッセージボックスがでれば成功です
msgbox.GIF

ここで気づいてほしいことがあります
main関数がありません
その代わりにWinMain関数が使われています
WindowsアプリケーションではWinMain関数がアプリケーションの開始となる関数です
戻り値、引数も必ず上記のように書かなければなりません

また、起動してみるとコンソール画面(黒画面)は表示されません
WinMain関数から開始するとコンソール画面は隠蔽されてしまいます
(コンソール画面を出す方法は後述)

この関数でまず、いきなり変だと感じると思うのは__stdcallと書かれている部分です
これは呼び出し規約と言われて
関数を呼び出すときの仕組みです
普段コンソールで作っている関数は暗黙のうちに
__cdeclという呼び出し規約になっていますが
ウィンドウズの関数は基本的に__stdcallという呼び出し規約に従います
それ以上のことはあまり考えなくてもよいと思うので詳しい説明は割愛させてもらいます

ではWinMain関数の引数について説明します
HINSTANCEはインスタンスハンドルを指します
インスタンスハンドルはウィンドウを作るときに必要です
インスタンスハンドルというのは
OSがアプリケーションを識別するためのハンドル(ポインタ)です
アプリケーションが開始するとOSからインスタンスハンドルが引数として送られてきます。
プロセス毎に違うハンドル割り当てられるので
同じアプリケーションでも二つ同時に起動するとそれぞれ違うインスタンスハンドルが割り当てられます。
インスタンスハンドルは第1引数と第2引数の二つありますが使うのは
最初の引数のインスタンスハンドル(hInstance)のみです
第2引数のインスタンスハンドル(hPrevInstance)は使いません(これはWin16時代の名残です)

第3引数(lpCmdLine)はコマンドライン引数を格納してる文字列です
第4引数(nShowCmd)はコマンドライン引数の個数です
コンソールの表示はされませんが
コンソールから直接起動したりバッチファイルを使うことで
コンソールアプリケーション同様、引数を受け取ることは可能です

では、
シンプルなウィンドウを作ってみましょう
コード量がかなりあるので最初は戸惑うかも知れません
ただ、やっている処理の内容は次の3つだけです

・ウィンドウクラスを定義し、登録
・ウィンドウを作成
・メッセージループを回す

ゆっくり順番に見ていけば大丈夫です

	#include <tchar.h>
	#include <windows.h>
	#include <windowsx.h>

	// ウィンドウプロシージャ
	LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

	int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd)
	{
		const TCHAR* szClassName = _T("Basic");	// ウィンドウクラス名

		/********************************************************/
		/*		ウィンドウクラスの定義と作成					*/
		/********************************************************/

		// 新規ウィンドウクラスの定義
		WNDCLASSEX wc;
		wc.cbSize = sizeof(WNDCLASSEX);				// ウィンドウクラスのサイズ
		wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;	// ウィンドウクラスのスタイル	
		wc.lpfnWndProc = WndProc;				// ウィンドウプロシージャへの関数ポインタ
		wc.cbClsExtra = 0;					// 0(使わない)
		wc.cbWndExtra = 0;					// 0(使わない)
		wc.hInstance = hInstance;				// インスタンスハンドル
		wc.hIcon = (HICON)LoadImage(NULL,
			MAKEINTRESOURCE(IDI_APPLICATION),
			IMAGE_ICON,
			0,
			0,
			LR_DEFAULTSIZE | LR_SHARED);			// ウィンドウのアイコン
		wc.hCursor = (HCURSOR)LoadImage(NULL,
			MAKEINTRESOURCE(IDC_ARROW),
			IMAGE_CURSOR,
			0,
			0,
			LR_DEFAULTSIZE | LR_SHARED);			// ウィンドウのカーソル
		wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);	// ウィンドウ背景色(再描画のとき、この設定ブラシで塗りつぶしが行われる)
		wc.lpszMenuName = NULL;					// メニュー名
		wc.lpszClassName = (LPCTSTR)szClassName;		// ウィンドウクラス名
		wc.hIconSm = (HICON)LoadImage(NULL,
			MAKEINTRESOURCE(IDI_APPLICATION),
			IMAGE_ICON,
			0,
			0,
			LR_DEFAULTSIZE | LR_SHARED);// 小さいアイコンが設定された場合の情報を記述

		// ウィンドウクラスの登録
		if(!RegisterClassEx(&wc))
			return -1;	// 登録失敗

		/********************************************************/
		/*		ウィンドウの作成			*/
		/********************************************************/

		HWND hWnd = CreateWindow(
				szClassName,		//ウィンドウクラス名
				_T("タイトル"),		//タイトルバーにこの名前が表示されます
				WS_OVERLAPPEDWINDOW,	//ウィンドウスタイル
				CW_USEDEFAULT,		//X座標
				CW_USEDEFAULT,		//Y座標
				CW_USEDEFAULT,		//幅
				CW_USEDEFAULT,		//高さ
				NULL,			//親ウィンドウのハンドル、親を作るときはNULL
				NULL,			//メニューハンドルorリソースID
				hInstance,		//インスタンスハンドル
				NULL);
		
		// ウィンドウ作成失敗
		if (hWnd == NULL)
			return -1;

		// ウィンドウを可視状態にする
		ShowWindow(hWnd, nShowCmd);
		UpdateWindow(hWnd);

		MSG msg = {}; 
		while(msg.message != WM_QUIT) {
			if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)){
				TranslateMessage(&msg);
				DispatchMessage(&msg);
			}
			else{
				// メッセージ処理をしてないとき
			}
		}
		return 0;
	}
 
	// ウィンドウプロシージャ
	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));
	}

次のように出てくれば成功です
window.GIF

正直、冗長な部分も多いと思いますので
特に大事な部分に絞って説明します
それ以外の設定はこのサンプル通りに使ってくれればよいと思います
上から見ていきましょう

// ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

これはウィンドウに送られてきたメッセージを処理するための関数です
処理の中身は後述。

メイン関数です

int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd)

WINAPI、CALLBACKは__stdcallが#defineで置き換えられているだけです(ウィンドウズの関数なので__stdcall)

WNDCLASSEX構造体の変数に色々代入し、
RegisterClassEx関数でWNCLASSEX構造体を登録してます
WNDCLASSEX構造体のいくつか重要な変数を説明しておきます

CS_HREDRAW、CS_VREDRAWは
ウィンドウのサイズが変わった時に再描画するかのフラグです
CS_DBLCLKSはダブルクリックしたときのメッセージを受け付けるかの設定です

wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;		// ウィンドウクラスのスタイル	

ウィンドウへのメッセージを処理するプロシージャの設定です
関数ポインタなのでグローバル関数もしくはクラスの静的関数でなければなりません

wc.lpfnWndProc = WndProc;					// ウィンドウプロシージャへの関数ポインタ

インスタンスハンドルをセットします

wc.hInstance = hInstance;					// インスタンスハンドル

アイコンとカーソルをセットします
LoadImage関数はビットマップ(.bmp)、アイコン(.ico)、カーソル(.cur)の読み込みができます

wc.hIcon = (HICON)LoadImage(NULL,
	    MAKEINTRESOURCE(IDI_APPLICATION),
			IMAGE_ICON,
			0,
			0,
			LR_DEFAULTSIZE | LR_SHARED);		// ウィンドウのアイコン
wc.hCursor = (HCURSOR)LoadImage(NULL,
			MAKEINTRESOURCE(IDC_ARROW),
			IMAGE_CURSOR,
			0,
			0,
			LR_DEFAULTSIZE | LR_SHARED);		// ウィンドウのカーソル

ウィンドウの背景色をセットします

wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);	// ウィンドウ背景色(再描画のとき、この設定ブラシで塗りつぶしが行われる)

メニューをセットします(ウィンドウを作成するときにもセットできるのでNULLで構いません)

wc.lpszMenuName = NULL;					// メニュー名

ウィンドウクラス名をセットします
"BUTTON"(後述)など予め定義されたウィンドウクラス名は使ってはいけません

wc.lpszClassName = (LPCTSTR)szClassName;			// ウィンドウクラス名

ウィンドウクラスを登録します

// ウィンドウクラスの登録
if(!RegisterClassEx(&wc))
  return -1;	// 登録失敗

ウィンドウクラスに基づいて
ウィンドウを作成します

HWND hWnd = CreateWindow(
				szClassName,		//ウィンドウクラス名
				_T("タイトル"),		//タイトルバーにこの名前が表示されます
				WS_OVERLAPPEDWINDOW,	//ウィンドウスタイル
				CW_USEDEFAULT,		//X座標
				CW_USEDEFAULT,		//Y座標
				CW_USEDEFAULT,		//幅
				CW_USEDEFAULT,		//高さ
				NULL,			//親ウィンドウのハンドル、親を作るときはNULL
				NULL,			//メニューハンドルorリソースID
				hInstance,		//インスタンスハンドル
				NULL);

ここで重要なのは
ウィンドウクラス名とウィンドウスタイルです
ウィンドウクラス名は先ほど定義したウィンドウクラスにセットした名前を使います
ボタンなどを作る場合は"BUTTON"などの予めデフォルトで登録済みのウィンドウクラス名を使います
ウィンドウスタイルはWS_OVERLAPPEDWINDOWとしていますが
これは最小化ボタン、最大化ボタン、閉じるボタンが付いていて、ウィンドウのサイズも
ウィンドウの枠をドラッグすることで変えられます
ほかにも色々なスタイルが存在します
(追々に説明していきます)

X座標、Y座標、幅、高さに
CW_USEDEFAULTを入れると
適当な位置に適当な大きさでウィンドウを作成してくれます
指定したい場合はここに整数値をいれます

第8引数は
親ウィンドウがある場合は親ウィンドウのハンドルを指定します
親がいない場合はNULLを指定します
(コントロールなどは指定)

第9引数は
メニューハンドルまたはリソースIDを指定します
リソースIDはコントロールなどの作成時に指定して
コントロールの識別に使います
(コントロール、メニューに関してはまた今度)

かなり長くなってしまいましたがウィンドウズプログラミングにおいて
基本的な重要な部分はほとんどここに詰まっています
(なのでここが理解できれば、後は楽だと思います)

コントロールを作るときなどは予め用意されたウィンドウクラスが登録されているので
ウィンドウクラスの定義、登録などのめんどくさいことはしなくてよいです

選択肢 投票
(^ω^)やったお 0  
何これwww意味不すぎwww 0  
。(`ω´#)。あぁん?最近、だらしねぇな 0  

トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS