如何从C#主机应用程序执行Javascript回调函数
我正在用C#创建一个应用程序,为大多数GUI托管自定义网页。作为主机,我希望提供一个javascript API,以便嵌入式web页面能够访问主机应用程序提供的一些服务 我已经能够通过使用WebBrowser.ObjectForScript属性和实现脚本类获得这项工作的简单案例。这对于同步javascript调用非常有效。但是,主机提供的一些操作是长时间运行的,我希望提供在操作完成时回调javascript的功能。这就是我遇到麻烦的地方 Javascript:如何从C#主机应用程序执行Javascript回调函数,c#,javascript,callback,webbrowser-control,C#,Javascript,Callback,Webbrowser Control,我正在用C#创建一个应用程序,为大多数GUI托管自定义网页。作为主机,我希望提供一个javascript API,以便嵌入式web页面能够访问主机应用程序提供的一些服务 我已经能够通过使用WebBrowser.ObjectForScript属性和实现脚本类获得这项工作的简单案例。这对于同步javascript调用非常有效。但是,主机提供的一些操作是长时间运行的,我希望提供在操作完成时回调javascript的功能。这就是我遇到麻烦的地方 Javascript: function onComple
function onComplete( result )
{
alert( result );
}
function start()
{
window.external.LongRunningProcess( 'data', onComplete );
}
C#:
[ComVisible(true)]
公共类脚本对象
{
公共void LongRunningProcess(字符串数据,回调)
{
//做工作,打电话回叫
}
}
javascript中的“start”函数启动了整个过程。我遇到的问题是,回调的类型是什么?从C#我该怎么称呼它
如果我使用字符串类型进行回调,它将编译并运行,但从LongRunningProcess方法中回调实际上包含onComplete函数的全部内容(即“function onComplete(result){alert(result)}”)
如果我使用对象类型,它将作为COM对象返回。使用Microsoft.VisualBasic.Information.TypeName方法,它返回“JScript类型信息”。但据我所知,这不是一个真正的类型,也没有任何真正提到它通过所有的MSDN
如果我使用DirectReflect接口,它运行时不会出错,但在对象上找不到任何成员、字段或属性
解决方法是传递回调函数的字符串名,而不是函数本身(即window.external.LongRunningProcess('data','onComplete');)。我确实知道如何按名称执行javascript函数,但我不希望网页中需要这种语法,它也不适用于javascript中的内联回调定义
有什么想法吗
值得一提的是,我已经让这个系统与Chromium嵌入式框架一起工作,但我正在将代码移植到WebBrowser控件,以避免Chromium重新分发的巨大规模。但是,正在开发的HTML页面最终将在Linux/Mac OSX上运行,在Linux/Mac OSX上可能仍将使用Chromium。您可以使用反射:
[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
方法。如果能用的话会更优雅,但我还没有证实:
[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);
}
}
[UPDATE]当您有一个JavaScript函数对象时,动态
方法确实非常有效,并且可能是从C#调用JavaScript的最简单方法。反射和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});
}
我不是C语言的程序员。。。也许这并不能解决您的问题,但我的同事使用一个库来解释C#中的JS(我不记得是C#还是.net)可能对您没有用处,但您尝试查看:。或者您可以像字符串
window.external.LongRunningProcess('data','onComplete')一样传递callback
在c#中,您可以创建一个具有此名称的函数,并像回调一样执行它,而不使用c#中的JS函数(但使用本机c#函数)。。。如果评论对您没有帮助,我很抱歉,时间eval
在这里显得多余。如果回调函数是通过名称传递的(而不是作为对象,如OP的问题中所述),那么下面的操作将不需要eval
:webBrowser1.Document.InvokeScript(callbackFunctionName,new object[]{result})
。虽然,eval
在需要向页面()中注入新的JavaScript代码时可能很有用。因为我的目标是.NET2.0,所以我不能使用dynamic,但很高兴知道它是有效的。您是如何知道神奇的“[DispID=0]”字符串的?有关于这个的文档吗?我从COM荣耀时代就知道了,但我只是搜索了一下,找不到任何官方来源。但也有很多非官方的。记录了Type.InvokeMember
的DispId[…]
语法。通过使用类型为“dynamic”的参数等“dynamic callback”,可以更好地避免“dynamic callbackbunc=callback;”行。我发现了一个奇怪的问题,即如果回调没有任何参数,它就不起作用,因此在您的示例中,“callbackFunc()”就不起作用。知道为什么吗?@Martynas确实callbackFunc()
不起作用。我目前对此没有任何解释,但有一个非常简单的解决方法,可以这样称呼它:callbackFunc(Type.Missing)
。
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});
}