お久しぶりです。 今回、ビジネス的な圧力により、他社製Webアプリから 自社C#製アプリと連携する必要が出てきました。 こちら側にCOMインタフェースを備えregasmし、JavaScriptでnew ActiveXObjectして メソッドを呼び出したりプロパティを参照したり 設定したり…までは出来ました。 しかし、COM側からのイベントをJSでフックする方法がわかっていません。 イベント名をCallbackとすると、IE11のF12ツールでオブジェクトを確認すると「add_Callback」「remove_Callback」というメソッドが自動で出来ているのが見えたので、 これをなんとかすればいけるかなと思い _obj.add_Callback(function(){ alert('ほげ'); }); と試してみたのですがクラスはオートメーション云々と言われます。 ググっても、C++製のちゃんとしたActiveXとの連携しか 情報が出てこないので困り果ててます。 どなたかご教示いただければ助かります…
■No33249に返信(kikuさんの記事) > 自身で検証したことはありませんが > わかりやすく説明しているブログを見つけたので貼っておきます。 > > http://qiita.com/tomochan154/items/1ce33f2aef167c0fed9d > ありがとうございます。 職場から書けなくて遅くなりました。 提示頂いた内容は、実はもう実装していて、Excel VBAで参照設定した場合にちゃんと動いてます。 今回肝心なところは、HTMLに一切の変更を加えずにJavaScript内だけでイベントフックする方法でした。 ググると、HTMLでobjectタグ使って生成してscriptのfor、event属性でフックする方法が 大量に出てきてたのですが、new ActiveXObject('hoge.piyo')で生成したオブジェクトに対して C#で言う +=、VBでいうAddHandler的な何かがないかな、という点でした。 IE11のF12ツールでオブジェクトをウォッチすると、C#で追加した覚えの無い add_XXX、remove_XXXというメソッドがあったのでどう使うのかなーと試行錯誤していたのですが 結局別の方法で解決しました。 var _obj; function ApplicationStart() { _obj = new ActiveXObject('hoge.piyo'); eval('function _obj::Callback() { onCallback(); }'); //ここでイベントハンドラ紐付け _obj.Startup(); // C#側で用意したメソッド } function onCallback() { alert('いぇーい'); } ただ、add_XXX、remove_XXXの使い方を御存知の方は情報をお待ちしてます。 evalで文字列ベッタベターな書き方は個人的に好まないので…
■No33250に返信(ぺんたごんさんの記事) > scriptのfor、event属性でフックする方法が大量に出てきてたのですが VBA で参照設定 + WithEvents した場合に相当するのがその記述です。 実装する場合は ComSourceInterfacesAttribute ですね。 > C#で言う +=、VBでいうAddHandler的な何かがないかな、という点でした。 VBA や VBScript、JScript にはそのための仕組みがないので、 遅延バインディングにしたいなら、自前で用意しないと駄目でしょうね。 無いのなら 作ってしまえ ホトトギス ―――ということで、 _obj.AttachEvent( "callback", onCallback ) ; のように、関数オブジェクトを受け取るメンバーを作ってみました。 (簡略化のため、C# や VBA から使うための event 実装は省略しています) CD /D C:\SAMPLE\20160222\ csc Orator.Sample.cs /t:library /platform:AnyCPU /out:Orator.Sample.dll regasm /codebase Orator.Sample.dll /tlb:Orator.Sample.tlb 【Orator.Sample.cs】 using System.Runtime.InteropServices; using System.Collections.Generic; [assembly: ComVisible(true)] namespace Orator { // JScript の関数オブジェクト用 [ComVisible(false)] public delegate object Function(object sender, params object[] args); [ComVisible(true)] [ProgId("Orator.Sample")] [Guid("A17FC5F2-0EC7-4A8F-A648-9075E5976359")] [ClassInterface(ClassInterfaceType.None)] [ComDefaultInterface(typeof(Orator.ISample))] public sealed class Sample : ISample { // JScript はイベント呼び出しの構文を備えていないので、 // 関数オブジェクトを受け取ってコールバックさせる private Dictionary<string, Function> events; public Sample() { events = new Dictionary<string, Function>(); events.Add("NameChanging", null); events.Add("NameChanged", null); } #region AttachEvent メソッド void ISample.AttachEvent(string eventName, object functionObject) { // 関数オブジェクトをイベントハンドラとして割り当て events[eventName] += new Function((caller, args) => // 関数オブジェクトの実行 functionObject.GetType().InvokeMember( "[DispID=0]", System.Reflection.BindingFlags.InvokeMethod, System.Type.DefaultBinder, functionObject, args ) ); } #endregion #region Name プロパティ private string name = "Sample"; string ISample.Name { get { return name; } set { string oldName = name; string newName = value; if (oldName != newName) { if (events["NameChanging"] != null) { events["NameChanging"](this, oldName, newName); } name = value; if (events["NameChanged"] != null) { events["NameChanged"](this); } } } } #endregion } #region COMオブジェクトのメンバー定義 [Guid("02A9900A-EB12-4189-B8FF-396CF80EF78A")] public interface ISample { // JScript の関数オブジェクトや、VBScript の GetRef を // イベントに割り当てるためのヘルパー void AttachEvent(string eventName, object functionObject); // Name プロパティ string Name { get; set; } } #endregion } 【Orator.Sample.hta】 <html> <head> <meta http-equiv="X-UA-Compatible" content="IE=9" /> <title>Sample</title> <script language="JScript"> var _obj; window.onload = function() { window.resizeTo(400, 250); _obj = new ActiveXObject("Orator.Sample"); text1.value = _obj.Name; _obj.AttachEvent("NameChanging", fnNameChanging ) ; _obj.AttachEvent("NameChanged", fnNameChanged ) ; button1.onclick = function() { if(_obj.Name != text1.value) { _obj.Name = text1.value; } else { alert("元のNameと同じです"); } }; } function fnNameChanging(oldName, newName) { alert("NameChanging:\r\n" + oldName + " => " + newName); } function fnNameChanged() { alert("NameChanged"); } </script> <hta:application border="dialog" contextmenu="no" scroll="no" selection="no" singleInstance="yes"> </head> <body style="background-color:ButtonFace;color:ButtonText"> <p> <input type="text" id="text1" value="undefined"> <input type="button" id="button1" value="set"> </p> </body></html>