首页 文章

如何在Javascript中连接C#ActiveX事件处理程序

提问于
浏览
10

利用几个代码片段,我试图用Javascript事件处理程序挂钩ActiveX对象 . 我无法确定为什么没有调用事件处理程序 .

Github Repository with project.

Update

通过在'onLoad'事件中将javascript调用放到SayHello(),我能够触发ActiveX事件 . 现在我正在寻找C#调用,以及如何将其挂钩到Javascript使用的ActiveX对象 .

(这也可能依赖于从IE的高级选项中启用本地脚本) .

Message Continued

事件处理程序在same form as described for this question中完成 .

<script for="MyObject" event="OnUpdateString(stuff)">
        document.write("<p>" + stuff);
        document.writeln("</p>");
    </script>

利用MSDN documentation我创建了一个WinForms应用程序,其中包含一个WebBrowser控件,该控件充当ObjectForScripting(与问题无关) . 此容器调用ActiveX事件,但未被Javascript处理 . 我要在ActiveX交互中包含C#Form代码,并允许它作为ActiveX和/或WebBrowser控件的未来用户的参考 .

此文件旨在与新的Windows窗体项目一起使用,其中WebBrowser控件已添加到主窗口 .

C# Form1.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Security.Permissions;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using ActiveXObjectSpace;

namespace TestActiveX
{
    [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
    [System.Runtime.InteropServices.ComVisibleAttribute(true)]
    public partial class Form1 : Form
    {
        MyObject myObject = new MyObject();
        public Form1()
        {
            InitializeComponent();
            Text = "ActiveX Test";

            Load += new EventHandler(Form1_Load);
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            webBrowser1.AllowWebBrowserDrop = false;
            webBrowser1.ObjectForScripting = this;
            webBrowser1.Url = new Uri(@"C:\path\to\TestPage.html");

            // Call ActiveX
            myObject.SayHello("C# Launch");
        }

        public string ControlObject()
        {
            return "<p>Control Object Called.</p>";
        }
    }
}

two other code snippets的帮助下,我创建了一个ActiveX对象 . 如上所述,需要在构建之后注册 .

C# ObjectX.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;


/// http://blogs.msdn.com/b/asiatech/archive/2011/12/05/how-to-develop-and-deploy-activex-control-in-c.aspx
/// https://stackoverflow.com/questions/11175145/create-com-activexobject-in-c-use-from-jscript-with-simple-event
///
/// Register with %NET64%\regasm /codebase <full path of dll file>
/// Unregister with %NET64%\regasm /u <full path of dll file>
namespace ActiveXObjectSpace
{

    /// <summary>
    /// Provides the ActiveX event listeners for Javascript.
    /// </summary>
    [Guid("4E250775-61A1-40B1-A57B-C7BBAA25F194"), InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    public interface IActiveXEvents
    {
        [DispId(1)]
        void OnUpdateString(string data);
    }

    /// <summary>
    /// Provides properties accessible from Javascript.
    /// </summary>
    [Guid("AAD0731A-E84A-48D7-B5F8-56FF1B7A61D3"), InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    public interface IActiveX
    {
        [DispId(10)]
        string CustomProperty { get; set; }
    }

    [ProgId("MyObject")]
    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.AutoDual)]
    [Guid("7A5D58C7-1C27-4DFF-8C8F-F5876FF94C64")]
    [ComSourceInterfaces(typeof(IActiveXEvents))]
    public class MyObject : IActiveX
    {

        public delegate void OnContextChangeHandler(string data);
        new public event OnContextChangeHandler OnUpdateString;

        // Dummy Method to use when firing the event
        private void MyActiveX_nMouseClick(string index)
        {

        }

        public MyObject()
        {
            // Bind event
            this.OnUpdateString = new OnContextChangeHandler(this.MyActiveX_nMouseClick);
        }

        [ComVisible(true)]
        public string CustomProperty { get; set; }


        [ComVisible(true)]
        public void SayHello(string who)
        {
            OnUpdateString("Calling Callback: " + who);
        }
    }
}

最后是由浏览器或容器加载的html页面 . 它成功加载ActiveX对象并包含OnUpdateString的事件处理程序 . 它检查可以调用ActiveX提供的函数SayHello并进行调用 .

我希望将Javascript和C#调用写入文档,但不会写入此类条目 .

TestPage.html

<!DOCTYPE html>

<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>DemoCSharpActiveX webpage</title>
</head>
<body>
        <script type="text/javascript">
        window.objectLoadFailure = false;
        </script>

