2001/12/25
fscommand + Visual Basicでマルチユーザ
サンプルパッケージダウンロード
目次
- 背景
このページでは、Webからすこし離れて、FlashをWindowsアプリケーションのインターフェース構築に使う場合のマルチユーザ化について解説します。Windowsアプリケーションなど作る予定はない! という方は読む必要はありません。FlashムービーはWindowsのShockwave Flashコンポーネントを用いてWindowsアプリケーションから読み込むことができます。 読み込まれた後でもFlashのほとんどの機能はそのまま有効ですが、Flashの中でXMLSocketオブジェクトを使っている場合、XMLSocketの機能は使えなくなってしまいます。Windowsプログラムの枠を飛び越えてソケット接続を確立することはできないということです。そこで、XMLSocketを使っているFlashムービーをVisual Basic(以下VB)で読み込んだときに、ソケット機能とXML処理をVBで代替する例を以下で説明します。 このチュートリアルの筆者がなんでこんなことを試みたかというと、FACEsサーバの専用ゲームロビーとして、Windowsの常駐アプリケーションを用意しようという計画があるからです。目的はなんであれ、読まれた方のお役に立てば幸いです。
目次に戻る
- fscommandとは
fscommandはFlashムービーがFlashのブラウザに対してメッセージをおくるためのActionScript関数です。パラメータとしてcommand, argumentsの二つを指定できます。Web上でFlashを使う場合はJavaScriptに対してメッセージを送る場合によく使用されますが、今回のサンプルでは、VBで制作したWindowsプログラムに対してメッセージを送ります。
目次に戻る
- flaのコード解説
以下はWindowsプログラムから読み込むサンプルFlashムービー内のActionScriptの解説です。このサンプルはFACEsサーバのダウンロードパッケージに含まれているtest.flaの内容とほとんど同じで、このムービー単独でもサーバに接続できますが、VBに埋め込まれてXMLSocketが機能しなくなったときにfscommandを使用するようになっているところが大きく違うところです。以下、test.flaとの違いについて主に解説していきます。
以下の二つのスクリプトはマウスにくっついて動く白丸用のスクリプトで、FACEsサーバパッケージ付属のtest.flaとおなじです。sendPosという関数を用意してマウスの位置を送信していますが、sendPosの機能は後で説明するように、Flashプレーヤー上とWindowsプログラムの中では動作が異なります。1: onClipEvent (mouseMove) { 2: if (_root.selfname == "1") { 3: _root.sendPos("1", _root._xmouse, _root._ymouse); 4: } 5: }1: onClipEvent (mouseMove) { 2: if (_root.selfname == "2") { 3: _root.sendPos("2", _root._xmouse, _root._ymouse); 4: } 5: }
この関数は、test.flaにはありませんが、今回のサンプルはボタンを押したときにサーバへの接続を行うようになっているので、ボタンに対するコールバック関数として用意されています。
1: function startApp () { 2: str = "<QN app=\"test\"/>"; 3: sendStr(str); 4: }
loadEnd関数はtest.flaと同じです。が、XMSocketオブジェクトはそのFlashムービーがWindowsプログラムから読み込まれている場合機能しないので、必ず接続に失敗します。なので、onConnectで呼ばれるコールバック関数の中で、接続に成功したかどうかをチェックしなければなりません。1: function loadEnd () { 2: mySocket = new XMLSocket(); 3: mySocket.onConnect = checkConnect; 4: mySocket.connect("localhost", 8080); 5: mySocket.onClose = checkClose; 6: }
checkConnect接続時のコールバック関数で、接続が失敗しても呼ばれるので、ここでSckという変数にXMLSocketでの接続が成功(Windowプログラムから呼ばれてない)したか失敗(Windowsプログラムから呼ばれている)したかの判定結果をBooleanでいれておきます。1: function checkConnect (bOK) { 2: if (bOK) { 3: mySocket.onXML = getData; 4: Sck = new Boolean(true); 5: trace ("trueconnection"); 6: } else { 7: Sck = new Boolean(false); 8: trace ("falseconnection"); 9: } 10: }
接続が切断されたときのコールバック関数です。XMLSocketでの接続が成功している場合は接続をソケットをcloseする必要がありますが、失敗しているときはcloseしてもしょうがない(そもそもconnectしていない)ので、fscommandでquitというコマンドを外部に送っています。quitというコマンドはFlash Playerに対しては終了コマンドとして作用するので、Flash Playerでこのムービーを開いている場合は、Flash Player自体が終了します。1: function closeSocket () { 2: if (Sck) { 3: mySocket.close(); 4: } 5: fscommand ("quit"); 6: trace ("closeSocket test\n"); 7: }
XMLオブジェクトを送信する関数です。XMLSocketで接続されているときはtest.flaと同じですが、Windowsプログラムから呼ばれているときには、Sendというコマンドを定義してfscommandでXMLオブジェクトを構成する文字列を外部プログラムに送っています。1: function sendStr (str) { 2: if (Sck) { 3: theXML = new XML(); 4: theXML.parseXML(str); 5: mySocket.send(theXML); 6: } else { 7: fscommand ("Send", str); 8: } 9: }目次に戻る
- VBのコード解説
さてこんどはVBのコードの解説です。行番号は通しで付けています。このVBプログラムは、Project1というプロジェクト名になっていますがProject1には、4つのオブジェクトがあり、それぞれ、Form1(Form)、swfObj(ShockwaveFlash)、Timer1(Timer)、Winsock1(Winsock)という名前になっています(括弧内はコンポーネント名)。また参照設定を見ていただくとわかりますが、XML解析のために、MSXML 4.0というDLLを使用しています。ダウンロードはMicrosoftのサイト を参照してください。
4行目からのForm_Load()関数では、起動時の処理を行っています。vbtest.swf(vbtest.flaから生成されたSWFファイル) を読み込み、FACEsサーバプログラムが走っているマシンの名前とポート番号を指定し、接続を行っています。ここでは、サーバパッケージ付属のサンプルとおなじ、localhost、ポート8080に接続しています。1: Option Explicit 2: Dim con As Integer 3: 4: Private Sub Form_Load() 5: Dim tPath As String 6: 7: tPath = CurDir & "\vbtest.swf" 8: swfObj.Movie = tPath 9: 10: Winsock1.RemoteHost = "localhost" 11: Winsock1.RemotePort = 8080 12: Winsock1.Connect 13: con = 1 14: End Sub 15:
SWFファイルから発行されたfscommandを解釈します。quitというコマンドはFlash Playerではデフォルトで停止コマンドとして解釈されますが、この例はオリジナルアプリケーションですので、アプリケーションの終了処理を書く必要があります。あと、サーバへのXMLオブジェクト送信用にSendというコマンドを独自に定義して使用しているので、Send専用の処理を書いています。16: Private Sub swfObj_FSCommand(ByVal tCmdstr As String, _ 17: ByVal tArgstr As String) 18: 19: If (tCmdstr = "quit") Then 20: Winsock1.Close 21: Winsock1.LocalPort = 0 22: Call EnableTimer 23: con = 0 24: ElseIf (tCmdstr = "Send") Then 25: SendStr tArgstr 26: End If 27: 28: End Sub 29:
アプリケーション終了のタイミング調整にTimerを使っているので、その関連処理です。30: Private Sub EnableTimer() 31: Timer1.Enabled = True 32: End Sub 33: 34: Private Sub Timer1_Timer() 35: Unload Me 36: End Sub 37:
fscommandによってswfからSendコマンドが送られて来たときのために、SendStrという関数を用意しています。WinsockのSenddataメソッドで、文字列をFACEsサーバに送信しています。XMLSocket(とFACEsサーバ)は0バイト文字(VBではvbNullChar)をXMLオブジェクトの終端文字として解釈するので、送信時にvbNullCharを付加しています。38: Private Sub SendStr(str As String) 39: If con = 1 Then 40: Winsock1.SendData str & vbNullChar 41: End If 42: End Sub 43:
サーバからXMLオブジェクトを受信したときの処理です。Workという文字列変数に保存して、XMLオブジェクトとしての処理を次で定義しているProcessXMLという関数で行っています。44: Private Sub Winsock1_DataArrival(ByVal bytesTotal As Long) 45: Dim Work As String 46: Winsock1.GetData Work 47: ProcessXML Work 48: End Sub 49:
サーバから受信した文字列の処理を行っている関数です。引数文字列をXMLドキュメントとしてロード、パースし、ルートノードの名前と属性値に応じて処理を分岐しています。処理内容としては、test.flaのgetData関数で行っていることをそのまま代替しています。VBプログラム側からswfの関数を直接呼び出す方法がない(もしくは見つからない)ので、Shockwave Flashコンポーネントの、TSetPropertyメソッドとSetVariableメソッドを用いて同等の処理を実現しています。50: Private Sub ProcessXML(str As String) 51: Dim xml As DOMDocument 52: Dim docRoot As IXMLDOMNode 53: Dim n As String 54: Dim x As Double 55: Dim y As Double 56: 57: Set xml = New DOMDocument 58: xml.loadXML str 59: Set docRoot = xml.documentElement 60: 61: If (StrComp(docRoot.nodeName, "P", vbTextCompare) = 0) Then 62: If docRoot.Attributes.getNamedItem("n").nodeTypedValue = "1" Then 63: swfObj.TSetProperty "_root.mouse1", 0, _ 64: docRoot.Attributes.getNamedItem("x").nodeTypedValue 65: swfObj.TSetProperty "_root.mouse1", 1, _ 66: docRoot.Attributes.getNamedItem("y").nodeTypedValue 67: ElseIf docRoot.Attributes.getNamedItem("n").nodeTypedValue = "2" Then 68: swfObj.TSetProperty "_root.mouse2", 0, _ 69: docRoot.Attributes.getNamedItem("x").nodeTypedValue 70: swfObj.TSetProperty "_root.mouse2", 1, _ 71: docRoot.Attributes.getNamedItem("y").nodeTypedValue 72: End If 73: ElseIf (StrComp(docRoot.nodeName, "N", vbTextCompare) = 0) Then 74: swfObj.SetVariable "_root.selfname", _ 75: docRoot.Attributes.getNamedItem("n").nodeTypedValue 76: ElseIf (StrComp(docRoot.nodeName, "D", vbTextCompare) = 0) Then 77: If docRoot.Attributes.getNamedItem("n").nodeTypedValue = "1" Then 78: swfObj.TSetProperty "_root.mouse1", 0, 0 79: swfObj.TSetProperty "_root.mouse1", 1, 0 80: ElseIf docRoot.Attributes.getNamedItem("n").nodeTypedValue = "2" Then 81: swfObj.TSetProperty "_root.mouse2", 0, 0 82: swfObj.TSetProperty "_root.mouse2", 1, 0 83: End If 84: End If 85: End Sub目次に戻る