Hatena::Groupmoz-addon

Ci.nsIZIGOROu

2007-10-20記事書けない

JScriptで始めるネットワークプログラミング(2) - SctiptXを使ったEchoサーバー

| 05:30 |  JScriptで始めるネットワークプログラミング(2) - SctiptXを使ったEchoサーバー - Ci.nsIZIGOROu を含むブックマーク はてなブックマーク -  JScriptで始めるネットワークプログラミング(2) - SctiptXを使ったEchoサーバー - Ci.nsIZIGOROu

前回の「 JScriptで始めるネットワークプログラミング - Echoサーバー編 - Ci.nsIZIGOROu - Mozilla 拡張機能勉強会」では、COMのイベントハンドラ登録が余りに気持ち悪いことがご理解頂けたかと思うのですが、ScriptXと言うActiveXをインストールするともう少し綺麗に書けます。

論より証拠。まずはソース。

var EchoServer = function(port) {
  this.socket = WScript.CreateObject("MSWinsock.Winsock");
  this.sink = WScript.CreateObject("ScriptX.Factory").NewEventSink(this.socket, this);
  this.sctl = WScript.CreateObject("ScriptControl");

  this.sink.handler("ConnectionRequest") = this.onConnectRequest;
  this.sink.handler("Connect") = this.onConnect;
  this.sink.handler("DataArrival") = this.onDataArrival;
  this.sink.handler("Error") = this.onError;

  this.sctl.Language = "VBScript";
  this.sctl.AddObject("WScript", WScript);
  this.sctl.AddCode('Function GetData(wsock): Dim brcv: wsock.GetData brcv, vbstring: GetData = brcv: End Function');

  this.socket.Close();
  this.socket.LocalPort = port;
  this.socket.Listen();
};

EchoServer.prototype = {
  run: function() {
    while (true)
      WScript.Sleep(1000);
  },
  GetData: function() {
    return this.sctl.Run('GetData', this.socket);
  },
  onConnectRequest: function(reqID) {
    WScript.Echo("reqID: " + reqID);

    this.ctx.socket.Close();
    this.ctx.socket.Accept(reqID);

    WScript.Echo("Accepted");
  },
  onConnect: function() {
    WScript.Echo("Connect");
  },
  onDataArrival: function(bytesTotal) {
    WScript.Echo("Bytes: " + bytesTotal);
    var rcv = this.ctx.GetData();
    WScript.Echo("Data: " + rcv);
    this.ctx.socket.SendData(rcv);
  },
  onError: function(eNum, eDesc, eScope, eSource, eHelpFile, eHelpContext, eCancel) {
    WScript.Echo("desc: " + eDesc);
    this.ctx.socket.Close();
    WScript.Quit();
  }
};

var server = new EchoServer(7777);
server.run();

ポイントは、ScriptXのNewEventSink()メソッドによって得られるsinkオブジェクトです。

sink.handler(EventName) = handler;

と言う形でイベントハンドラに関数を直接代入出来ます。

注意しなきゃいけないのは、ハンドラ内のthisです。これはsinkオブジェクトそのものを指しています。

従ってNewEventSink()の第二引数にオブジェクトを渡しているのですが、これはハンドラ内ではthis.ctxで取得出来ます。

今はハンドラに関数を直接代入しちゃってますけど、リスナーのようなパターンを使って上手くwrapしてやるといい感じになると考えてます。

nanto_vinanto_vi2007/10/23 01:14> これは「引数の数が一致していません。または不正なプロパティを指定しています。」などと言われて怒られます。

これは
new sctl.Modules.Item(1).CodeObject.TestObject()

(new sctl.Modules.Item(1)).CodeObject.TestObject()
と解釈されて、sctrl.Modules.Item がコンストラクタではないためですね。
new (sctl.Modules.Item(1).CodeObject.TestObject)()
とすれば無事オブジェクトを生成できます。

ZIGOROuZIGOROu2007/10/23 02:35> id:nanto_viさん

いつもありがとうございます。なるほど、とても納得しました。
実はこれはとあるネタの複線だったりするんで、そう書けるとなると相当楽になりそうです。

nanto_vinanto_vi2007/10/26 23:37> 何故Itemで切れちゃうのかは分からない。

なぜかといわれれば、new演算子の優先順位が関数呼び出しより高いからというのが答えになるでしょうか。でないと new Date().getTime() なども期待したとおり動きませんし。

ZIGOROuZIGOROu2007/10/27 02:42なるほどー。勉強になります。