如何从C#主机应用程序执行Javascript回调函数
我正在C#中创建一个应用程序,它为大多数GUI提供自定义网页。 作为主持人,我想提供一个javascript API,以便嵌入式网页可以访问主机应用程序提供的一些服务。
我已经能够使用WebBrowser.ObjectForScripting属性和实现脚本类来获得这个工作的简单案例。 这适用于同步javascript调用。 但是,主机提供的一些操作是长时间运行的,并且我希望能够在操作完成时回调javascript。 这就是我遇到麻烦的地方。
使用Javascript:
function onComplete( result ) { alert( result ); } function start() { window.external.LongRunningProcess( 'data', onComplete ); }
C#:
[ComVisible(true)] public class ScriptObject { public void LongRunningProcess( string data, callback ) { // do work, call the callback } }
javascript中的’start’函数将整个过程踢掉。 我遇到的问题是,回调的类型是什么? 我该怎么称呼它来自C#?
如果我使用字符串类型进行回调,它会编译并运行,但是在LongRunningProcess方法中,回调实际上包含onComplete函数的全部内容(即’function onComplete(result){alert(result)}’)
如果我使用对象类型,它将作为COM对象返回。 使用Microsoft.VisualBasic.Information.TypeName方法,它返回’JScriptTypeInfo’。 但据我所知,这不是一个真正的类型,也没有通过所有MSDN真正提到它。
如果我使用IReflect接口,它运行时没有错误,但是我找不到对象上没有成员,字段或属性。
解决方法是传递回调函数的字符串名称而不是函数本身(即window.external.LongRunningProcess(’data’,’onComplete’);)。 我知道如何按名称执行javascript函数,但我不想在网页中使用该语法,它也不适用于javascript中的内联回调定义。
有任何想法吗?
为了它的价值,我已经让这个系统与Chromium Embedded框架一起工作,但我正在努力将代码移植到WebBrowser控件上,以避免重新分配Chromium的大小。 但是,正在开发的HTML页面最终将在可能仍在使用Chromium的Linux / Mac OSX上运行。
你可以使用Reflection:
[ComVisible(true)] public class ScriptObject { public void LongRunningProcess(string data, object callback) { string result = String.Empty; // do work, call the callback callback.GetType().InvokeMember( name: "[DispID=0]", invokeAttr: BindingFlags.Instance | BindingFlags.InvokeMethod, binder: null, target: callback, args: new Object[] { result }); } }
您也可以尝试dynamic
方法。 如果它有效会更优雅,但我还没有validation它:
[ComVisible(true)] public class ScriptObject { public void LongRunningProcess(string data, object callback) { string result = String.Empty; // do work, call the callback dynamic callbackFunc = callback; callbackFunc(result); } }
[更新] dynamic
方法确实很有效,当你有一个JavaScript函数对象时,它可能是从C#调用JavaScript的最简单方法。 Reflection和dynamic
允许调用匿名JavaScript函数。 例:
C#:
public void CallbackTest(object callback) { dynamic callbackFunc = callback; callbackFunc("Hello!"); }
JavaScript :
window.external.CallbackTest(function(msg) { alert(msg) })
正如@Frogmouth
在这里指出的那样,您可以将回调函数名称传递给LongRunningProcedure
:
function onComplete( result ) { alert( result ); } function start() { window.external.LongRunningProcess( 'data', 'onComplete' ); }
当LongRunningProcedure
完成后,使用.InvokeScript
如下:
public void LongRunningProcess(string data, string callbackFunctionName) { // do work, call the callback string codeStrig = string.Format("{0}('{1}')", callbackFunctionName, "{{ Your result value here}}"); webBrowser1.Document.InvokeScript("eval", new [] { codeStrig}); }