こける Wired-Winsockを使ってみようぜ-6.Windowが固まっちゃうなぁ'97/12/07

6.Windowが固まっちゃうなぁ


今回はWinsockとは直接関係ありませんが、次回からの「非同期型」の為の準備です。

さて、前回までのgethostbyname,gethostbyaddr,connect,send,recvはすべて「同期モード」という奴で、スレッドでも使わないことには「凍結したように見えて仕方ない」状態でしたね。
コンソールアプリケーションならまだしも、普通のアプリケーションでこれはないでしょ、という感じです。
しかし、「通信した結果」を得たいのなら、結果が返ってくるまで関数から戻ってこれないのは仕方ないじゃないですか?
Windowを固まらせたくないならば、スレッド使えばいいじゃない。
とまぁ、そういう言い分もあるでしょうけどね。

WinsockはもともとUnixのSocketを参考に作られていますが、「Win」sockです。Windows専用部分があるわけです。
「結果はWindowへのメッセージでもらう」だから、関数自体はさっさと返ってくる。ってのがWinsockの非同期モードです。
あ、勘違いしないでください。Unixのsocketにも非同期モードはあります。ただ、「結果はWindowへのメッセージをもらう」ってところが違うのです。

関数を呼び出すときに、「結果をもらうWindowのハンドル」と「結果用のメッセージ番号」を引数として渡します。
すると後でそのWindowへのメッセージとして結果が届くわけです。たとえエラーだとしても。

そうすると普通にFormへメッセージを送ってもらってもいいんですが、これだとライブラリとかコンポーネントとかにする時にちょっと困ります。
そりゃ、Formをサブクラス化するようなライブラリを作ってもいいんですが。
こういう時には、メッセージを受け取る為のWindowハンドルがあれば良いですね。
FormのVisibleをFalseにしてもいいんですが、Formだとちと大袈裟すぎます。
Delphiは、こういう時のために「AllocateHWnd」「DeallocateHWnd」というライブラリを用意してくれています。
TTimerコンポーネントもこれを使ってWindowsからWM_TIMERメッセージをもらっています。
タスクトレイとか、メッセージを受け取らなきゃならないコンポーネントやオブジェクトを作ることになったら思い出してください。
スレッドを作るようなコンポーネントも、結果をPostMessageで伝える場合は有効でしょう。

AllocateHWndは、「引数のMethodをウィンドゥプロシージャとして持つ不可視のWindow」を作成するルーチンです。
function AllocateHWnd(Method: TWndMethod): HWND;
Methodってのがウィンドゥプロシージャ。
TWndMethod = procedure(var Message: TMessage) of object;
使うときの注意は、「必要のないメッセージもMethodで処理すること」です。
これを忘れると、時々変なことが起こります。変なことは終了際に多いですね。例えば「Windowsが終了できない」とかね。
自分に必要の無いメッセージはDefWindowProcを使って処理します。
function DefWindowProc(hWnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT;
hWndは、AllocateHWndで取得したWindowハンドル。
Msgは、Methodの引数Message.Msg。
wParamとlParamはそれぞれ、Message.wparamとMessage.lParamをそのまま入れます。
返り値のLRESULTは、Message.Resultに入れてMethodからExitします。
TXXXXクラスのメソッド、WndProcをAllocateHWndのMethodに指定するとこんな感じ。
Methodと説明してきましたが、引数の名前なので、実際には何でも良いのです。
procedure TXXXX.WndProc(var Msg:TMessage);
begin
  if Msg.msg=処理したいメッセージ then begin
    したかった処理;
  end
  else begin
    Msg.Result:=DefWindowProc(保存しておいたAllocateHWndの返り値,Msg.Msg,Msg.wParam,Msg.lParam);
  end;
end;
DeallocateHWndは、AllocateHWndで作った不可視Windowを片づけるルーチン。
procedure DeallocateHWnd(Wnd: HWND);
片づけ忘れるとメモリリークです。ちゃんと片づけましょう。

これらを使って自分で作成したライブラリやコンポーネントにメッセージを送ってもらうことができます。

そうそう、AllocateHWndもDeallocateHWndもFormsユニットにありますから、usesするのを忘れないでくださいね。

今回は少し短かったのですが、これで終わり。
サンプルプログラムもありません。

次の話
[表紙] [Program Files] [オブジェクト指向異聞] [プログラム未整理知識] [Winsockを使ってみようぜ] [だべり] [What's New] [書いた奴] [リンク]