        <object id="MyObject" onerror="window.objectLoadFailure = true" classid="clsid:7A5D58C7-1C27-4DFF-8C8F-F5876FF94C64"></object>

        <script for="MyObject" event="OnUpdateString(stuff)">
            document.write("<p>" + stuff);
            document.writeln("</p>");
        </script>


        <script type="text/javascript">
            document.write("<p>Loaded ActiveX Object: " + !window.objectLoadFailure);
            document.writeln("</p>");
            if (typeof window.external.ControlObject !== "undefined") {
                document.write(window.external.ControlObject());
            }


            var obj = document.MyObject;
            if (typeof obj.SayHello !== "undefined") {
                document.writeln("<p>Can Call say hello</p>")
            }
            obj.SayHello("Javascript Load");

        </script>
</body>
</html>

包含页面显示此输出

Output

加载的ActiveX对象:调用的真实控制对象 . 可以打电话打招呼

1 回答

  • 3

    Updated ,只要您可以从HTML( MyObject.object != null )实例化 <object> ,JavaScript事件处理程序的最终问题就是您在调用 MyObject.SayHello("Javascript Load") 之前使用 document.write 终止原始HTML文档,并将其替换为 <p>Loaded ActiveX Object: ...</p> . 到那时,所有原始的JavaScript事件处理程序都消失了 .

    因此,以下工作正常,事件被触发和处理(使用 alert ):

    <!DOCTYPE html>
    <html>
    <head>
        <title>DemoCSharpActiveX webpage</title>
    </head>
    <body>
        <script type="text/javascript">
            window.objectLoadFailure = false;
        </script>
    
        <object id="MyObject" onerror="window.objectLoadFailure = true" classid="clsid:7A5D58C7-1C27-4DFF-8C8F-F5876FF94C64"></object>
    
        <script type="text/javascript" for="MyObject" event="OnUpdateString">
            alert("Hello from event handler");
        </script>
    
        <script type="text/javascript" for="window" event="onload">
            alert("Hello from window.onload!");
            alert(MyObject.object);
            MyObject.SayHello("Javascript Load");
        </script>
    </body>
    </html>
    

    要使原始逻辑工作,您可以直接操作DOM而不是使用 document.write . 或者,至少在 OnUpdateString 被触发并处理后调用它 .


    既然我已经看到了完整的消息来源,我可以在这里说出一些错误 .

    • 您可以在 SayHello 内点击一个断点,因为您从C#[ MyObject myObject = new MyObject() ]创建 MyObject 并从C#[ myObject.SayHello("C# Launch") ]调用它 . 删除它,你会发现当你从JavaScript [ obj.SayHello("Javascript Load") ]调用它时,它永远不会被调用 .

