简体   繁体   English

如何从C#主机应用程序执行Javascript回调函数

[英]How can I execute Javascript callback function from C# host application

I'm creating an application in C# that hosts custom web pages for most of the GUI. 我正在C#中创建一个应用程序,它为大多数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. 作为主持人,我想提供一个javascript API,以便嵌入式网页可以访问主机应用程序提供的一些服务。

I've been able to get the simple case for this working using the WebBrowser.ObjectForScripting property and implementing a scripting class. 我已经能够使用WebBrowser.ObjectForScripting属性和实现脚本类来获得这个工作的简单案例。 This works great for synchronous javascript calls. 这适用于同步javascript调用。 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. 但是,主机提供的一些操作是长时间运行的,我想提供在操作完成时回调javascript的能力。 And this is where I'm running into trouble. 这就是我遇到麻烦的地方。

Javascript: 使用Javascript:

function onComplete( result )
{
    alert( result );
}

function start()
{
    window.external.LongRunningProcess( 'data', onComplete );
}

C#: C#:

[ComVisible(true)]
public class ScriptObject
{
    public void LongRunningProcess( string data, <???> callback )
    {
        // do work, call the callback
    }
}

The 'start' function in javascript kicks this whole process off. javascript中的'start'功能将整个过程踢掉。 The problem I'm having is, What is the type for the callback? 我遇到的问题是,回调的类型是什么? And how should I call it from C#? 我该如何从C#中调用它?

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

If I use the object type, it comes back as a COM object. 如果我使用对象类型,它将作为COM对象返回。 Using the Microsoft.VisualBasic.Information.TypeName method, it returns 'JScriptTypeInfo'. 使用Microsoft.VisualBasic.Information.TypeName方法,它返回'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. 但据我所知,这不是一个真正的类型,也没有通过所有的MSDN真正提到它。

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. 如果我使用IReflect接口,它运行时没有错误,但是我找不到对象上没有成员,字段或属性。

A work around would be to pass the string name of the callback function instead of the function itself ( ie window.external.LongRunningProcess( 'data', 'onComplete' ); ). 解决方法是传递回调函数的字符串名称而不是函数本身(即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. 我知道如何按名称执行javascript函数,但我不想在网页中使用该语法,它也不适用于javascript中的内联回调定义。

Any Ideas? 有任何想法吗?

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. 为了它的价值,我已经让这个系统使用了Chromium Embedded框架,但是我正在努力将代码移植到WebBrowser控件上,以避免重新分配Chromium的大小。 However, the HTML pages being developed will eventually be run on Linux/Mac OSX where Chromium will probably still be used. 但是,正在开发的HTML页面最终将在Linux / Mac OSX上运行,其中可能仍会使用Chromium。

You can use Reflection for that: 你可以使用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 });
    }
}

You could also try dynamic approach. 您也可以尝试dynamic方法。 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] 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. [更新] dynamic方法确实很好用,当你有一个JavaScript函数对象时,它可能是从C#调用JavaScript的最简单方法。 Both Reflection and dynamic allow to call an anonymous JavaScript function, as well. Reflection和dynamic允许调用匿名JavaScript函数。 Example: 例:

C#: C#:

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

JavaScript : JavaScript

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

As @Frogmouth noted here already you can pass callback function name to the LongRunningProcedure : 正如@Frogmouth在这里指出的那样,你可以将回调函数名称传递给LongRunningProcedure

function onComplete( result )
{
    alert( result );
}

function start()
{
    window.external.LongRunningProcess( 'data', 'onComplete' );
}

and when LongRunningProcedure completes use .InvokeScript as the following: 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});  
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM