【InsideYaneSDK_Yane2nd解析プロジェクト】
第2回:
CWindow::Createは電波系猫耳娘の夢をみるか? byみはえる

 今回は、CWindow::Createの解析です。要は、前回やり残した分を解析しただけだったり^^;。
 当然タイトルには何の意味もありません。本当はここでタイトルの解説があったのですが、保存する前にフロントページがフリーズしてしまったため消えてしまいました(泣)。同じギャグを書くのはかなり苦痛なので今回は無し(笑)。

 ではいってみよー。


LRESULT CWindow::Create(CWindowOption& opt,HWND hParent){

 CWindow::Createは、引数としてCWindowOption型(ウィンドウのタイトル、ウィンドウ名、ウィンドウサイズ、表示オプションが格納されている)オブジェクトと、親ウインドウハンドルを取ります。親ウィンドウハンドルは、デフォルト引数でNULLが設定されているので、省略可能です。

	HINSTANCE hInst = CAppInitializer::GetInstance();
	m_bFullScreen = g_bFullScreen;	//	現在の画面モードを内部的に保持
	m_opt	= opt;	//	コピーしておく

 CAppInitializer::GetInstance( )は、WinMainが受け取ったインスタンスハンドルを返します。
 m_bFullScreenは、現在の画面モード(true = フルスクリーン , false = ウィンドウ)をg_bFullScreenから受け取ります。g_bFullScreenは初期値でウィンドウモードが設定されています(関係ないですけど、g_bFullScreenは、ChangeScreenが実行されたときに更新されます)。
 m_optはCWindowOptionを受け取ります。

 ここから、ウィンドウの登録を行います。

	//	起動後、一度目なのか?
	static bool bFirst = true;
	if (bFirst) {
		bFirst = false;

 CWindow::Createが呼ばれたのが2度目以降なのであれば、ウィンドウ登録フェイズを飛ばします。

		// おきまりのウインドウクラス生成処理
		WNDCLASSEX wndclass;
		wndclass.cbSize	= sizeof(WNDCLASSEX);
		wndclass.style	= 0;	//	ダブルクリック感知するなら→CS_DBLCLKS;
		wndclass.lpfnWndProc	= gWndProc;
		wndclass.cbClsExtra	= 0;
		wndclass.cbWndExtra	= 0;
		wndclass.hInstance	= hInst;

 ウィンドウクラス生成に必要な情報を設定していきます。

 コールバック関数を登録するlpfnWndProcには、CWindowのstaticメンバgWndProc()を設定します。ウィンドウが作成された後は、このウィンドウに対するメッセージが、gWndProc()に送られます。gWndProc( )では受け取ったメッセージを、HookListに登録されているメッセージフックに転送します。

		// とりあえず、"MAIN"のアイコン表示
		wndclass.hIcon	= LoadIcon(hInst,"MAIN");
		wndclass.hIconSm	= LoadIcon(hInst,"MAIN");
		wndclass.hCursor	= LoadCursor(NULL,IDC_ARROW);
		// とりあえず、マウスカーソルも用意(不要ならば、あとで消すべし!)

 タイトルバー、タスクバーに表示するアイコン、マウスの形状を決めます。自前で用意したい場合は、リソースにアイコンを登録して、ここを書き換えることになります(よね?)。YaneSDK2ndを直接いじるのはちょと気が引けますにょ〜^^; 誰か方法教えてください。ついでにマウスカーソルを消す方法も教えてください(^^;>なんにも知らない奴)

		//	とりあえず、起動直後は、白か黒にしとこっと…
		if (m_dwFillColor==0) {
			wndclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
		} else {
			wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
		}

 m_dwFillColorの値によって、起動時の画面背景色を白か黒に設定します。m_dwFillColorは、CWindowコンストラクタで、0に初期化されています。

		//	MENUも仮で読み込んでみる
		wndclass.lpszMenuName  = "IDR_MENU";
		//	このリソースが本当に存在するのかは、実行時までわからないので
		//	正確なウィンドゥのアドジャストがウィンドゥ生成後でしか出来ない

		{
			HRSRC hrsrc;
			hrsrc = ::FindResource(NULL,"IDR_MENU",RT_MENU);
			if (hrsrc!=NULL) { m_bUseMenu = true; }	//	メニューありやがんで^^;
		}

 FindResourceは、メニューがリソースとして存在するかどうかを確認するために呼び出しています。

		wndclass.lpszClassName = opt.classname.c_str();

 ウィンドウクラス名は、optの中から受け取ります。c_str()は、string文字列に’\n’を追加した文字列へのポインタを返します。

		// なんで失敗してんだろね?
		if (!::RegisterClassEx(&wndclass)) {
			Err.Out("CWindow::CreateでRegisterClassEx失敗");
			return 1;
		}
	}

 設定が全て終わったので、::RegisterClassEx()でウィンドウを登録してみます。もし失敗したら、ログにエラーを吐いてアプリケーションを終了します。

 以上でウィンドウが無事登録されました。次に実際にウィンドウを作成し、ディスプレイに表示させます。

	LONG lChild = 0;
	if (hParent!=NULL) {
		lChild	= WS_CHILDWINDOW;
	}

 hParentはCwindow::Create()の引数でしたね。初期値(NULL)で無ければ、親ウィンドウのハンドルが入っている筈(=今から作ろうとしているのは子ウィンドウである)筈なので、lChildにWS_CHILDWINDOWフラグを入れておきます(ただ、マルチウィンドウを行ったYaneSDKサンプルが存在しないので、実際に出来るのかよくわかりません^^;)

	if (m_bFullScreen){
		//	ウインドウの生成(フルスクリーン時)
		//	このときは、画面のスタイルオプションは無視する

		int sx,sy;
		GetScreenSize(sx,sy);
		m_hWnd = ::CreateWindow(opt.classname.c_str(),opt.caption.c_str(),
						WS_POPUP|WS_VISIBLE	 /* |WS_SYSMENU|
						WS_MAXIMIZEBOX|WS_MINIMIZEBOX */ ,
						0, 0, sx, sy,
						hParent, NULL, hInst, NULL );
	} else {
		// ウィンドゥ境界を考慮に入れなくてはならない
		// 表示範囲が640*480ならば、生成すべきWindowサイズは、それ以上である
		RECT r;
		InnerAdjustWindow(r,opt);
		m_hWnd = ::CreateWindow(opt.classname.c_str(),opt.caption.c_str(),
				// WS_EX_TOPMOSTを指定したいんだけどなー
						lChild | opt.style,
						CW_USEDEFAULT,
						CW_USEDEFAULT,
						r.right-r.left,r.bottom-r.top,
						hParent,NULL,hInst,NULL);
	}

 現在の画面モードがフルスクリーンモードなのか、ウィンドウモードなのかで処理が変わります。

 フルスクリーンモードの場合:GetScreenSize()で現在の画面サイズを取得し、それをウィンドウ自体のサイズに指定してウィンドウを作成します。オプションはWS_POPUP|WS_VISIBLEなので、作成された時点で、タイトルバー&境界線が無い(黒色か白色の)ウィンドウが画面に表示されます。

 ウィンドウモードの場合:InnerAdjustWindow()関数を呼び出し、optが持つクライアント領域のサイズ情報から、(タイトルバーや境界線などの幅を増やした)ウィンドウ全体の適切なサイズを計算し、RECT rに格納します。そしてそれを渡してウィンドウを作成します。デフォルトではWS_VISIBLEを設定していないので、まだこの段階では画面に表示されません(で、あってる?^^;)。

	if (m_hWnd==NULL){
		Err.Out("CWindow::CreateでCreateWindowに失敗。");
		return 1;
	}

 CreateWindowがm_hWndにNULLを返した場合、ウィンドウの作成に失敗したので、エラーメッセージを出力して、1を返します(アプリケーションは終了します)。

	//	画面のセンターに生成するように修正^^
	if ((!m_bFullScreen) && (m_opt.size_x!=0) && (m_opt.size_y!=0)) {
		int	sx,sy;
		GetScreenSize(sx,sy);
		sx	 = (sx-m_opt.size_x) >> 1;
		sy	 = (sy-m_opt.size_y) >> 1;
		SetWindowPos(sx,sy);
	}

 フルスクリーンでなく、また、ウィンドウのX、Yサイズが0で無ければ、現在の画面全体のサイズと、アプリケーションウィンドウのサイズの平均を出して、ウィンドウがディスプレイのセンターに来るように座標を修正し、SetWindowPos()で移動します(CWindow::SetWindowPos()は内部で::SetWindowPos()を呼び出しています)。

	if ((m_opt.size_x!=0) && (m_opt.size_y!=0)){
		::ShowWindow(m_hWnd, SW_SHOW);
	}
	::SetFocus(m_hWnd);

 ウィンドウのX、Yサイズが0で無ければ、ウィンドウを表示します。これ、逆に言えば、サイズをゼロにしておくと、SetDisplay()するまで、ディスプレイにウィンドウを表示させずにいることが出来ます(これ裏技かにゃ〜^^;>やねうらおさんのサンプル12で行われています)。サイズを指定した上で、表示しないでいられたらもっと便利なんですが……(と、ないものねだり>そんな機能欲しがってるのお前だけやろ^^;)

 その後、SetFocus()で作成したウィンドウにフォーカスを移します。

	//	コールバックされたときに、それぞれのWindowClassにdispatch出来るように
	//	GWL_USERDATAにthisを隠しておく。
	::SetWindowLong(m_hWnd,GWL_USERDATA,(LONG)this);

 ::GetWindowLong()という、ウィンドウ毎に固有の情報を取得できる関数があり、::SetWindowLong()は、その情報を設定する関数です。GWL_USERDATAはユーザーが任意の32ビット値を入れる事が可能で、ここではCWindow*を入れてあります。この値は、CWindow::gWndProc()で取り出され、そのウィンドウに登録されたメッセージフック関数を呼び出すのに使われています。

	//	自分のウィンドゥなので直接フックできる
	//	(というかCAppManagerに未登録の段階なので直接フックしかない)
	GetHookList()->Add(this);			//	フックを開始する

 最後に自分自身のメッセージフックを登録します。これによって、適切なタイミングでCWindow::WndProc()が呼ばれるようになります。

	return 0;
}

 全てが上手く行けば0を返して、長かったCreate関数はおしまい(おつかれさま♪)。


[InsideYaneSDK]トップに戻る
フレームワーク解説がまだ途中の人は、ブラウザの「戻る」ボタンを押してください。