    • 这会导致另一个问题: <object> 无法成功创建,甚至更多,因此您的JavaScript脚本都不会运行,因为您的测试HTML文件是从本地文件系统提供的(通过 file:// 协议) . 这是一项安全限制 . 尝试更改您的脚本,如下所示,看不到任何警报实际显示:

    <script type="text/javascript" for="window" event="onload">
        alert("Hello from window.onload!");
        alert(MyObject.object) // null! object wasn't created...
        document.write("<p>Loaded ActiveX Object: " + !window.objectLoadFailure);
        document.writeln("</p>");
        if (typeof window.external.ControlObject !== "undefined") {
            document.write(window.external.ControlObject());
        }
    
    
        var obj = document.MyObject;
        if (typeof obj.SayHello !== "undefined") {
            document.writeln("<p>Can Call say hello</p>")
        }
        obj.SayHello("Javascript Load");
    </script>
    
    // static constructor, runs first
    static Form1()
    {
        SetWebBrowserFeatures();
    }
    
    static void SetWebBrowserFeatures()
    {
        // don't change the registry if running in-proc inside Visual Studio
        if (LicenseManager.UsageMode != LicenseUsageMode.Runtime)
            return;
    
        var appName = System.IO.Path.GetFileName(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName);
    
        var featureControlRegKey = @"HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Main\FeatureControl\";
    
        Registry.SetValue(featureControlRegKey + "FEATURE_BROWSER_EMULATION",
            appName, GetBrowserEmulationMode(), RegistryValueKind.DWord);
    
        // enable the features which are "On" for the full Internet Explorer browser
    
        Registry.SetValue(featureControlRegKey + "FEATURE_ENABLE_CLIPCHILDREN_OPTIMIZATION",
            appName, 1, RegistryValueKind.DWord);
    
        Registry.SetValue(featureControlRegKey + "FEATURE_AJAX_CONNECTIONEVENTS",
            appName, 1, RegistryValueKind.DWord);
    
        Registry.SetValue(featureControlRegKey + "FEATURE_GPU_RENDERING",
            appName, 1, RegistryValueKind.DWord);
    
        Registry.SetValue(featureControlRegKey + "FEATURE_WEBOC_DOCUMENT_ZOOM",
            appName, 1, RegistryValueKind.DWord);
    
        Registry.SetValue(featureControlRegKey + "FEATURE_NINPUT_LEGACYMODE",
            appName, 0, RegistryValueKind.DWord);
    
        Registry.SetValue(featureControlRegKey + "FEATURE_LOCALMACHINE_LOCKDOWN",
            appName, 0, RegistryValueKind.DWord);
    
        Registry.SetValue(featureControlRegKey + "FEATURE_BLOCK_LMZ_SCRIPT",
            appName, 0, RegistryValueKind.DWord);
    
        Registry.SetValue(featureControlRegKey + "FEATURE_BLOCK_LMZ_OBJECT",
            appName, 0, RegistryValueKind.DWord);
    }
    
    static UInt32 GetBrowserEmulationMode()
    {
        int browserVersion = 0;
        using (var ieKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Internet Explorer",
            RegistryKeyPermissionCheck.ReadSubTree,
            System.Security.AccessControl.RegistryRights.QueryValues))
        {
            var version = ieKey.GetValue("svcVersion");
            if (null == version)
            {
                version = ieKey.GetValue("Version");
                if (null == version)
                    throw new ApplicationException("Microsoft Internet Explorer is required!");
            }
            int.TryParse(version.ToString().Split('.')[0], out browserVersion);
        }
    
        if (browserVersion < 7)
        {
            throw new ApplicationException("Unsupported version of Microsoft Internet Explorer!");
        }
    
        UInt32 mode = 11000; // Internet Explorer 11. Webpages containing standards-based !DOCTYPE directives are displayed in IE11 Standards mode. 
    
        switch (browserVersion)
        {
            case 7:
                mode = 7000; // Webpages containing standards-based !DOCTYPE directives are displayed in IE7 Standards mode. 
                break;
            case 8:
                mode = 8000; // Webpages containing standards-based !DOCTYPE directives are displayed in IE8 mode. 
                break;
            case 9:
                mode = 9000; // Internet Explorer 9. Webpages containing standards-based !DOCTYPE directives are displayed in IE9 mode.                    
                break;
            case 10:
                mode = 10000; // Internet Explorer 10.
                break;
        }
    
        return mode;
    }
    
    • 现在,您的脚本确实运行了,但仍然没有创建 <object>alert(MyObject.object) 显示 null ) . 最后,您需要在ActiveX对象上实现IObjectSafety接口,并且只需要在您自己的HTML页面上实现site-lock . 如果没有正确的 IObjectSafety ,将无法在默认的IE安全设置下创建对象 . 如果没有站点锁定,它可能会成为一个巨大的安全威胁,因为任何恶意脚本都可能在您的应用程序上下文之外创建和使用您的对象 .

    Updated 发表评论:

    我已经使用您提供的示例更新了项目,请注意我已经进行了更改,以便有一个C#按钮和一个Javascript按钮来触发事件 . JS按钮有效,但C#不会触发 . 我正在寻找一个“Hello from:C#button”警报 .

    在您的代码中, myObject 实例仅由C#创建和访问:

    MyObject myObject = new MyObject();
    
    // ...
    
    private void button1_Click(object sender, EventArgs e)
    {
       // Call ActiveX
       myObject.SayHello("C# Button");
    }
    

    此实例与您从HTML创建的 <object id="MyObject" onerror="window.objectLoadFailure = true" classid="clsid:7A5D58C7-1C27-4DFF-8C8F-F5876FF94C64"></object> 实例无关 . 它们是两个独立的,无关的对象 . 您的事件处理程序仅适用于后者 <object> 实例 . 您甚至不订阅 new MyObject() 实例上的任何事件 .

    如果我理解你的目标,你需要这个:

    private void button1_Click(object sender, EventArgs e)
    {
        // Call ActiveX
        //myObject.SayHello("C# Button");
    
        this.webBrowser1.Document.InvokeScript("eval",
            new[] { "MyObject.SayHello('C# Button')" });
    }
    

    现在,将调用JavaScript事件处理程序,您将看到 "C# Button" 警报 .

相关问题