Как мой код может узнать, работает ли он как VBScript, .HTA или VBA? - PullRequest
0 голосов
/ 13 февраля 2019

(Окончательное редактирование: код, который я в итоге собрал, работает гораздо ниже, это, вероятно, окончательный ответ в этой теме. :-))

Я пытаюсь написать общий экземпляр-иВставьте код, который будет работать в автономном VBScript (файл .vbs), в файле .hta и как VBA (например, в файле Excel).Чтобы сделать это, мне нужен какой-то способ, чтобы сам код мог сказать, на каком движке он работает.

Лучшая идея, которую я слышал, до сих пор заключалась в тестировании, если определенные объекты существуют или нет, но в VBA это не помогает.во время компиляции (поэтому я не могу обойти это с On Error), так что это не сработало.Попытка выяснить имя файла, который он запускает, не принесла жизнеспособности;это одна из тех вещей, которые выполняются по-разному, в зависимости от того, в каком из трех обработчиков сценариев выполняется код. Мне бы хотелось иметь что-то простое, подобное этому, но я не уверен, чем его заполнить:

Редактировать:Большинство ответов до сих пор включают проверку на наличие, возможно, несуществующих объектов, что напрямую не работает в VBA с включенным Option Explicit (он выдает ошибку времени компиляции, поэтому 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 включен.Какой лучший способ сделать это?

Ответы [ 7 ]

0 голосов
/ 15 февраля 2019

Хотя я согласен с @ this , вот мой Option Explicit -защищенный метод без операторов On Error, который использует Window_OnLoad прослушиватель для HTA ​​(как Comintern did) итрюк с меткой строки, чтобы отличить 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
0 голосов
/ 14 августа 2019

Для любого, кто столкнется с этим в будущем, это последний код, который я закончил, и он работает!Отдельное спасибо всем в этой теме, которые поделились идеями, которые собрались вместе, чтобы сделать эту работу!: -)

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

'-------------------------------------------------------------------
0 голосов
/ 14 февраля 2019

Ограничение на требование Option Explicit в реализации VBA делает это немного сложнее, чем могло бы быть в противном случае (это без одной строки) ... По иронии судьбы это также является ключом к решению.Если вы не ограничиваете себя единственной функцией, вы можете обойтись без нее, выполнив что-то вроде этого:

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, что не является совместимым типом.В той же строке кода выдается ошибка 424 (требуется объект) в VBScript, поскольку он не является строго типизированным языком.Это означает, что проверка типа 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

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>

РЕДАКТИРОВАТЬ:

FWIW, ссылка на одну строкувыше, если Option Explicit не требуется, просто это:

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

Все три объекта имеют свойство по умолчанию, которое возвращает String.В VBScript это вернет «Windows Script Host».В VBA он вернет имя хоста (например, «Microsoft Excel» в Excel).В HTA он вернет "[объект]".

0 голосов
/ 14 февраля 2019

Я бы предположил, что вы подходите к проблеме задом наперед.Вместо того, чтобы спрашивать «кто здесь хозяин?», Вы должны иметь общий интерфейс для задач.Например, у вас был бы модуль - назовем его MyAPI:

Public Function MySleep(Milliseconds As Long)
End Function

Затем вы можете реализовать один для VBA, один для VBS, другой для HTA ​​(хотя я сомневаюсь, есть ли реальная разницаВаш код, независимый от хоста, тогда потребовал бы включения того модуля, который все должны учитывать. Например, см. Здесь для включения файла в VBS . Также выглядит, что HTA имеет нечто подобное . В VBA это просто еще один модуль.

Тогда в вашем коде агностического хоста вы будете вызывать MySleep вместо объявленного API Sleep или WScript.Sleep, и позволить включенному модулю предоставитьспецифичная для хоста реализация без каких-либо ветвлений и, следовательно, необходимости отключать Option Explicit и проверять несуществующие объекты.

0 голосов
/ 14 февраля 2019

Любая необъявленная переменная будет 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

Обратите внимание, что это приведет к неожиданным результатам, еслинапример, 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 и, таким образом, выдает ошибку времени выполнения 424 "Требуется объект" при тестировании с Is Nothing.

Обратите внимание, что у вас не может быть Option Explicit указано для этого.Причина в том, что если вы выполните Dim WScript и Dim Application для удовлетворения компилятора, то во время выполнения эти переменные будут затенять глобальные идентификаторы, которые вы проверяете, и функция будет последовательно возвращать любой хост, который вы проверили первым.

0 голосов
/ 14 февраля 2019

Попробуйте проверить наличие объектов контекста, например

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
0 голосов
/ 14 февраля 2019

Все программы имеют хост, который предоставляет как минимум глобальный объект Application.WScript предоставляет глобальный WScript, Word / Excel и Application объект, HTA объект IE Window (который через свойство parent предоставляет доступ к InternetExplorer.Application объекту.

В COM мы вызываем QueryRef дляIНеизвестный интерфейс для поиска объектов, которые у него есть. Это происходит под капотом. Главное, вы пытаетесь его использовать и ищите ошибку, которая говорит о свойстве E_Not_Implemented.

Это стандарт того, что должно быть вобъект Application https://docs.microsoft.com/en-au/previous-versions/windows/desktop/automat/using-the-application-object-in-a-type-library

So 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 также имеет объект приложения. Вы должны проверить на name.

...