Memanfaatkan beberapa cuplikan kode, saya mencoba menghubungkan objek ActiveX dengan event handler Javascript. Saya tidak dapat mengidentifikasi mengapa event handler tidak dipanggil.

Repositori Github dengan proyek.

Perbarui

Dengan menempatkan panggilan javascript ke SayHello() dalam acara 'onLoad', saya dapat mengaktifkan acara ActiveX. Sekarang saya sedang mencari panggilan C#, dan bagaimana menghubungkannya ke objek ActiveX yang digunakan oleh Javascript.

(Ini mungkin juga mengandalkan mengaktifkan skrip lokal dari opsi Lanjutan IE).

Pesan Lanjutan

Penangan peristiwa dilakukan dalam formulir yang sama seperti yang dijelaskan untuk pertanyaan ini.

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

Memanfaatkan dokumentasi MSDN Saya membuat Aplikasi WinForms yang berisi kontrol WebBrowser yang bertindak sebagai ObjectForScripting (tidak terkait dengan masalah). Wadah ini membuat panggilan ke acara ActiveX, tetapi tidak ditangani oleh Javascript. Saya menyertakan kode Formulir C# untuk melengkapi interaksi ActiveX dan memungkinkan ini menjadi referensi bagi pengguna masa depan kontrol ActiveX dan/atau WebBrowser.

File ini dimaksudkan untuk digunakan dengan proyek Windows Form baru di mana kontrol WebBrowser ditambahkan ke jendela utama.

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>";
        }
    }
}

Menggabungkan dari bantuan dua lainnya cuplikan kode Saya membuat objek ActiveX. Yang, sebagaimana dicatat, perlu didaftarkan setelah dibangun.

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

Terakhir adalah halaman html yang akan dimuat oleh browser atau container. Ini memuat objek ActiveX dengan sukses dan berisi event handler untuk OnUpdateString. Ia memeriksa bahwa fungsi yang disediakan ActiveX, SayHello, dapat dipanggil dan melakukan panggilan.

Saya berharap Javascript dan panggilan C# ditulis ke dokumen, tetapi tidak ada entri seperti itu yang ditulis.

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>

Halaman yang berisi menunjukkan output ini

Keluaran

Objek ActiveX yang dimuat: benar

Objek Kontrol Dipanggil.

Bisa Menelepon menyapa

9
he_the_great 18 Mei 2015, 22:29
Saya tidak dapat menemukan hal lain yang mencurigakan di sini, kode serupa pasti berfungsi untuk saya sebelumnya. Mungkin, tambahkan type="text/javascript" ke tag <script> itu juga.
 – 
noseratio
20 Mei 2015, 01:47
Sayangnya, tidak satu pun dari saran ini yang membuahkan hasil.
 – 
he_the_great
20 Mei 2015, 03:02
Dalam hal ini saya sarankan Anda membuat proyek lengkap (dengan aplikasi dan DLL) tersedia untuk mengulangi ini dan memberikan hadiah untuk pertanyaan ini. Saya yakin seseorang akan mengambilnya dari sana.
 – 
noseratio
20 Mei 2015, 03:59
Saya suka ide repo, tapi saya terpaksa menunggu hadiahnya.
 – 
he_the_great
20 Mei 2015, 06:19

1 menjawab

Jawaban Terbaik

Diperbarui, selama Anda bisa mendapatkan <object> yang dipakai dari HTML (MyObject.object != null), masalah utama dengan event handler JavaScript Anda hanyalah Anda mematikan dokumen HTML asli dengan document.write sebelum Anda memanggil MyObject.SayHello("Javascript Load"), dan menggantinya dengan <p>Loaded ActiveX Object: ...</p>. Pada saat itu, semua event handler JavaScript asli hilang.

