WebBrowser上のJSに配列、オブジェクト、関数を渡す
昨日解決できなかった、配列やオブジェクトをJSに渡すという話をスタックオーバーフローにでも投げようと改めて調べていたら、解決しちゃったお話。
配列を作る
昨日の結論としては、「コンストラクタとして呼べないのでどうしようもない」というものだった。
のだけど、よく考えれば、Array()
は普通に関数として呼んでも機能する。試しにdynamicで呼んでみると動くではないか。
Creating JavaScript arrays and other objects from C++ - CodeProject
上の記事のような「存在しないプロパティのDISPIDを取得」することができないらしく、COM経由での要素追加はできなかった。これも push()
を使うことで達成。というかArrayの使い方としてはそっちの方が正しいよね・・・。
一度追加されてしまえば取得設定はCOMのプロパティ同様に行える。インデクサじゃなくて、数字がプロパティ名になったオブジェクト。これもType.InvokeMember
なら大丈夫。
dynamic dwindow = browser.Document.Window.DomWindow; object window = browser.Document.Window.DomWindow; var arr = window.GetType().InvokeMember( "Array", BindingFlags.InvokeMethod, null, window, new object[]{ 1, 2, 3 } ); dynamic darr = arr; darr.push(4); var val = arr.GetType().InvokeMember("2", BindingFlags.GetProperty, null, arr, new object[0]); Console.WriteLine(val); arr.GetType().InvokeMember("0", BindingFlags.SetProperty, null, arr, new object[] {"first"});
こうなると連想配列というか任意のオブジェクトを作りたくなるが、プロパティの追加ができないので・・・本当にできない?
オブジェクトを作る
これまた再掲。
.net - C# COM object with a dynamic interface - Stack Overflow
このQ&Aの中でさらっと触れられている IExpando
というインターフェイス。
On a side note, IExpando does the same job for IDispatchEx, so a JavaScript client can add new properties which can later be accesses by managed code.
ということはですよ、IExpandoにキャストできるんじゃない?
まずは空のオブジェクトを作ります。ひょっとしてと思ったら、Object()
もArray()
と似たような動きをしてくれました。
var obj = window.GetType().InvokeMember("Object", BindingFlags.InvokeMethod, null, window, new object[0]); var expando = (IExpando)obj; expando.AddProperty("prop").SetValue(expando, "value"); dwindow.hoge(obj); // hogeは適当なfunction
よっしゃ。あれ、ということは。
var expando = (IExpando)window; expando.AddProperty("prop").SetValue(expando, "value"); dwindow.eval("alert(prop)");
グローバル変数が作れる・・・!ということは。
関数を作る
ここまでの集大成ですね。
var fun = window.GetType().InvokeMember( "Function", BindingFlags.InvokeMethod, null, window, new object[]{ "alert('Hello world!')" } ); var expando = (IExpando)window; expando.AddProperty("myfunc").SetValue(expando, fun); dwindow.eval("myfunc()");
ちなみに匿名関数なのでdynamicから直接呼ぶことはできません。
もうちょっとIExpandoで遊ぶ
IExpando.GetMembers()
をしてみると、どれもPropertyとMethodの両方で登場するという。そしてwindowオブジェクトのメンバーを列挙してもArrayやDateは現れない。これはfor..in
を使った時の結果と一致します。
じゃあIExpando.GetMember()
でDate
は見つからないのかというと、ちゃんと出てきます。ちなみにType.GetMember()
では見つかりません。
IExpando.GetProperties()
とType.GetProperties()
の結果はIHTMLWindow2_**
みたいなのを覗けばほとんど同じなんですが、それぞれ存在しないものがあります。IExpando経由でしか列挙できないものとしてはlocalStorage
とか。これが実装される頃にはもはやIHTMLWindowが拡張されなくなったんですかね・・・。
その他
IExpando.AddMethod()
: サポートされていないって怒られた- コンストラクタ呼び出し: やっぱり
CreateInstance
はダメっぽい
おしまい
まとめた何かをQiitaに投げるかも?
c# - What is IExpando and where is it used? - Stack Overflow
Microsoft had high hopes for JScript, it was a primary language supported along-side C#, VB.NET and Managed C++. That didn't work out.