我的代码如何知道它是以VBScript,.HTA还是VBA运行?

Cyb*_*aco 7 vbscript excel vba hta identification

编辑:到目前为止,大多数响应都涉及检查可能不存在的对象,这些对象在使用Option Explicit on的VBA中完全不起作用(它会抛出编译时错误,因此On Error不起作用,并且关闭Option Explicit不是一个选项).还有其他一些环形/开箱即用的方法来找出这里需要什么吗?

我正在尝试编写通用的复制粘贴代码,这些代码可以在独立的VBScript(.vbs文件),.hta文件和VBA(例如,在Excel文件中)中使用.要做到这一点,我需要一些方法让代码本身告诉它运行的引擎.

到目前为止我听到的最好的想法是测试某些对象是否存在,但在VBA中,在编译时失败(因此我无法绕过On Error),因此无法解决问题.试图找出它运行的文件的名称并不是最终可行的; 根据代码运行的三个脚本引擎中的哪一个,这是不同的事情之一.我希望有这样简单的东西,但我不确定要填写什么:

Option Explicit

'--- Returns a string containing which script engine this is running in,
'--- either "VBScript", "VBA", or "HTA".

Function ScriptEngine()

    If {what goes here?} Then ScriptEngine="VBS"
    If {what goes here?} Then ScriptEngine="VBA"
    If {what goes here?} Then ScriptEngine="HTA"
    End Function
Run Code Online (Sandbox Code Playgroud)

如果填写正确,您应该能够将该功能复制并粘贴到任何VBA,VBS或HTA文件中而无需修改,调用它,并获得结果而不是错误,即使启用了Option Explicit也是如此.最好的方法是什么?

Com*_*ern 9

Option ExplicitVBA实施中对要求的限制使得这比其他情况要困难得多(没有它就是单线)......具有讽刺意味的是,它也是解决方案的关键.如果你不限制自己只使用一个函数,你可以通过这样做来逃避它:

Dim hta

Sub window_onload()
     hta = True
End Sub

Function HostType()
    On Error Resume Next
    If hta Then
        HostType = "HTA"
    Else
        Dim foo
        Set foo = foo
        If Err.Number = 13 Then
            HostType = "VBA"
        Else
            HostType = "VBS"
        End If
    End If
End Function
Run Code Online (Sandbox Code Playgroud)

它的工作方式如下 - 如果它是通过HTA文件加载的,则window_onload事件处理程序运行,将hta变量设置为True.这是第一次测试.第二个"测试"是关于线路抛出的错误Set foo = foo.这是在VBA中,它被理解为试图类型不匹配SetVariantEmpty,这是不兼容的类型.同一行代码在VBScript中抛出错误424(需要对象),因为它不是强类型语言.这意味着跳过VBA的类型检查,它会尝试实际执行分配(失败).其余的只是弄清楚它是如何投掷并返回结果的.

测试代码

VBA

Option Explicit

Dim hta

Sub Test()
    Debug.Print HostType    'VBA
End Sub

Sub window_onload()
     hta = True
End Sub

Function HostType()
    On Error Resume Next
    If hta Then
        HostType = "HTA"
    Else
        Dim foo
        Set foo = foo
        If Err.Number = 13 Then
            HostType = "VBA"
        Else
            HostType = "VBS"
        End If
    End If
End Function
Run Code Online (Sandbox Code Playgroud)

VBScript中

WSCript.Echo HostType

Dim hta

Sub window_onload()
     hta = True
End Sub

Function HostType()
    On Error Resume Next
    If hta Then
        HostType = "HTA"
    Else
        Dim foo
        Set foo = foo
        If Err.Number = 13 Then
            HostType = "VBA"
        Else
            HostType = "VBS"
        End If
    End If
End Function
Run Code Online (Sandbox Code Playgroud)

HTA

<HTML>
    <BODY>
        <script type="text/vbscript">
            Dim hta

            Sub Test()
                MsgBox HostType 
            End Sub

            Sub window_onload()
                 hta = True
            End Sub

            Function HostType()
                On Error Resume Next
                If hta Then
                    HostType = "HTA"
                Else
                    Dim foo
                    Set foo = foo
                    If Err.Number = 13 Then
                        HostType = "VBA"
                    Else
                        HostType = "VBS"
                    End If
                End If
            End Function
        </script>
        <button onclick="vbscript:Test()">Click me</button> 
    </BODY>
