リフレクションを用いてC#とActionScriptで定義されたメソッドを相互に呼び出す
環境はC#(VC# 2008 Express) .NET Framework 3.5 & Flash 8(ActionScript2)
(環境が最新ではないので事情が変わってるかもしれませんが)
ActionScriptで定義されたメソッドの呼び出し
呼び出されるメソッドはあらかじめActionScriptで以下のように記述しておく必要があります。
import flash.external.ExternalInterface; ExternalInterface.addCallback("hoge", null, hoge);
通常swfに定義されたActionScriptのメソッドは、XMLを作成しFlashのコンテナに渡すことで呼び出しを行います。
C#ではAxShockwaveFlashObjectsのCallFunctionにXMLを渡します。
今回は、XMLを暗黙的に作成しメソッドの呼び出しを行えるようにします。
呼び出しのイメージとしては、メソッド呼び出しを仲介するオブジェクトを介し、
FlashPlayer.hoge();
のように呼び出せるようにします。
この場合、FlashPlayer.hoge();の呼び出し時に
C#で定義されたのメソッド呼び出し
ActionScriptでC#に定義されたメソッドを呼び出す場合以下のようにします。
ExternalInterface.call("piyo");
こうするとFlashコンテナのFlashCallイベントがコールされるので、XML(_IShockwaveFlashEvents_FlashCallEventのrequestフィールド)を解析しメソッドを呼び出します。
この場合はリフレクションを用いてメソッドを呼び出すだけです。
上記のそれぞれの機能をまとめたクラス群を以下に示します。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using AxShockwaveFlashObjects; using System.Xml; using System.Runtime.Remoting.Messaging; using System.Runtime.Remoting.Proxies; namespace FlashObject { /// <summary> /// Flashオブジェクトにロードされたswfに定義されたメソッド呼び出しを簡易化するクラス /// </summary> /// <typeparam name="T"></typeparam> public abstract class CallFunc<T> : System.MarshalByRefObject where T : new() { /// <summary> /// Ploxyを介したインスタンスを作成します /// </summary> /// <param name="flashobject"></param> /// <returns></returns> public static T CreateInstance(AxShockwaveFlash flashobject) { return (T)(new Ploxy(new T(), flashobject).GetTransparentProxy()); } class Ploxy : RealProxy { T _this; AxShockwaveFlash flashobject; public Ploxy(T _this, AxShockwaveFlash flashobject) : base(typeof(T)) { this._this = _this; this.flashobject = flashobject; } public override IMessage Invoke(IMessage msg) { var mm = msg as IMethodMessage; var ret = invoke(mm.MethodName, mm.Args); return new ReturnMessage(ret, null, 0, mm.LogicalCallContext, (IMethodCallMessage)msg); } /// <summary> /// Flashオブジェクトにロードされたswfに定義されたメソッドを呼び出す /// </summary> /// <param name="methodname"></param> /// <param name="_params"></param> /// <returns></returns> private string invoke(string methodname, object[] _params) { var doc = new XmlDocument(); var invoke = (XmlElement)doc.AppendChild(doc.CreateElement("invoke")); invoke.SetAttribute("name", methodname); invoke.SetAttribute("returntype", "xml"); invoke = (XmlElement)invoke.AppendChild(doc.CreateElement("arguments")); foreach (var param in _params) { invoke.AppendChild(create_args(doc, param)); } string response = String.Empty; try { response = flashobject.CallFunction(doc.InnerXml); } catch { } return response; } /// <summary> /// 引数処理を行う /// </summary> /// <param name="doc"></param> /// <param name="param"></param> /// <returns></returns> private XmlElement create_args(XmlDocument doc, object param) { XmlElement element; if (param is int || param is uint || param is long || param is ulong || param is short || param is ushort || param is byte ) { element = doc.CreateElement("number"); element.InnerText = param.ToString(); } else if (param is bool) { element = doc.CreateElement(param.ToString().ToLower()); element.InnerText = ""; } else { element = doc.CreateElement("string"); element.InnerText = param.ToString(); } return element; } } } /// <summary> /// Flashオブジェクトからのコールバックを処理するクラス /// </summary> /// <remarks> /// <seealso cref="CallBackProxy<T>">CallBackProxy<T></seealso>のインスタンス作成を容易にするための /// ジェネリックメソッドを提供します /// </remarks> public class CallBackProxy { /// <summary> /// <seealso cref="CallBackProxy<T>">CallBackProxy<T></seealso>のインスタンスを作成します /// </summary> /// <typeparam name="T"></typeparam> /// <param name="owner"></param> /// <param name="flashobject"></param> /// <returns></returns> public static CallBackProxy<T> CreateInstance<T>(T owner, AxShockwaveFlash flashobject) { return new CallBackProxy<T>(owner, flashobject); } } /// <summary> /// Flashオブジェクトからのコールバックを処理するクラス /// </summary> /// <typeparam name="T"></typeparam> public class CallBackProxy<T> { T owner; AxShockwaveFlash flashobject; /// <summary> /// Flashオブジェクトを初期化します /// </summary> /// <param name="owner"></param> /// <param name="flashobject"></param> public CallBackProxy(T owner, AxShockwaveFlash flashobject) { this.owner = owner; this.flashobject = flashobject; flashobject.FlashCall += FlashCall; } /// <summary> /// Flashオブジェクトからのコールバックイベント /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void FlashCall(object sender, _IShockwaveFlashEvents_FlashCallEvent e) { var document = new XmlDocument(); document.LoadXml(e.request); callfunc(document); } /// <summary> /// Flashオブジェクトからのコールバック処理 /// </summary> /// <param name="document"></param> private void callfunc(XmlDocument document) { var invoke = document.GetElementsByTagName("invoke"); string name = invoke[0].Attributes["name"].Value; XmlNode _param = document.GetElementsByTagName("arguments")[0]; var t = owner.GetType(); var m = t.GetMethod(name); m.Invoke(owner, parseparam(_param.ChildNodes)); } /// <summary> /// Flashオブジェクトからのコールバック引数処理 /// </summary> /// <param name="_params"></param> /// <returns></returns> private object[] parseparam(XmlNodeList _params) { List<object> retparam = new List<object>(_params.Count); foreach (XmlNode param in _params) { if (param.Name == "number") { retparam.Add(int.Parse(param.InnerText)); } else if (param.Name == "true" || param.Name == "false") { retparam.Add(bool.Parse(param.Name)); } else { retparam.Add(param.InnerText); } } return retparam.ToArray(); } } }
C#からメソッドの呼び出しを行う
メソッドの名前が必要になるので、それらと同じメソッドを定義したクラスを用意します。
ActionScriptで
function hoge():Void{ } function hogehoge(arg:Number):Void{ } function hogehoge2():Number{ }
と定義されていれば、
public class FlashObjectCallFunc : FlashObject.CallFunc<FlashObjectCallFunc> { public void hoge(){ } public void hogehoge(int arg){ } public int hogehoge2(){ } }
このようにC#で定義します。
メソッドの呼び出しは以下のようにします。
var axfunc = new FlashObjectCallFunc.CreateInstance(axShockwaveFlash);
axfunc.hoge();
ActionScriptからメソッドの呼び出しを行う
FlashObject.CallBackProxy<App> axcb = FlashObject.CallBackProxy.CreateInstance(owner, owner.axShockwaveFlash);
Appは呼び出されるメソッドのクラスです。
ownerはAppのインスタンスです。
ActionScriptから呼び出す場合以下のようにすればAppクラスのpiyoメソッドを呼び出すことができます。
ExternalInterface.call("piyo");