前回までは、主にWindowsが発生させたメッセージをHSPアプリケーション側で受け取り、それに対する処理をすることを中心にやってきました。実際には、アプリケーションはメッセージを受け取るだけでなく、メッセージを発生させることもできるのです。今回は、アプリケーションが明示的にメッセージを発生させる仕組みについて説明していきます。メッセージを発生させることをメッセージを「送信する」または「ポストする」と呼ぶということを以前に説明しましたが、この『送信』と『ポスト』は、実際には異なります。これについての説明も、メッセージのメカニズムとともにしていきます。メッセージのポストまずは「メッセージをポストする」という動作について説明していきましょう。スレッドのメッセージキューまず、『キュー』とはいったいどういうものか知らない方のために説明しておきましょう。FIFO)方式のデータ構造」を表すものです。このデータ構造では、先に格納された要素が先に取り出されるので、『待ち行列』とも呼ばれています。キューでは、最初の(先に入れられた)データを先頭、最後の(後に入れられた)データを末尾と呼び、キューの末尾に新しいデータを追加することを「ポストする」と呼びます。ウィンドウを所有しているスレッドは、ウィンドウメッセージを格納するためのメッセージキューと呼ばれるキューを持っています。通常、メッセージはいったんこのメッセージキューにためられて、スレッドがウィンドウメッセージ処理の機会を得たときに、先頭から順に処理されていきます。スレッドはウィンドウに送られてくる複数のメッセージを同時に処理することができないので、メッセージをいったんキューにためておき、先に送られてきたメッセージから順にキューから取り出してウィンドウプロシージャで処理していくのです。メッセージキューは、ウィンドウごとに準備されているのではなく、スレッドごとに準備されています。そのスレッドが所有しているウィンドウにポストされたメッセージは(別のウィンドウであっても)すべて同じメッセージキューに格納されます。HSPはシングルスレッド(スレッドを1つしか持たない)アプリケーションですから、HSPアプリケーションの1つのプロセスで作られるすべてのウィンドウ(HSPオブジェクトやコントロールなども含めて)に対して、メッセージキューが1つ存在することになります。メッセージ処理の仕組みC言語などで書かれているGUIアプリケーションプログラムは、メッセージがキューに存在するかどうかを調べるために、GetMessageまたはPeekMessageというAPI関数を呼び出します。この2つの関数は、メッセージキューにメッセージが存在する場合には、キューの先頭からメッセージの情報を取り出して、それを呼び出し元に返します。メッセージキューにメッセージが存在しない場合には、GetMessage関数は新しいメッセージを受け取るまでスレッドを停止させ、PeekMessage関数はそのまま次の処理を続行させます。アプリケーションは、これらの関数呼び出しで、メッセージを受け取ったことを知ると、そのメッセージ情報をウィンドウプロシージャに渡すためのAPI関数(DispatchMessage関数)を呼び出します。これによって、メッセージはウィンドウプロシージャに送られ、メッセージが処理されるのです。HSP内部でも、私たちからは見えないところでこれらの関数が使われています。具体的には、スクリプト中でstop命令やwait命令、await命令が実行されたときは内部ではGetMessage関数やPeekMessage関数によるメッセージの取得が行われているのです。以上のことから、メッセージキューに格納されているメッセージをウィンドウプロシージャで処理するためには、アプリケーションがGetMessage関数やPeekMessage関数を呼び出さなければならないことになります。HSPではstop命令やwait命令、await命令が実行されなければ、キューのメッセージを処理する機会が与えられないということです。適切なメッセージ処理のためにも、HSPプログラム中ではこれらの命令を実行させる必要があるということに注意しなければなりません。PostMessage関数によるメッセージのポストメッセージのポストというのは、スレッドのメッセージキューにメッセージをポストする、ということです。アプリケーションから明示的にメッセージをポストするには、PostMessage関数を呼び出します。parameter);この関数を呼び出すと、引数で指定されたメッセージ情報が、メッセージキューにポストされます。メッセージ処理の機会が与えられた時点で(GetMessage関数やPeekMessage関数が呼び出されたときに)、ウィンドウプロシージャに送られてメッセージ処理がなされます。次の図は、PostMessage関数によりポストされたメッセージの処理のメカニズムを表すイメージ図です。ポストされたメッセージはキューの最後に追加されます。メッセージ処理の機会が与えられると、キューの先頭にあるメッセージから順番に処理されていく様子がわかります。次に、「メッセージを送信する(送る)」といったときの動作について説明しましょう。「メッセージのポスト」は、通常、ウィンドウに何らかの通知をしたい場合のみに用いるものです。この場合、メッセージをポストしたら、そのメッセージに対する処理が行われたかどうかに関わらず、次の処理を続行します。ところが、「メッセージの送信」は、ウィンドウに今すぐに何かをさせたいときや、ウィンドウに何かを要求して、すぐにその結果を知りたいときに使われます。メッセージを送信すると、そのメッセージが処理されるまで次の動作には移りません。対象となるウィンドウによるメッセージ処理がなされて、その結果がわかって初めて、次の処理を行うのです。parameter);それぞれのパラメータはPostMessage関数と変わりません。これらのパラメータはそのままウィンドウプロシージャに渡されることになります。SendMessage関数のメカニズムは、対象となるウィンドウを所有しているスレッドが、呼び出しスレッドと同じであるかどうかによって異なってきます。次はこれについて説明します。SendMessage関数による同スレッド内のメッセージ送信まずは、呼び出し側スレッドとが所有しているウィンドウに対してSendMessage関数を呼び出したときの動作について説明しましょう。同じスレッドのウィンドウに対してSendMessage関数を呼び出すと、SendMessage関数は内部で直接ウィンドウプロシージャを呼び出します。これは、ちょうど呼び出し側コードから直接ウィンドウプロシージャを呼び出すのと同じになると考えることができます。上の図にある通り、SendMessage関数に渡した引数はそのままウィンドウプロシージャに渡され、ウィンドウプロシージャが返した戻り値はそのままSendMessage関数の戻り値として呼び出し側に返されることになります。呼び出し側スレッドとは別のスレッドが所有しているウィンドウに対してSendMessage関数を呼び出した場合の動作を説明しましょう。これは、例えば同じアプリケーションの別のプロセスのウィンドウや、別のアプリケーションのウィンドウに対してメッセージを送信した場合に起こります。Windowsでは、「ウィンドウプロシージャは、常にそのウィンドウを所有しているスレッドによって処理されるべき」という考えに基づいて、ウィンドウメッセージ処理を行っています。したがって、所有スレッド以外のスレッドがウィンドウプロシージャの処理をしなくても済むように、次のようなメカニズムが働いているのです。呼び出し側のスレッドでSendMessage関数が呼び出されると、送信先のウィンドウを所有しているスレッドのメッセージキューにいったんメッセージが格納されます。すると、その時点で呼び出し側のスレッドが休止状態となります。この休止状態では、このスレッドにCPU時間が割り当てられることはないので、他のスレッドに影響することはありません。メッセージを受け取った方のスレッドは、その時点ではまだ別の処理をしているかもしれません。この場合、その処理が終わるまで待つことになります。受け取り側がメッセージを処理することができるようになったとき、すなわち、GetMessage関数やPeekMessage関数が呼び出されたときに、送信されたメッセージに対しての処理がウィンドウプロシージャで行われます。このとき、送信されたメッセージは、他のPostMessage関数などでポストされたメッセージよりも優先的に処理されることになります。(このメッセージ処理を待っているスレッド(呼び出し側)を止めてしまっているのですから、このメッセージを優先的に処理するのは当然ですね。)ウィンドウプロシージャでの処理が終わって戻り値が返されると、休止状態にあった呼び出し側スレッドは再び動き出します。受け取った戻り値をSendMessage関数の戻り値として返すのです。送信とポストの比較さて、SendMessage関数とPostMessage関数の違いを理解するのに、1つサンプルを作成し、実行してみましょう。実際に、自分自身のウィンドウに向けてメッセージを発生させてみて、それをHSPのメッセージ処理の機能で取得するということを行います。どの段階でメッセージ処理が行われているかを調べることによって、いつウィンドウプロシージャが呼び出されているのかということを知ろうというわけです。ここでは、自分自身のウィンドウに送るメッセージコードとしてWM_USER以降で定義できる値を使用しています。Windowsでは、アプリケーションが独自のメッセージを使いたいときに、メッセージコード0x0400から0xBFFFまでの範囲で自由にアプリケーション独自のメッセージを定義してよいことになっています。0x0400という形で定義された定数名で、通常はWM_USER+X(Xは0以上の整数)という値を別の定数名として定義して、それを使用します。95/98/Meでは、ダイアログ制御メッセージとの衝突を避けるためにWM_USER+0x0100(すなわち0x0500)より大きい値を定義しなければならない、となっています。現在のところ0x0400~0x0500の範囲のメッセージコードでうまくいかない例を聞いたことはないですが、すべての環境でうまく動作させるため、とりあえずそのような値を使用した方がよいでしょう。PostMessageの場合まずはPostMessage関数の場合を調べてみましょう。"メッセージ1をポストします。"メッセージ1をポストしました。"メッセージ2をポストします。"メッセージ2をポストしました。lparamreturnこのスクリプトを実行させると、ウィンドウにはメッセージ1をポストします。メッセージ1をポストしました。lParam=100メッセージ2をポストします。メッセージ2をポストしました。lParam=200と表示されます。このスクリプトを実行させて分かることは、PostMessage関数を使った場合では、wait命令やstop命令を実行したところでメッセージ処理が行われているということです。wait命令やstop命令を実行したときにGetMessage関数によってメッセージ処理の機会が与えられ、メッセージキューにポストされていたメッセージが取り出されて、ウィンドウプロシージャでの処理が行われたのです。次にSendMessage関数の場合を調べてみます。上のスクリプトで、呼び出す関数をPostMessageからSendMessageに変えただけのものです。"メッセージ1を送信します。"メッセージ1を送信しました。"メッセージ2を送信します。"メッセージ2を送信しました。lparamreturnこのスクリプトを実行させると、ウィンドウにはメッセージ1を送信します。lParam=100メッセージ1を送信しました。メッセージ2を送信します。lParam=200メッセージ2を送信しました。このスクリプトを実行させて分かることは、SendMessage関数を使った場合では、関数を呼び出した時点でメッセージ処理が行われているということです。SendMessage関数を呼び出した瞬間、ウィンドウプロシージャが呼び出されていることになります。メッセージ送信命令sendmsgHSP3では、SendMessage関数を呼び出すための命令であるsendmsg命令が標準命令として実装されています。n4n1:ウィンドウハンドルn2:メッセージコードn3:wParamパラメータn4:lParamパラメータこの命令を実行すると、SendMessage関数が呼び出されます。関数の戻り値はシステム変数statに格納されます。"メッセージを送信します。"メッセージを送信しました。