</HTML>
Run Code Online (Sandbox Code Playgroud)

编辑:

如果Option Explicit不需要FWIW,上面提到的单线程就是这样:

Function HostString()
    HostString = Application & document & WScript
End Function
Run Code Online (Sandbox Code Playgroud)

所有三个对象都有一个返回a的默认属性String.在VBScript中,这将返回"Windows Script Host".在VBA中,它将返回主机的名称(即Excel中的"Microsoft Excel").在HTA中它将返回"[object]".

  • 哇,这是一个聪明的黑客. (2认同)
  • @Cyber​​Taco很高兴为您效劳."ReDim WScript(0)"在HTA中运行而不是VBScript的原因是因为在VBScript中它是应用程序对象,因此你会遇到类型不匹配的问题.在HTA中,它将是一个未声明的变量(默认为"Variant").`Redim`只是将它重新定义为具有一个(空)元素的`Variant`数组. (2认同)
  • @Cyber​​Taco - 这是正常的范围分辨率.`ReDim`语句计为一个声明(如果指定了'Option Explicit`,它甚至可以在没有`Dim`的情况下单独使用).新变量"隐藏"在其定义范围内的变量.请参阅[VBA规范]中"ReDim"的"Static Sematics"下的第二个项目符号(https://msdn.microsoft.com/en-us/库/ ee156854.aspx). (2认同)

Kul*_*gin 5

虽然我同意@this,但这里是我的Option Explicit-safe 方法,没有On Error使用Window_OnLoadHTA 侦听器的语句(就像Comintern那样)和行标签技巧来区分 VBScript 和 VBA。

Dim IsInHTA, IsInVBScript

Sub Window_Onload()
    IsInHTA = True
End Sub

Sub LineLabelTest()
'VBA and VB6 (maybe VB5 too, IDK) treats "DummyLabel:" as a line label
'VBScript treats "DummyLabel" as an expression to call and treats ":" as a statement separator.
DummyLabel:
End Sub

Sub DummyLabel()
    'this is called by the LineLabelTest subroutine only in VBScript
    IsInVBScript = True
End Sub

Function HostType()
    LineLabelTest

    If IsInVBScript Then
        If IsInHTA Then 
            HostType = "HTA"
        Else
            HostType = "VBS" 'Other hosts incuding WSH, ASP, Custom
        End If
    Else
        HostType = "VBA" 'VBA or VB6 (maybe VB5 too, don't know)
    End If
End Function
Run Code Online (Sandbox Code Playgroud)

  • 好的!我在玩 VBScript 标签时发现了它,但我没想过要把它变成一个函数调用。 (2认同)

Noo*_*les -1

Application所有程序都有一个至少提供全局对象的主机。WScript 提供了全局WScript、Word/ExcelApplication对象、HTA 和 IE Window 对象(通过父属性可以访问InternetExplorer.Application对象的访问)。

在 COM 中,我们在 IUnknown 接口上调用 QueryRef 来查找它拥有哪些对象。这发生在幕后。原则是您尝试使用它并查找表示属性 E_Not_Implemented 的错误。

这是应用程序对象中应包含的内容的标准https://learn.microsoft.com/en-au/previous-versions/windows/desktop/automat/using-the-application-object-in-a-type-图书馆

所以wscript.nameInternetExplorer.Application.Name、 和Word.Application.Name

在 Wscript 中,此代码打印Windows Scripting Host. 在Word 中打印Normal 424 Object Required。在 HTA 中Microsoft VBScript runtime error 424 Object required

On Error Resume Next
x = wscript.name
If err.Number = 0 then
    Msgbox x
Else
    Msgbox err.source & " " & err.number & " " & err.description
End If
Run Code Online (Sandbox Code Playgroud)

在 Word Microsoft Word、VBSMicrosoft VBScript runtime error 424 Object required和 HTA中也是如此Microsoft VBScript runtime error 424 Object required

On Error Resume Next
x = Application.Name
If Err.Number = 0 Then
    MsgBox x
Else
    MsgBox Err.Source & " " & Err.Number & " " & Err.Description
End If
Run Code Online (Sandbox Code Playgroud)

另请注意,对应用程序是否已定义的测试并不稳健。Excel 还有一个 Application 对象。您必须测试name.