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

问题描述 投票:7回答:6

(最终编辑:我最终放在一起工作的代码在下面,它可能是这个帖子中的最终答复。:-))

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

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

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

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

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

excel vba vbscript hta identification
6个回答
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中的类型不匹配,其中它被解释为尝试Set VariantEmpty,这不是兼容类型。同一行代码在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

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

甚至

<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>

编辑:

FWIW,如果不需要Option Explicit,上面引用的单线是这样的:

Function HostString()
    HostString = Application & document & WScript
End Function

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


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
投票

尝试检查上下文对象的存在,比如

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

1
投票

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

除非在VBA标准库之外不存在VarType(我不知道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来满足编译器,那么在运行时这些变量将遮蔽你正在检查的全局标识符,并且该函数将始终返回你首先检查的任何主机。


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或测试不存在的对象。


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

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

-1
投票

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

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

这是应用程序对象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

另请注意,定义与否的应用程序测试不稳定。 Excel还有一个Application对象。你必须测试name

© www.soinside.com 2019 - 2024. All rights reserved.