WindowsAPI
前回のとこは理解出来ました?
メッセージを取り出してプロシージャに渡して プロシージャが処理するってもの。
んじゃあ 今回は、そのプロシージャとやらを作ってみましょうか!!
〜ウインドウプロシージャを作る〜
勉強するより見た方が早い。これです。
#include <windows.h> このプログラムも まだ実行しちゃダメ。 LRESULT CALLBACK WndProc(HWND hwnd,UINT msg,WPARAM wp,LPARAM lp){ return DefWindowProc(hwnd,msg,wp,lp); } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, int nCmdShow){ HWND hwnd; WNDCLASS windowclass; MSG msg; windowclass.style = CS_HREDRAW | CS_VREDRAW; windowclass.lpfnWndProc = WndProc; windowclass.cbClsExtra = 0; windowclass.cbWndExtra = 0; windowclass.hInstance = hInstance; windowclass.hIcon = LoadIcon(NULL , IDI_APPLICATION); windowclass.hCursor = LoadCursor(NULL , IDC_ARROW); windowclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); windowclass.lpszMenuName = NULL; windowclass.lpszClassName = TEXT("oyabunn"); if(!RegisterClass(&windowclass))return -1; hwnd = CreateWindow(TEXT("oyabunn") , TEXT("こんちは世界!") , WS_OVERLAPPEDWINDOW |WS_VISIBLE , 200 , 200 , 200 , 200 , NULL , NULL ,hInstance , NULL); while(GetMessage(&msg,NULL,0,0))DispatchMessage(&msg); return msg.wParam; }これがウインドウプロシージャだ!!
プロシージャがウインドウズに呼び出されるものだってのは前回言いましたよね?
なので、関数の前は LRESULT CALLBACK ってなってます。(コレ決まりごとね。)
さて、その引数です。ウインドウズから渡されるのはメッセージになってるはずですね。
2個目がメッセージになってます。
だから、キーボードが押されたら、
2個目の引数を受け取ったmsgに「キーボード押された」ってメッセージが入ってる。
さて、自作のウインドウプロシージャですが。今のままでは何もしていません。
自分が受け取ったメッセージをそっくりそのままDefWindowProc関数に渡すだけです。
自分のプロシージャを呼び出すようになっただけで。実は前回と何も変わらない。
このウインドウプロシージャを、自分のウインドウに登録します。
「このウインドウに来たメッセージは こいつがしょりします!」ってこと
windowclass.lpfnWndProc = WndProc;
ってところですね。これでOK。
〜メッセージを処理してみよう〜
せっかくだから1つぐらい来たメッセージを処理してみましょうか。
プログラムを終了させてみよう。
終了をどうやるか。人をクビにするときは「君明日からこなくていいよ」って言えばOK。ではウインドウはどうするか。
いつもならウインドウの×を押せば消せますよね?
ウインドウの×ボタンを押すと WM_CLOSEってメッセージがキューに入ります。
ウインドウを消すのはDefWindowProcです。
メッセージを受けたDefWindowProcがDestroy関数でウインドウを消して WM_DESTROY メッセージを送ることになってます。
つまり
WM_CLOSE ⇒ ウインドウを消す(DefWindowProcがやる) ⇒ WM_DESTROY が出てくる。
ってことです。
「ウインドウを消されたんなら もういいんじゃないの?」
いえ違います。ウインドウは消えましたがプログラムは終了していません。
WM_DESTROY が来たらプログラムを終わらせなきゃいけません。どうやるか?
PostQuitMessage()関数を実行します。
これは メッセージキューにWM_QUITメッセージをポストするものです。
要するに自分に向かってメッセージを送るわけ。
これで何が起こるか?
実は前回触れなかったんですが。GetMessage関数で メセージを受け取るよね?
これ、成功すると1を返り値にするわけ。だからwhile(1)で永遠にDispatchでメッセージを送り続けるわけ。
(ちなみに キューが空っぽの時はプログラムは止まる)
ただ、WM_QUITメッセージを受け取ったときは別。返り値が0になる。
GetMessage関数の返り値 WM_QUIT以外を受け取ったとき 1 WM_QUITを受け取ったとき 0 0になるとwhile(0)なんだから Dispatchメッセージをやらないで
無限ループを抜け出して、そのまま終了ということになる。
whileループのところ。(while(GetMessage(&msg,NULL,0,0))DispatchMessage(&msg);) WM_QUIT以外を受け取ったとき while(1)DispatchMessage(&msg); WM_QUITを受け取ったとき while(0)DispatchMessage(&msg);
つまりPostQuitMessageってのは 無限ループを抜け出すために 自分自身へ終了メッセージを送るものなの。
Windowsプログラムは メッセージを処理するプロシージャが全てで、メイン関数は何もしないのが基本。
ここが今までと違うでしょ?
だからプログラムを終了するために こんなふうに無限ループを抜け出してプログラムを終了させる専用のメッセージが
用意されてるわけ。
#include <windows.h> LRESULT CALLBACK WndProc(HWND hwnd,UINT msg,WPARAM wp,LPARAM lp){ switch(msg){ case WM_DESTROY: PostQuitMessage(0); return 0; default: break; } return DefWindowProc(hwnd,msg,wp,lp); } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, int nCmdShow){ HWND hwnd; WNDCLASS windowclass; MSG msg; windowclass.style = CS_HREDRAW | CS_VREDRAW; windowclass.lpfnWndProc = WndProc; windowclass.cbClsExtra = 0; windowclass.cbWndExtra = 0; windowclass.hInstance = hInstance; windowclass.hIcon = LoadIcon(NULL , IDI_APPLICATION); windowclass.hCursor = LoadCursor(NULL , IDC_ARROW); windowclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); windowclass.lpszMenuName = NULL; windowclass.lpszClassName = TEXT("oyabunn"); if(!RegisterClass(&windowclass))return -1; hwnd = CreateWindow(TEXT("oyabunn") , TEXT("こんちは世界!") , WS_OVERLAPPEDWINDOW |WS_VISIBLE , 200 , 200 , 200 , 200 , NULL , NULL ,hInstance , NULL); while(GetMessage(&msg,NULL,0,0))DispatchMessage(&msg); return msg.wParam; }
このプログラムは実行してもいいですよ。ちゃんと動きます。
こんなのが出たと思います。ちゃんと サイズ変更も 最大化最小化。そして終了もできると思います。
プロシージャの中を書き換えましたね。
さっきいった通り、ウインドウを消すのはDefWindowProcがやってくれます。
消し終わった時に奴が出す WM_DESTROY を受け取ったら。 PostQuitMessageでメッセージを出して
無限ループを抜け出して プログラムを終了するわけです。
簡単でしょ?
ちなみに
return msg.wParam
ってのは こうやって書く約足になっているので。守りましょう。ちなみに今までプログラムを実行しちゃダメ って言ったのは。DefWindowProcはウインドウは消してくれるけど
無限ループは抜け出させてくれないから、ウインドウは消えてもプログラムはずっと動いたままってことになるから
危険なわけね。
今回からはちゃんと終了できるので。安心安心(^^
まとめ: |