Dengan demikian, berikut ini berfungsi dengan baik, acara tersebut dipecat dan ditangani (dengan 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>

Untuk membuat logika asli Anda berfungsi, Anda dapat memanipulasi DOM secara langsung daripada menggunakan document.write. Atau, setidaknya panggil setelah OnUpdateString dipecat dan ditangani.


Sekarang setelah saya melihat sumber lengkapnya, saya dapat mengatakan beberapa hal yang salah di sini.
  • Anda dapat mencapai break point di dalam SayHello karena Anda membuat MyObject dari C# [MyObject myObject = new MyObject()] dan memanggilnya dari C# [myObject.SayHello("C# Launch")]. Hapus itu dan Anda akan melihatnya tidak pernah dipanggil saat Anda memanggilnya dari JavaScript [obj.SayHello("Javascript Load")].

  • Itu mengarah ke masalah lain: <object> tidak berhasil dibuat, dan terlebih lagi, tidak ada skrip JavaScript Anda yang berjalan, karena file HTML pengujian Anda disajikan dari sistem file lokal (melalui file:// protokol). Ini adalah batasan keamanan. Coba ubah skrip Anda seperti di bawah ini untuk melihat tidak ada peringatan yang benar-benar muncul:

    <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>
    
  • Ada beberapa cara untuk memperbaikinya. Yang paling mudah mungkin menggunakan "Tanda Web". Yang paling sulit adalah menyediakan implementasi kustom IInternetSecurityManager. Saya sendiri akan menggunakan metode lain - Kontrol Fitur Internet - dan nonaktifkan kunci FEATURE_LOCALMACHINE_LOCKDOWN, FEATURE_BLOCK_LMZ_SCRIPT, FEATURE_BLOCK_LMZ_OBJECT. Anda dapat menggunakan kode berikut yang saya adaptasi dari jawaban terkait saya yang lain:

    // 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;
    }
    
  • Sekarang, skrip Anda berjalan, tetapi <object> masih belum dibuat (alert(MyObject.object) menunjukkan null). Terakhir, Anda harus menerapkan IObjectSafety antarmuka pada objek ActiveX Anda dan kunci situs hanya ke halaman HTML Anda sendiri. Tanpa IObjectSafety yang tepat, objek tidak akan dibuat di bawah pengaturan keamanan IE default. Tanpa penguncian situs, ini bisa menjadi ancaman keamanan yang besar, karena skrip berbahaya apa pun dapat membuat dan menggunakan objek Anda di luar konteks aplikasi Anda.


Diperbarui Untuk mengatasi komentar:

Saya telah memperbarui proyek dengan contoh yang Anda berikan, perhatikan bahwa saya telah membuat perubahan sedemikian rupa sehingga ada tombol C # dan tombol Javascript untuk menjalankan acara. Tombol JS berfungsi, tetapi C# tidak menyala. Saya mencari peringatan "Halo dari: tombol C#".

Dalam kode Anda, instance myObject dibuat dan diakses secara eksklusif dari C#:

MyObject myObject = new MyObject();

// ...

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

Instance ini tidak ada hubungannya dengan instance <object id="MyObject" onerror="window.objectLoadFailure = true" classid="clsid:7A5D58C7-1C27-4DFF-8C8F-F5876FF94C64"></object> yang Anda buat dari HTML. Mereka adalah dua terpisah, objek yang tidak berhubungan. Penangan peristiwa Anda hanya berfungsi untuk instance <object> yang terakhir. Anda bahkan tidak berlangganan acara apa pun di instance new MyObject().

Jika saya memahami tujuan Anda dengan benar, Anda memerlukan ini:

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

    this.webBrowser1.Document.InvokeScript("eval",
        new[] { "MyObject.SayHello('C# Button')" });
}

Sekarang, event handler JavaScript akan dipanggil dan Anda akan melihat peringatan "C# Button".

3
Community 23 Mei 2017, 14:46
Terima kasih telah mencoba menjawab, tetapi premis Anda salah. SayHello dipecat untuk C# dan Javascript. Protokol file sudah digunakan, hanya saja tidak menyatakan protokolnya. MyObject.object bukan null, ini sebenarnya adalah 'ActiveXObjectSpace.MyObject'. Saya memang mengaktifkan menjalankan skrip lokal, tetapi tidak berhasil. Saya belum mencoba IObjectSafety karena saya tidak tertarik untuk membatasi situs apa yang memiliki akses untuk menjalankan objek ActiveX. Satu-satunya kode yang hilang dari pertanyaan awal adalah file Designer.cs yang dihasilkan VS.
 – 
he_the_great
21 Mei 2015, 19:44
@he_the_great, apa yang saya tulis di sini berlaku untuk proyek yang Anda sediakan, apa adanya. Saya baru saja mengunduh, mengkompilasi, dan menjalankannya. Satu-satunya koreksi yang saya buat adalah jalur ke file HTML uji dan melakukan RegAsm. Jika ada prasyarat/langkah lain untuk memastikan objek AX dibuat, Anda harus mencantumkannya.
 – 
noseratio
21 Mei 2015, 20:41
@Noserato, saya sukses dengan Javascript dengan memindahkan panggilan SayHello ke acara onLoad. Panggilan C# masih tidak masuk, saya sudah memindahkannya ke tombol sehingga dapat menyala sesuka hati setelah Javascript selesai memuat.
 – 
he_the_great
21 Mei 2015, 21:15
Aku sangat ingin itu menjadi jawabannya. Objek sedang dihancurkan oleh 'tulis' tetapi hanya oleh event handler. Panggilan C# masih tidak akan memecat acara untuk JS.
 – 
he_the_great
22 Mei 2015, 20:54
1
Anda telah berhasil melewati komunikasi C# dengan objek ActiveX yang digunakan oleh Javascript. Tetapi mungkin Anda benar karena beginilah caranya (saya tidak memiliki akses ke kode klien).
 – 
he_the_great
27 Mei 2015, 00:04