利用多个代码片段我试图用Javascript事件处理程序挂钩ActiveX对象。我无法确定为什么没有调用事件处理程序。
Github Repository with project.
更新
将javascript调用放入' onLoad'中的SayHello()。事件,我能够触发ActiveX事件。现在,我正在寻找C#调用,以及如何将其挂钩到Javascript使用的ActiveX对象。
(这也可能依赖于从IE的高级选项中启用本地脚本)。
消息续
事件处理程序在same form as described for this question。
中完成 <script for="MyObject" event="OnUpdateString(stuff)">
document.write("<p>" + stuff);
document.writeln("</p>");
</script>
利用MSDN documentation我创建了一个WinForms应用程序,其中包含一个充当ObjectForScripting的WebBrowser控件(与问题无关)。此容器调用ActiveX事件,但未被Javascript处理。我要在ActiveX交互中包含要完成的C#表单代码,并允许它作为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其他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>
包含页面显示此输出
输出
加载的ActiveX对象:true
调用控制对象。
可以打电话打招呼
答案 0 :(得分:3)
已更新,只要您可以从HTML(<object>
)获取MyObject.object != null
实例化,这是JavaScript事件处理程序的最终问题只是您在调用document.write
之前使用MyObject.SayHello("Javascript Load")
终止原始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 myObject = new 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>
有几种方法可以修复它。最简单的可能是使用"Mark of Web"。最难的是提供IInternetSecurityManager
的自定义实现。我自己会使用另一种方法 - Internet Feature Control - 并停用FEATURE_LOCALMACHINE_LOCKDOWN
,FEATURE_BLOCK_LMZ_SCRIPT
,FEATURE_BLOCK_LMZ_OBJECT
键。您可以使用我从其他related answer改编的以下代码:
// 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
接口,并将site-lock仅用于您自己的HTML页面。如果没有正确的IObjectSafety
,将无法在默认的IE安全设置下创建对象。如果没有站点锁定,它可能会成为一个巨大的安全威胁,因为任何恶意脚本都可能在您的应用程序上下文之外创建和使用您的对象。
<小时/> 更新了以解决评论:
我用你提供的例子更新了项目,注意我有 进行了更改,以便有一个C#按钮和一个Javascript按钮 解雇事件。 JS按钮有效,但C#不会触发。我 寻找“你好来自:C#按钮”警报。
在您的代码中,{C}创建了myObject
实例并以 方式访问
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"
警报。