(最终编辑:我最终放在一起工作的代码在下面,它可能是这个帖子中的最终答复。:-))
我正在尝试编写通用的复制粘贴代码,这些代码可以在独立的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也是如此。最好的方法是什么?
在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
Variant
到Empty
,这不是兼容类型。同一行代码在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]”。
虽然我同意@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
尝试检查上下文对象的存在,比如
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
任何未声明的变量将是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
请注意,如果例如这将产生意外结果Application
或WScript
在某处定义,主机分别不是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
/ Application
为Variant/Empty
,因此在使用Is Nothing
进行测试时抛出运行时错误424“Object Required”。
请注意,您不能为此指定Option Explicit
。原因是因为如果你做Dim WScript
和Dim Application
来满足编译器,那么在运行时这些变量将遮蔽你正在检查的全局标识符,并且该函数将始终返回你首先检查的任何主机。
我建议你向后解决这个问题。您应该使用通用的任务界面,而不是询问“谁是主持人?”。举个例子,你会有一个模块 - 我们称之为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声明的Sleep
或WScript.Sleep
,并让包含的模块提供特定于主机的实现而不进行任何分支,因此需要禁用Option Explicit
或测试不存在的对象。
对于将来遇到这种情况的人来说,这是我最终制作的最终代码,并且它有效!特别感谢这个主题中的每个人都提出了共同努力使这项工作成功的想法! :-)
'------------------------------------------------------------------
'--- 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
'-------------------------------------------------------------------
所有程序都有一个主机,至少提供一个全局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.name
,InternetExplorer.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
同样在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
。