我的代码如何找出它是作为VBScript,.HTA还是VBA运行的?

时间:2019-02-13 20:47:32

标签: excel vba vbscript hta identification

编辑:到目前为止,大多数响应都涉及检查可能不存在的对象,在Option Explicit启用的情况下,该对象完全无法在VBA中使用(它会引发编译时错误,因此On Error不起作用,并关闭Option显式不是一种选择)。还有其他回旋/开箱即用的方式来查找这里需要什么吗?

我正在尝试编写通用的复制和粘贴代码,这些代码将在独立的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

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

7 个答案:

答案 0 :(得分:9)

在VBA实现中要求使用Option Explicit的限制使此操作比原本要困难得多(没有它,这是一个单行代码)...具有讽刺意味的是,这也成为实现此目标的关键解。 如果您不将自己限制在一个功能上,则可以通过执行以下操作来摆脱它:

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

它的工作原理如下-如果通过HTA文件加载,则window_onload事件处理程序将运行,并将hta变量设置为True。那是第一次测试。第二个“测试”位于行Set foo = foo引发的错误上。这是VBA中的类型不匹配,在VBA中它被解释为试图将Set的{​​{1}}转换为Variant,这是不兼容的类型。同一行代码在VBScript中引发错误424(需要对象),因为它不是强类型语言。这意味着将跳过VBA的类型检查,并尝试实际执行分配(失败)。剩下的只是弄清楚它是如何抛出的并返回结果。

测试代码

VBA

Empty

VBScript

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

HTA

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

编辑:

FWIW,如果不需要<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> ,则上面引用的单行代码就是这样:

Option Explicit

所有三个对象都有一个默认属性,该属性返回一个Function HostString() HostString = Application & document & WScript End Function 。在VBScript中,这将返回“ Windows Script Host”。在VBA中,它将返回主机的名称(即Excel中的“ Microsoft Excel”)。在HTA中,它将返回“ [object]”。

答案 1 :(得分:4)

尽管我同意@this,但是这是我的Option Explicit安全方法,没有On Error语句使用Window_OnLoad监听器进行HTA(就像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

答案 2 :(得分:2)

尝试检查是否存在上下文对象,例如

Function ScriptEngine()
    dim tst
    on error resume next
    Err.Clear
    tst = WScript is Nothing
    if Err=0 then ScriptEngine="WScript" : exit function
    Err.Clear
' similar way check objects in other environments

End Function

答案 3 :(得分:1)

任何未声明的变量将是Variant/Empty,因此,如果VarType(something)未定义,则vbEmpty将是0(或something)。

除非VarType在VBA标准库之外不存在(我不知道TBH),那么就不必捕获/跳过/处理任何错误,才能在VBA中进行测试:

Function GetHostType()
    If VarType(wscript) <> vbEmpty Then
        GetHostType = "VBS"
        Exit Function
    End If        
    If VarType(Application) <> vbEmpty Then
        GetHostType = "VBA"
        Exit Function
    End If        
    GetHostType = "HTA"
End Function

请注意,如果例如ApplicationWScript 定义在某处,并且主机分别不是VBA或VBScript。

或者,不管是否定义了VarType,这都行得通:

Function GetHostType()
    On Error Resume Next

    If Not WScript Is Nothing Then
        If Err.Number = 0 Then
            GetHostType = "VBS"
            Exit Function
        End If
    End If
    Err.Clear ' clear error 424 if not VBS

    If Not Application Is Nothing Then
        If Err.Number = 0 Then
            GetHostType = "VBA"
            Exit Function
        End If
    End If
    Err.Clear ' clear error 424 if not VBA

    GetHostType = "HTA"
End Function

再次假定没有定义这些名称的对象;该机制依赖于WScript / ApplicationVariant/Empty,因此在使用Is Nothing进行测试时会引发运行时错误424“ Object Required”。

请注意,您不能为此指定Option Explicit。原因是因为如果您使用Dim WScriptDim Application来满足编译器的要求,那么在运行时,这些变量将覆盖正在检查的全局标识符,并且该函数将始终返回您首先检查的主机

答案 4 :(得分:1)

我建议您将问题向后处理。而不是询问“谁是这里的主持人?”,而是应该具有用于​​任务的通用接口。例如,您将有一个模块-称它为MyAPI

Public Function MySleep(Milliseconds As Long)
End Function

然后,您可以为VBA实现一个,为VBS实现一个,为HTA实现另一个(尽管我怀疑是否存在真正的区别。与主机无关的代码将要求包含该模块,所有这些模块都应允许。例如, see here for including a file to VBS。看起来也像HTA has something similar。在VBA中,这只是另一个模块。

然后,在不可知主机代码中,您将调用MySleep而不是API声明的SleepWScript.Sleep,然后让包含的模块提供主机特定的实现,而无需任何分支因此,需要禁用Option Explicit或测试不存在的对象。

答案 5 :(得分:1)

对于以后遇到此问题的任何人,这是我最终制作的最终代码,并且可以使用!特别感谢此主题中的每个人,他们贡献了共同的想法来完成这项工作! :-)

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

Function ScriptEngine

    On Error Resume Next

    ScriptEngine="VBA"

    ReDim WScript(0)
    If Err.Number=501 Then ScriptEngine="VBScript"

    Err.Clear

    ReDim Window(0)
    If Err.Number=501 Then ScriptEngine="HTA"

    On Error Goto 0

    End Function

'-------------------------------------------------------------------

答案 6 :(得分:-1)

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

在COM中,我们在IUnknown接口上调用QueryRef以查找其具有的对象。这发生在引擎盖下。原理是您尝试使用它,并查找表示属性E_Not_Implemented的错误。

这是应在Application对象https://docs.microsoft.com/en-au/previous-versions/windows/desktop/automat/using-the-application-object-in-a-type-library中使用的标准

所以wscript.nameInternetExplorer.Application.NameWord.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

与Word Microsoft Word,VBS Microsoft 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

还请注意,是否定义了针对Application的测试不是很可靠。 Excel还具有一个Application对象。您必须测试name