如何从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}); }