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 sebuah>.
<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
1 menjawab
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 membuatMyObject
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 (melaluifile://
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 kunciFEATURE_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)
menunjukkannull
). Terakhir, Anda harus menerapkanIObjectSafety
antarmuka pada objek ActiveX Anda dan kunci situs hanya ke halaman HTML Anda sendiri. TanpaIObjectSafety
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"
.
type="text/javascript"
ke tag<script>
itu juga.