VB6.0におけるDoEventの動作(妄想)

ちょっとメモ。多分に妄想が入っているけど、確度が高いと思われる推論ということで。
DoEventは他のコントロールのイベントQにイベントが入っていて処理待ちになっているときに、そっちに制御を移す機能がある。非同期プロセスで処理開始シグナルを送るようなイメージだけど、どうもそんな高級なものではないようだ。
↑のエントリのトラブルでは、WinsockコントロールのDataArrivalイベント内で組み立てた応答電文を送信。電文の送信完了待ちがしたくてDataArrivalをExitせずにDoEventしながらSendCompleteイベントが叩かれるのを監視。監視にはPublic宣言したBoolean変数を使っていた。

ここにはVB6.0のDoEventの振る舞いに関する重大な思い込みがある。プログラムを書いた人はDoEventしたイベントルーチンはそのまま実行を続けて、他のイベントルーチンが一見「非同期に」処理を開始するかのように思って書いたようだけど、どうも動きを分析したところDoEventというのは、VBランタイムのイベントQの探索とイベントルーチン呼び出しを「同期的に」実行する文、ということらしい。

人の期待する動作は、送った電文のSendCompleteが叩かれてからDataArrivalをExit。その後、通信相手からの電文が届いて、改めてDataArrivalが叩かれるというものだけど、トラブル発生時には次のような動きをしていた。Aは問題のVBプログラム。Bは通信先。

  1. A:DataArrival内でSendDataを叩いて電文を送信。
  2. B:データを受信した通信先が次の電文を送信。
  3. A:DoEventを実行。VBランタイムはイベントQの探索を行いDataArrivalが実行される。SendCompleteは実行されない。
  4. A:DataArrival内でSendDataを叩いて電文を送信。

以下スタックオーバーフローするまで手続きの再帰呼びだしが続く

こんな感じ。SendCompleteではなくDataArrivalが先に実行されるのはWinsockコントロールのイベント発生チェックがDataArrivalの方がSendCompleteよりも先に記述されているからだと思われる。これが今まで問題にならなかったのは、VBプログラムのSendCompleteイベント発生が、たまたまDataArrivalイベントよりも先に発生していたからだ(通信相手の応答時間と、受け取った後のVBプログラムのプログラムカウンタがどこを指しているかという運の要素と2つの組み合わせによる)。ここの部分を「たまたま」と捉えられるかどうかが、非同期通信処理を書けるかどうかの境界になるんだと思う。あとはVB6.0におけるDoEventの実行時の振る舞いへの理解か。

こんな事はMSDNとか調べれば載っていることなのかもしれない。現場に入っていて調べ物ができない状態で妄想を膨らませた結果がコレ。まあ、また来週調べモノはしてみよう。ひとまず覚書。

ああー、ひょっとすると、SendCompleteってのは全部Qに溜まっているわけじゃなくて、DataArrivalしたらリセットされちゃうとか? あ、いや、SendDataしたら、の方がありそうだ。うーん、どうなっているんだ? まあいい。今日は休みだ、休み。