WindowsAPI
キーボードを押されたことをどうやって知るか。
クリックされたのをどうやって判別するか。
その答えはウインドウプロシージャとメッセージキューにあります
〜メッセージキューとウインドウプロシージャ〜
この回のプログラムは実行しないでください。
実行しても正しく終了しないので気をつけてください。
実行した場合はタスクマネージャーから消せますし、再起動すれば消えます。
前回のプログラムは一つ疑問じゃありません? なんでドラッグしたりボタンを押したりできるのか。
僕らはそんなプログラム書いた覚えないですよね?
そもそもキーボードを打ったり、マウスでクリックしたときに何が起こるのか。
例えば僕が自分の作ったウインドウにマウスを持っていってクリックしたとします。
その段階で何が起こるか。
実はウインドウズから僕のウインドウに向かってメッセージが送られているのです。
クリックにしてもそう。キーボードを押しても。ウインドウの「終了」ボタンを押してもそう。
ウインドウズが「キーボード押されたよ!」とか「クリックされたよ!」ってメッセージをウインドウに向かって伝えてきます。
じゃあ、クリックされたときにウインドウに文字を表示させようとしたら何をしたらいいのか。
答えは簡単。そのメッセージを受け取り。それがクリックのメッセージだったら文字を表示するようにすればいいのです。
じゃあメッセージを受け取ったり、それがどのメッセージなのかを区別するにはどうしたらいいのか
それをこれから話しましょう(^^
ウインドウをクリックしたりドラッグしたりなど、ウインドウにたしいて何かすると、ウインドウズはメッセージを送ってきます。
それを受け取るのがGetMessage関数です。
この関数でウインドウに送られてきたメッセージをキャッチすることが出来ます。
「じゃあこれでメッセージを受け取って、それに応じて文字を出したりすればいいんだね!」
ざ〜んねん。そうではない。もう1段階ある。
実はメッセージの処理はWinMain関数ではやらない。
確かにこの段階でメッセージは届いてるんだけど。もしウインドウが2こあったら?
2こならまだいいよ。10こあったら?50こあったら?普通はそんなに無いと思うけれど
全てのメッセージがここに来ることを考えたらここでメッセージを処理するのは賢くない。
ウインドウに来たメッセージはそのウインドウが処理するべきなのです。
それがウインドウプロシージャって関数。そういう関数が用意されてるわけじゃなくて、そういう仕事をする関数。
つまりウインドウ1つ1つに それぞれメッセージを処理する関数を作って。
WinMainの方ではメッセージを受け取っては配り。受け取っては配るという作業をすればいいでしょ?
そうすればウインドウに来たメッセージはそのウインドウが処理をすることができるもんね
このほうが効率がいいでしょ?
とにかくプログラムを見てみよう。
#include <windows.h> じっこうしちゃだめよ 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 = DefWindowProc; 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; }新しく追加れたのは2行だけです。
何をしてるのか解説しましょう。
GetMessage関数はメッセージキューからメッセージを取り出します。
メッセージってのは沢山送られてくるので。その保存場所が用意されるんです。それがメッセージキュー。
ここにウインドウズはどんどんどんどん ウインドウに何かあるたびにメッセージを入れてきます。
このGetMessage関数でそのキューの中にあるものを1つづつ取り出してるわけです。
それを受け取るための MSG っていう構造体。メッセージ専用のものです。
用意しておいたこれを GetMessage関数の引数にして受け取っています。これでここにメッセージが入るわけです。
次の引数は、どのウインドウのメッセージを受け取るのか選ぶものですが、全部受け取りたいのでNULLで
その次とその次はどんなメッセージを受け取るのか選ぶものですが、これも全部受け取りたいので0で。
これで変数MSGに メッセージを1つ取り出せました。今度はこれをウインドウプロシージャに渡さなきゃいけません。
それをするのがDispatchMessage関数です。これが変数MSGをウインドウにばらまきます。
正確にはウインドウズに渡しています。そしてウインドウズがウインドウプロシージャに渡すのです。
ん〜いうならば
「受け取った手紙を またポストに入れて ウインドウプロシージャに届けてもらってる」
感じです。自分では渡せないわけです。
じゃあ、そのウインドウプロシージャはと言えば
#include <windows.h> じっこうしちゃだめよ 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 = DefWindowProc; 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; }これです。この茶色いところ。
ウインドウクラスのところで ウインドウプロシージャは設定します。
そして そのウインドウクラスを使ったウインドウのメッセージが このウインドウプロシージャに行くわけです。
今回はウインドウズにはじめから用意されている ごく普通のウインドウプロシージャを当てはめました。
ウインドウの最大化や最小化など基本的なメッセージに対して基本的な動作が出来ます。
さて、ここまでのことをまとめてみましょう
その1:
ウインドウズがメッセージキューにどんどんメッセージを入れます。
クリックしただの、ウインドウを動かしただの色々なもの。
プログラムではGetMessage関数を使って
そのメッセージキューから1つだけメッセージを取り出します。その2:
ウインドウに来たメッセージなんだから仕事はウインドウにやらせましょう。
と、いうことでウインドウプロシージャにメッセージを渡すように
ウインドウズに頼みますその3:
ウインドウズは「しょうがねーなー」と思いながら
そのウインドウのウインドウクラスに登録されている
ウインドウプロシージャを呼び出して
「こんなメッセージ届いてましたよ(^^」
と、届ける。
ウインドウプロシージャは メッセージに合わせて
色々な処理をする。と、こういうことなんです。
やってることは キューからメッセージを取り出し。それをウインドウプロシージャに渡し、そこで処理をしている
だけのことです。
簡単でしょ?
大切なのでもう1度言います
キューからメッセージを取り出し。それをウインドウプロシージャに渡し、そこで処理をしている
のです。どうしてwhile文の中に入ってるのか。なんでMessageBoxが要らないのかは次回話します。
これでまともになってきましたね。
ただ、ちゃんと実行させることはできません。
次回へ引き継ぐ!
まとめ: ウインドウへの操作は全て メッセージ として専用の保存場所 メッセージキュー に溜められる。 プログラムではそこからメッセージを1つ取り出し。ウインドウプロシージャに回す。 ウインドウプロシージャではメッセージごとにそれに合った処理をする。 |