PreProcessMessageメソッドとは?
ここではPreProcessMessageというメソッドの深層を探ることを通して Windowsプログラムの概略を説明しています。
MSDNライブラリの説明だけでは分かりにくい
System.Windows.Forms名前空間のControlクラスにPreProcessMessageという メソッドが存在します。既存のコントロールから、継承したコントロールを作成 する時にお目にかかったことのある方もいるかもしれませんが、まずはMSDNライブラリ の説明を見てみます。
C#(.NET)以外、例えばC++などでWindowsプログラムを組んだことのある方なら、 この説明でこのメソッドが一体どのようなものなのか大体予想できると思います。 しかし、そうでない方にとってはいまいちピンと来ない説明だと思います。そこで 順を追って、このPreProcessMessageというメソッドをみていきたいと思います。
全てはRunメソッドから始まる
Visual Studio .NETなどでWindowsフォームアプリケーションを作成すると、 「アプリケーションのメイン エントリ ポイントです。」というコメントの後に 次のようなメソッドが定義されています。
{
Application.Run(new Form1());
}
C言語などでもお馴染みのmain関数に相当するものです。この中では 唯一Runというメソッドが呼び出されていますが、全てはこの Runというメソッドから始まります。ではその後は一体 どうなっているのでしょうか?VC#やVBだとこの後の処理は隠蔽され、 「イベントが発生したらこのメソッドが呼び出される」ということは 分かっていても、一体誰がそのメソッドを呼び出すのかは直接見えないように なっています。このあたりのことを説明する前にまず、イベントの発生 とその通知について簡単に説明しておきます。
Windowsプログラムの心はメッセージループにある
通常ウィンドウを持ったWindowsプログラムは起動しても特に何も動作 しないのが普通です。マウスがクリックされたり、キーボードの キーを叩いたりすると、それに伴って動作をします。これらの イベントはWindows内部ではメッセージという形で扱われています。 つまり、マウスからの入力などは、まずWindowsが一手に引き受けて、 「マウスがクリックされました」という趣旨のメッセージを該当ウィンドウ に送信しています。そして、各ウィンドウはそのメッセージを受信して 対応する処理をするというわけです。
次に各ウィンドウがどのようにしてメッセージを処理するかという話 に入ります。まずWindowsから送られたメッセージは一旦、ウィンドウ毎に 作られたメッセージキューというバッファに入れられます。これはメッセージ の取りこぼしを防ぐ簡単な方法です。さて、ここで考えてみます。目の前に 次々と溜まっていくメッセージがあり、それを順次取り出して、メッセージ によって処理を振り分けるというプログラムを書くとしたら、どのように したらよいでしょうか? もはや、ご想像の通りだと思いますが、C言語でいうところのwhile文を使用して 「ウィンドウが破棄されましたよ」というメッセージを受信するまで メッセージキューからメッセージを取り出して、処理していけばいいことが わかります。それをごく自然に表現したのが以下の処理です。
{
DispatchMessage(&msg);
}
GetMessageというのはメッセージキューからメッセージを取り出して、 msgという変数に格納して通常trueを返してくれます。しかし、ウィンドウが破棄 された時だけfalseを返すので上記のように記述できます。そして、DispatchMessage という関数を呼び出すことによって、予め登録しておいたウィンドウプロシージャ という関数をWindowsに呼び出してもらうよう依頼します。するとWindowsは 適当なタイミングで我々の登録しておいたウィンドウプロシージャという関数 にメッセージを引数に渡して呼び出してくれます。あとは、ウィンドウプロシージャ の中で、switch文などを用いてメッセージによって処理を振り分ければいいことに なります。
PreProcessMessageはDispatchMessage関数の前に呼ばれる
さて、前置きが長くなってしまいましたが、.NET Frameworkでもほぼ同じような 処理をしています。Runメソッドが呼び出されると2,3のメソッドを経由して 通常LocalModalMessageLoopという内部メソッドが呼び出されます。この中で 先に説明したようなwhile文によメッセージループが行われているというわけです。
では本節の本題PreProcessMessageは何処で呼び出されているでしょうか? ご想像の通り、GetMessage関数とDispatchMessage関数の間で呼び出されています。 それでは、.NET Frameworkにおけるウィンドウプロシージャは?というと、 Controlクラス内にある、WndProcというメソッドがそれにあたります。 このメソッドは多くの方が扱ったことがあるのではないでしょうか? VBなどではサブクラス化というテクニックがありましたが、 .NETではこのWndProcをオーバーライドすることによって実現できます。
PreProcessMessageの存在意義?
さて、ここでひとつの疑問が浮上します。何故WndProcひとつにせず、 PreProcessMessageなるメソッドが存在するのか?ということです。 これには主に、キーボード入力に関する処理が関わっています。 実はキーボード入力はWndProc内のみでは処理されておらず、PreProcessMessage 内でも処理されています。これはコマンドキーなどが常に通常のキー よりも先に処理されなければいけないからです。このことについては、節を変えて 書きたいと思っています。