【问题标题】:How can I execute Javascript callback function from C# host application如何从 C# 主机应用程序执行 Javascript 回调函数
【发布时间】:2014-02-03 23:59:42
【问题描述】:

我正在用 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 中的“开始”功能启动了整个过程。我遇到的问题是,回调的类型是什么?我应该如何从 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 页面最终将在 Linux/Mac OSX 上运行,而 Chromium 可能仍会被使用。

【问题讨论】:

  • 我不是 C# 中的程序员...也许这不是您问题的解决方案,但我的同事使用库来执行 C# 中的 JS(我不记得是否 C#或 .net)可能对您没用,但您尝试查看:javascriptdotnet.codeplex.com。或者你可以传递callback 像字符串window.external.LongRunningProcess( 'data', 'onComplete'); 并且在c# 中你可以使用这个名称创建一个函数并像回调一样执行它而不使用C# 中的JS 函数(但使用本机C# 函数)...如果注释不是'无法帮助您,很抱歉耽误您的时间

标签: c# javascript callback webbrowser-control


【解决方案1】:

您可以为此使用反射:

[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);
    }
}

[更新]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) })

【讨论】:

  • 反射方法奏效了。由于我的目标是 .NET2.0,所以我不能使用动态,但很高兴知道它有效。你是怎么知道神奇的“[DispID=0]”字符串的?在某处是否有相关文档?
  • 我知道是从COM荣耀的日子回来的,但我只是搜索了一下,找不到任何官方来源。不过有很多非官方的,for exampleType.InvokeMemberDispId[...] 语法记录在 here
  • 更妙的是,您可以避免使用“dynamic callbackFunc = callback;”这一行通过具有“动态”类型的参数等“动态回调”。我发现了一些奇怪的问题,如果回调没有任何参数,它就不起作用,所以在你的例子中,“callbackFunc()”不起作用。知道为什么吗?
  • @Martynas,确实callbackFunc() 不起作用。我目前对此没有任何解释,但是有一个非常简单的解决方法,这样称呼它:callbackFunc(Type.Missing)
【解决方案2】:

正如@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});  
    }

【讨论】:

  • eval 在这里似乎是多余的。如果回调函数是按名称传递的(而不是像 OP 的问题中那样作为对象传递),则以下将在没有 eval 的情况下进行:webBrowser1.Document.InvokeScript(callbackFunctionName, new Object [] { result })。虽然,eval 在需要将新的 JavaScript 代码注入页面 (example) 时可能很有用。
猜你喜欢
  • 2019-06-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-06
  • 1970-01-01
  • 2010-09-21
  • 2018-01-02
  • 1970-01-01
相关资源
最近更新 更多