如何从 C# 主机应用程序执行 Javascript 回调函数 [英] How can I execute Javascript callback function from C# host application

查看:54
本文介绍了如何从 C# 主机应用程序执行 Javascript 回调函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在用 C# 创建一个应用程序,它为大多数 GUI 托管自定义网页.作为宿主,我想提供一个 javascript API,以便嵌入的网页可以访问宿主应用程序提供的一些服务.

I'm creating an application in C# that hosts custom web pages for most of the GUI. As the host, I'd like to provide a javascript API so that the embedded web pages can access some of the services provided by the host application.

我已经能够使用 WebBrowser.ObjectForScripting 属性并实现脚本类来获得此工作的简单案例.这对于同步 javascript 调用非常有用.但是,主机提供的一些操作是长时间运行的,我想提供在操作完成时回调 javascript 的能力.这就是我遇到麻烦的地方.

I've been able to get the simple case for this working using the WebBrowser.ObjectForScripting property and implementing a scripting class. This works great for synchronous javascript calls. However, some of the operations that the host provides are long running and I'd like to provide the ability for the javascript to be called back when the operation completes. And this is where I'm running into trouble.

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# 调用它?

The 'start' function in javascript kicks this whole process off. The problem I'm having is, What is the type for the callback? And how should I call it from C#?

如果我使用字符串类型进行回调,它会编译并运行,但在 LongRunningProcess 方法中,回调实际上包含 onComplete 函数的全部内容(即 'function onComplete( result ) { alert( result ) }' )

If I use the string type for callback, it compiles and runs, but from within the LongRunningProcess method callback actually contains the full contents of the onComplete function ( i.e. 'function onComplete( result ) { alert( result ) }' )

如果我使用对象类型,它会作为 COM 对象返回.使用 Microsoft.VisualBasic.Information.TypeName 方法,它返回JScriptTypeInfo".但据我所知,这不是真正的类型,整个 MSDN 中也没有真正提及它.

If I use the object type, it comes back as a COM object. Using the Microsoft.VisualBasic.Information.TypeName method, it returns 'JScriptTypeInfo'. But as far as I can tell, that's not a real type, nor is there any real mention of it through all of MSDN.

如果我使用 IReflect 接口,它运行时不会出错,但是我找不到对象的成员、字段或属性.

If I use the IReflect interface, it runs without error, but there are no members, fields, or properties on the object that I can find.

解决方法是传递回调函数的字符串名称而不是函数本身(即 window.external.LongRunningProcess( 'data', 'onComplete' ); ).我确实知道如何按名称执行 javascript 函数,但我不希望网页中需要该语法,它也不适用于 javascript 中的内联回调定义.

A work around would be to pass the string name of the callback function instead of the function itself ( i.e. window.external.LongRunningProcess( 'data', 'onComplete' ); ). I do know how to execute the javascript function by name, but I'd rather not have that syntax be required in the web pages, it also would not work with inline callback definitions in the javascript.

有什么想法吗?

就其价值而言,我已经让这个系统与 Chromium Embedded 框架一起工作,但我正在努力将代码移植到 WebBrowser 控件,以避免重新分配 Chromium 的庞大规模.但是,正在开发的 HTML 页面最终将在 Linux/Mac OSX 上运行,其中 Chromium 可能仍会使用.

For what it's worth, I've already got this system working with the Chromium Embedded framework, but I'm working to port the code over to the WebBrowser control to avoid the hefty size of redistributing Chromium. However, the HTML pages being developed will eventually be run on Linux/Mac OSX where Chromium will probably still be used.

推荐答案

你可以使用反射:

[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 方法.如果能用就更优雅了,不过我还没有验证过:

You could also try dynamic approach. It'd be more elegant if it works, but I haven't verified it:

[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] dynamic 方法确实很好用,并且可能是当您拥有 JavaScript 函数对象时从 C# 回调 JavaScript 的最简单方法.反射和 dynamic 也允许调用匿名 JavaScript 函数.示例:

[UPDATE] The dynamic method indeed works great, and probably is the easiest way of calling back JavaScript from C#, when you have a JavaScript function object. Both Reflection and dynamic allow to call an anonymous JavaScript function, as well. Example:

C#:

public void CallbackTest(object callback)
{
    dynamic callbackFunc = callback;
    callbackFunc("Hello!");
}

JavaScript:

window.external.CallbackTest(function(msg) { alert(msg) })

这篇关于如何从 C# 主机应用程序执行 Javascript 回调函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