【问题标题】:ActiveX event handlers in an HTA using JScript使用 JScript 的 HTA 中的 ActiveX 事件处理程序
【发布时间】:2020-12-03 18:28:24
【问题描述】:

在 C# 中,我可以编写如下事件处理程序:

var wdApp = new Microsoft.Office.Interop.Word.Application();
wdApp.DocumentBeforeSave += (Document doc, ref bool saveAsUI, ref bool cancel) => {
   //do stuff here
};

在 VBA/VB6 中,我可以使用静态事件处理:

Dim WithEvents wdApp As Word.Application

Private Sub wdApp_DocumentBeforeSave(ByVal Doc As Document, SaveAsUI As Boolean, Cancel As Boolean)
    'do stuff here
End Sub

我更喜欢使用动态事件处理。然而,在 JScript 中,即使使用 here 描述的语法进行静态事件处理:

var wdApp = new ActiveXObject('Word.Application');
wdApp.Visible = true;

function wdApp::Quit() {
    window.alert('Quit');
};

失败了:

0x800a138f - JScript 运行时错误:预期对象

此外,静态事件处理是 VBA/VB6 中的一个选项,因为可以将声明标记为 Private。但是,在 JScript 中,变量和处理程序都必须在全局范围内声明。

两个问题:

  1. 如何在 HTA 环境中使用 JScript 处理自动化创建的对象的事件? (注意:我知道在 WSH 中可以使用传递给 CreateObject 的前缀和名为 wdApp_Quit 的函数,但我正在寻找 HTA 解决方案。)

  2. 如何在不污染全局范围的情况下做到这一点?


有一个老问题here

【问题讨论】:

  • 我看了看,但我什么也做不了。您提到的 WSH 解决方案使用“ConnectObject”,我尝试在带有 WScript.Shell 的 HTA 中使用它,但它不起作用。自 2009 年以来,在 MSDN 上有一个与您类似的未回答问题,所以看起来这可能是不可能的。
  • 应该是对象名::事件名。 msdn.microsoft.com/en-us/library/ms974564.aspx
  • 据我所见,事件连接取决于 WScript 对象,该对象在 mshta.exe 主机(运行 HTA)中不存在。但是,mshta 可以通过 wscript.exe 主机调用 javascript 对象,并获得对 WScript 对象的访问权限。这样的解决方案对您来说可以吗,还是看起来太丑陋了?
  • @EduardoPoço 据我所知,事件连接取决于 WScript 对象——正如我在(已编辑)问题中指出的,Microsoft 对 Javascript 的扩展允许定义varName::eventName() { } 形式的全局函数,它将在eventName 事件上为全局范围的varName 对象调用。这种机制在 WScript 和 HTA 中都可用。但我想要一些不需要全局函数声明和全局变量的东西;类似于 C# 机制——wdApp 不必是全局变量才能附加/删除处理程序。
  • @EduardoPoço Gordon 描述了在 this comment 中尝试类似的解决方案但没有成功,但只要我不必在全局范围内声明函数和变量,这将是值得的。

标签: events activex jscript hta activexobject


【解决方案1】:

错误似乎是因为

  1. 在 Javascript/JScript function declarations are evaluated first 中,在变量初始化之前。就好像代码是这样写的:

     var wdApp;
     function wdApp::Quit() { ... }
     wdApp = new ActiveXObject('Word.Application');
    
  2. Microsoft JScript 解析器将特别命名的声明(带有::)解释为将函数附加为事件处理程序的指令。

  3. 但是在声明的那一刻,处理程序不能附加到wdApp所引用的对象上,因为此时wdApp仍然是undefined。因此,错误。

解决方案是在 wdApp 初始化后强制执行函数声明。这可以通过以下三种方式之一完成1

  1. 由于函数声明仅被提升到函数范围内,因此将函数声明包装在 IIFE 中:

     var wdApp = new ActiveXObject('Word.Application');
     (function() {
         function wdApp::Quit() {
             //do stuff here
         }
     })();
    
  2. 使用某种字符串创建声明 -> 代码机制 -- evalsetTimeout 使用字符串、window.execScriptnew Function

     var wdApp = new ActiveXObject('Word.Application');
     eval('function wdApp::Quit() { ... }`);
    
  3. 在当前SCRIPT 块之前初始化变量。 Scripting Events 文章中的大多数示例都是通过在 SCRIPT 块之前的某个元素上设置 id 属性来实现的:

<object progid="ordersystem.clsorder" id="myorder" events="true"/>
<script language="jscript">
    function myorder::onNew() {
      WScript.Echo("new order received from myorder")
    }
//...

但这也可以使用多个 SCRIPT 块来完成:

    <SCRIPT>
        var wdApp = new ActiveXObject('Word.Application');
    </SCRIPT>
    <SCRIPT>
        function wdApp::Quit() {
            //do stuff here
        }
    </SCRIPT>

就污染全局命名空间而言,只有第三种变体要求wdApp在全局命名空间中;其他两个变体可以包装在 IIFE 中。


1.从技术上讲,IE和HTA下还有第四种方式,但它涉及非标准的HTML而不是非标准的Javascript;但它只能在使用 HTML OBJECT 标记而不是 new ActiveXObject( ... ) 声明对象时使用。

<script for="wdApp" event="Quit">
    //do stuff here
</script>

【讨论】:

  • 所以,:: 只适用于 eval 或 evalscript,是这样吗?
  • @EduardoPoço 或任何形式的字符串评估 -- setTimeoutnew Function
  • @EduardoPoço 似乎也可以包装在 IIFE 中。更新了我的答案。
猜你喜欢
  • 2012-05-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-30
  • 1970-01-01
  • 2011-04-04
  • 1970-01-01
  • 2011-10-11
相关资源
最近更新 更多