2012年6月10日日曜日

wWinMain(エントリーポイント)


int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow )

プログラムの入り口という意味でエントリーポイントとも呼ばれる関数です。普通のCのプログラムではmain()に相当します。
wWinMain()という名前の頭に付くwは、「はっは、ちょ、うぇ WinMain()関数とか」という意味ではなく、3つ目の引数 LPWSTR lpCmdLineをユニコードで受け取るという意味のwです。つまりワイド文字に由来します。 ワイド文字の解説はこのサイトが分かり易いかも知れません。 この空白ウインドウを出すだけのプログラムの様にコマンドラインを処理しないプログラムでは意味のない話です。だからwをはずして三番目の引数の型をPSTRに変えても動作します。

ところでLPWSTRとかHRESULTって何なの?
・・・と思う方もいるかも知れません。これらはC++を知っていても見慣れない単語でしょう。windowsのプログラミングではこのような独自定義の型が他にもありますが、つまるところ、これらは型です。int型だとかchar型とかと同じように型です。これらは大体複雑なクラス型ではなく、単なるデータ型なので深い意味はありません。もっと知りたい方はMSDNの
Windows におけるコーディング規約
とか 文字列を使用する
を一読しておくと良いかも知れません。

wWinMain内の引数について
ここから抜粋ーーーーーーーー
hInstance: "インスタンスへのハンドル" または "モジュールへのハンドル" と呼ばれます。オペレーティング システムは、この値を使用して、メモリに読み込まれた実行可能ファイル (EXE) を特定します。このインスタンス ハンドルは、アイコンやビットマップを読み込むなど、Windows の特定の機能に必要です。
hPrevInstance: 16 ビット Windows で使われていましたが、現在の値は常にゼロなので意味を持ちません。
lpCmdLine: コマンドライン引数を Unicode 文字列として格納します。
nCmdShow: メイン アプリケーション ウィンドウの最小化、最大化、通常表示を指定するためのフラグです。
ーーーーーーーーーーーーーーーーーー
チュートリアルでは最初と最後の引数(hInstanceとnCmdShow)だけはウインドウ表示のために
InitWindow( hInstance, nCmdShow )関数の中に送っています。
中の2つの引数は使いませんが何も書かないとコンパイラがそれで大丈夫かと警告をしてくるのでその警告を出さないためにwWinMain関数の中で
hPrevInstance;
lpCmdLine;
と、何も作用がなくても書くだけ書いておくというのが一つの手です。しかしそれはそれで意味が不明なコードとなるので、それを割けるためにこれまた何もしないマクロで包んでやっているのがチュートリアルのコードです。
UNREFERENCED_PARAMETER( hPrevInstance );
UNREFERENCED_PARAMETER( lpCmdLine );

何処からも参照されていない、使われていない値という意味です。

if( FAILED( InitWindow( hInstance, nCmdShow ) ) )
  return 0;

この部分は、
1・まず関数InitWindow()を実行してウインドウを作成する。
2・その戻り値がエラーを知らせる番号だった場合はreturn 0によって関数を、ひいてはプログラム自体をここで終了させてしまう。

という意味です。
無事にウインドウが作成できた場合はプログラムはその下のメッセージループという部分に行きます。

// Main message loop
MSG msg = {0};
while( WM_QUIT != msg.message )
{
   if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
   {
    TranslateMessage( &msg );
    DispatchMessage( &msg );
   }
}

これはWindowsからのサインを受け取ってウインドウプロシージャと呼ばれるシステムに渡すための部分です。 (最後にある関数WndProc()がウインドウプロシージャです。)
まずPeekMessage()という関数でメッセージをシステムから受け取っています。よくあるWinodowsのプログラムではイベント主導でプロセスが進むため、PeekMessage()関数の代わりにGetMessage()を使います。Direct3Dのプログラムでは能動的な描画処理を書くためにPeekMessage()を使ったりします。ここをもしGetMessage()に変えてしまうとメッセージが来るまで待機してしまうために、描画などの一定時間ごとに行わせたい処理を実行できません。チュートリアルではメッセージ来ない場合は
else
{
   Render();
}
によって描画処理をするコードになっています。

メッセージを受け取った時に行っている
TranslateMessage()は文字メッセージの処理に関わるらしいです。
DispatchMessage()はそのアプリケーションで定義したウインドウプロシージャに対してメッセージの送出が行われます。この一行を削除するとウインドウは表示されたとしても、まったく操作を受け付けないプログラムになってしまいます。

ところでこのメッセージループと呼ばれる部分は文字通りwhileによってループ文となっていますよね。PeekMessage()によってmsg構造体にメッセージを取得した時に終了情報( WM_QUIT )がない限りはこのコードを繰り返す事になります。
ちなみにこのWM_QUITという終了情報を送るのはウインドウプシージャー内の
PostQuitMessage( 0 );
という関数です。
つまり一般的なプログラムの終了手順は次の様なものとなります。
1・ユーザーがウインドウを閉じる
2・ウインドウプロシージャにcase WM_DESTROYなメッセージが送られて来る。
3・ PostQuitMessage( 0 );によって wWinMain()内の while(WM_QUIT != msg.message)の条件が満たされてメッセージループを抜けて return ( int )msg.wParam; までたどり着く。

0 件のコメント: